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.핵심클래스 빈즈등록 -->
<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 인터페이스 상속)
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" />
~생략~
<!-- 추가 -->
<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 내용추가
~생략~
<!-- 추가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