[Spring] SpringAop

김장환·2022년 9월 7일

Spring

목록 보기
15/17

Spring AOP개념

관점 지향 프로그래밍이라고 불린다.
관점 지향은 쉽게 말해 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 흩어진 Aspect를 모듈화하겠다는 것이다.

가장 중요한 역할을 하는 클래스와 필수는 아니지만 있다면 편리한 기능을 제공 클래스로 나눌수있으며
각각 핵심클래스, 공통관심클래스(=유틸리티 클래스)라고 한다.


이것도 예제를 진행하면서 정리해보고자 한다.


pom.xml 환경설정
=> pem.xml에 환경설정을 함으로 라이브러리에 ~aop를 생성한다.


TestService (인터페이스) 생성

  • 핵심클래스의 완충제역할을 한다
  • 여러개의 클래스중에서 공통으로 사용(핵심기능을 가진 메서드)

package sp.aop;

//여러개의 클래스중에서 공통으로 사용(핵심기능을 가진 메서드)
public interface TestService {

	public void save(String msg);//회원수정
	public void write();//출력
}


TestServiceImpl.java (TestService인터페이스를 상속받은 자식클래스)

  • 핵심클래스다
  • 오버라이딩하기
package sp.aop;

//핵심클래스(=Target)
//핵심기능메서드->save(),write()
public class TestServiceImpl implements TestService {

	private String msg="before AOP연습";
	/*
	 * AOP를 안하면 이렇게 해야한다
	 * public void log(){
	 * 		log.info(method.toString()+"메서드:"+target+"에서 호출전");  
	 * }
	 */
	@Override
	public void save(String msg) {
		// TODO Auto-generated method stub
		//log()
		this.msg=msg;
		System.out.println("save() 메서드 호출됨!");
	}

	@Override
	public void write() {
		// TODO Auto-generated method stub
		System.out.println("write()호출됨!="+this.msg);
	}

}


BeforeAdvice



BeforeLogAdvice.java (MethodBefore 인터페이스 상속) 생성

package sp.aop;

import java.lang.reflect.Method;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
//------------------------------------------------------
import org.springframework.aop.MethodBeforeAdvice;

/*
 * Advice->모든 클래스에서 공통으로 사용할 기능(로깅(출력),보안(로그인))
 * Advice클래스를 구현
 */
public class BeforeLogAdvice implements MethodBeforeAdvice {

	private Log log=LogFactory.getLog(getClass());
	/*
	 *1)스프링의 AOP(메서드중심)->핵심클래스의 메서드->save or write() 둘줄하나 선택할수있음
	 *2)생성된 객체를 배열로 받아옴
	 *3)핵심클래스의 객체를 얻어옴=>TestServiceImpl
	 */
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		// TODO Auto-generated method stub
		log.info(method.toString()+"메서드:"+target+"에서 호출전");

	}

}


app.xml에서 AOP를 적용시킬수 있도록 처리

  • 1.핵심클래스 빈즈등록
  • 2.Advice클래스 빈즈등록
  • 3.PointCut(어느위치에 AOP메서드를 지정해서 실행)
  • 4.Advice+Pointcut(=Advisior)설정
  • 5.AOP를 적용(ProxyFactoryBean객체를 생성->AOP객체를 생성)

~생략~
<!-- 1.핵심클래스 빈즈등록 -->
<bean id="testServiceImpl" class="sp.aop.TestServiceImpl" />


<!-- 2.Advice클래스 빈즈등록 -->
<bean id="beforeLog" class="sp.aop.BeforeLogAdvice" />


<!-- 3.PointCut(어느위치에 AOP메서드를 지정해서 실행)
		접근지정자 반환형 패키지명.하위패키지명..클래스명 특정메서드(..) 0개이상
		(*) 매개변수 한개 표시 (*,*) 매개변수 2개
 -->
<bean id="writePointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
	<property name="pattern" value=".*write.*" />
</bean>


<!-- 4.Advice+Pointcut(=Advisor)설정 -->
<bean id="testAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="advice" ref="beforeLog" />
	<property name="pointcut" ref="writePointcut" />
</bean>


<!-- 5.AOP를 적용(ProxyFactoryBean객체를 생성->AOP객체를 생성) -->
<bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="testServiceImpl" />
	<property name="interceptorNames">
		<list>
			<value>testAdvisor</value>
		</list>
	</property>
	
</bean>



</beans>


ResultMain.java 생성 및 실행
-실행을 시켜주는 클래스

package sp.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ResultMain {

	public static void main(String[] args) {
		String path="sp/aop/app.xml";//환경설정변수 지정
		ApplicationContext context=new ClassPathXmlApplicationContext(path);
		//AOP객체를 생성->Advisor작동=>Advice+pointcut=>중간에 가로채서 대신실행시킨다.
		TestService service=(TestService)context.getBean("testService");
		service.save("AOP 적용연습");//this.msg="AOP적용연습"
		//before advice(before() 작동실행)=>실행상태
		service.write();
		
	}

}


AfterAdvice



AfterLogAdvice.java 생성 (AfterReturningAdvice 인터페이스 상속)

  • After Advice구현=>AfterReturningAdvice을 상속받아서 작성
    => 그래야 뒤에 생성가능

package sp.aop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

//After Advice구현=>AfterReturningAdvice을 상속받아서 작성 (그래야 뒤에 생성가능)
public class AfterLogAdvice implements AfterReturningAdvice {
	//1.추가된 객체 2.핵심클래스의 메서드명 3.생성된객체들 4.target클래스객체
	
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println(method.toString()+"메서드:"+target+"에서 호출후");
	}

}


app.xml 내용추가

  • < bean id="afterLog" class="sp.aop.AfterLogAdvice" /> 추가
  • savePointcut 부분 추가
  • testAfterAdvisor 부분 추가
  • AOP적용부분에 value를 추가
    => < value>testAfterAdvisor< /value>
~생략~
<!-- 추가 -->
<bean id="afterLog" class="sp.aop.AfterLogAdvice" />
~생략~
<!-- 추가 -->
<bean id="savePointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
	<property name="pattern" value=".*save.*" />
</bean>
~생략~
<!-- 추가 -->
<bean id="testAfterAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="advice" ref="afterLog" />
	<property name="pointcut" ref="savePointcut" />
</bean>
~생략~
<bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="testServiceImpl" />
	<property name="interceptorNames">
		<list>
			<value>testAdvisor</value>
			<value>testAfterAdvisor</value>
		</list>
	</property>
</bean>


Around Advice

Around Advice를 구현(메서드 앞,뒤로 실행)
=> MethodInterceptor상속받아야한다



BALogAdvice.java(MethodInterceptor 인터페이스 상속) 생성


package sp.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/*
 *3. Around Advice를 구현(메서드 앞,뒤로 실행)=> MethodInterceptor상속받아야한다 
 *		->자동으로 invoke()를 호출->앞,뒤에 공통으로 실행할 내용을 기술
 *		핵심클래스의 메서드호출->MethodIntercepter객체명.proceed()를 이용해서 호출가능
 */
public class BALogAdvice implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		// TODO Auto-generated method stub
		//invocation.getMethod()=>핵심클래스의 메서드이름을 알수있다.
		System.out.println("invoke()호출됨!!");
		System.out.println(invocation.getMethod()+"호출전인경우");
		sayHello();
		//핵심클래스의 메서드를 호출=>리턴값이 있는 메서드를 호출하는 경우
		Object returnValue=invocation.proceed();
		//-------------------------------------------
		after();
		System.out.println(returnValue+"를 리턴받음!!!");
		return null;//반환값이 없는경우
		//return returnValue -> 반환값이 있는 경우
		
	}
	
	public void sayHello() {//회원수정전에 먼저 실행을 해야하는 회원로그인
		System.out.println("sayHello() 호출됨");
	}
	//각 메서드를 실행시킬때마다 출력시켜주는 공통의 메서드
	public void after() {
		System.out.println("after() 호출됨!");
	}
	
}


app.xml 내용추가

  • < bean id="baLog" class="sp.aop.BALogAdvice" />
  • testBAAdvisor부분 추가
  • AOP적용부분에 value를 추가
    =>< value>testBAAdvisor< /value>
~생략~
<!-- 추가2 -->
<bean id="baLog" class="sp.aop.BALogAdvice" />
~생략~

<!-- 추가2 -->
<bean id="testBAAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="advice" ref="baLog" />
	<property name="pointcut" ref="savePointcut" />
</bean>

~생략~
<!-- 5.AOP를 적용(ProxyFactoryBean객체를 생성->AOP객체를 생성) -->
<bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="testServiceImpl" />
	<property name="interceptorNames">
		<list>
			<value>testAdvisor</value>
			<value>testAfterAdvisor</value>
			<value>testBAAdvisor</value>
		</list>
	</property>
</bean>


종합해서 전체 app.xml을 정리하자면

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

<!-- 1.핵심클래스 빈즈등록 -->
<bean id="testServiceImpl" class="sp.aop.TestServiceImpl" />


<!-- 2.Advice클래스 빈즈등록 -->
<bean id="beforeLog" class="sp.aop.BeforeLogAdvice" />

<!-- 추가 -->
<bean id="afterLog" class="sp.aop.AfterLogAdvice" />

<!-- 추가2 -->
<bean id="baLog" class="sp.aop.BALogAdvice" />


<!-- 3.PointCut(어느위치에 AOP메서드를 지정해서 실행)
		접근지정자 반환형 패키지명.하위패키지명..클래스명 특정메서드(..) 0개이상
		(*) 매개변수 한개 표시 (*,*) 매개변수 2개
 -->
<bean id="writePointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
	<property name="pattern" value=".*write.*" />
</bean>

<!-- 추가 -->
<bean id="savePointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
	<property name="pattern" value=".*save.*" />
</bean>


<!-- 4.Advice+Pointcut(=Advisor)설정 -->
<bean id="testAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="advice" ref="beforeLog" />
	<property name="pointcut" ref="writePointcut" />
</bean>

<!-- 추가 -->
<bean id="testAfterAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="advice" ref="afterLog" />
	<property name="pointcut" ref="savePointcut" />
</bean>

<!-- 추가2 -->
<bean id="testBAAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
	<property name="advice" ref="baLog" />
	<property name="pointcut" ref="savePointcut" />
</bean>


<!-- 5.AOP를 적용(ProxyFactoryBean객체를 생성->AOP객체를 생성) -->
<bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="testServiceImpl" />
	<property name="interceptorNames">
		<list>
			<value>testAdvisor</value>
			<value>testAfterAdvisor</value>
			<value>testBAAdvisor</value>
		</list>
	</property>
</bean>



</beans>


ResultMain에서 실행해서 잘되면 끝


2022-09-07

0개의 댓글