이번 시간에는 지난 시간에 자바 코드로 구현했던 AOP를 스프링을 통해 구현해보려 한다.
스프링에서는 AOP의 형태를 총 4가지로 구분하는데, Before, After Returning, After Throwing, Around가 있다.
Before는 부가적인 업무가 주 업무의 앞에서만 실행되는 경우이고, After Returning은 부가적인 업무가 주 업무의 뒤에서만 실행되는 경우이다. After Throwing은 부가적인 업무가 예외처리를 담당하고 있는 경우이고, Around는 부가적인 업무가 주 업무의 앞, 뒤에서 실행되는 경우이다.
먼저 Around Advice를 구현해보자.
우선 이전에 작성하였던 xml 파일을 복사하여 가져온 뒤, 아래와 같이 수정해주었다.
<?xml version="1.0" encoding="UTF-8"?>
...
<bean id="target" class="spring.aop.entity.NewlecExam" p:kor="1" p:eng="1" p:math="1" p:com="1" />
<bean id="logAroundAdvice" class="spring.aop.advice.LogAroundAdvice" />
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target" />
<property name="interceptorNames">
<list>
<value>logAroundAdvice</value>
</list>
</property>
</bean>
</beans>
위 코드는 아래의 자바 코드를 대신하는 구문이다.
Exam exam = new NewlecExam(1, 1, 1, 1);
Exam proxy = (Exam) Proxy.newProxyInstance(NewlecExam.class.getClassLoader(),
new Class[] {Exam.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
}
}
);
이후 xml 파일에서 미리 언급한 새로운 클래스를 생성하여 MethodInterceptor 인터페이스를 아래와 같이 구현한다.
public class LogAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long end = System.currentTimeMillis();
String message = (end - start) + "ms 시간이 걸렸습니다.";
System.out.println(message);
return result;
}
}
여기서 invocation.proceed() 함수는 앞에서 사용했던 method.invoke() 함수와 비슷한 기능을 수행한다.
다음으로 자바 코드를 다음과 같이 수정해주었다.
public class Program {
public static void main(String[] args) {
ApplicationContext context =
//new AnnotationConfigApplicationContext(NewlecDIConfig.class);
new ClassPathXmlApplicationContext("spring/aop/setting.xml");
Exam proxy = (Exam) context.getBean("proxy");
/*
* Exam exam = new NewlecExam(1, 1, 1, 1);
*
* Exam proxy = (Exam) Proxy.newProxyInstance(NewlecExam.class.getClassLoader(),
* new Class[] {Exam.class}, new InvocationHandler() {
*
* @Override public Object invoke(Object proxy, Method method, Object[] args)
* throws Throwable { long start = System.currentTimeMillis();
*
* Object result = method.invoke(exam, args);
*
* long end = System.currentTimeMillis();
*
* String message = (end - start) + "ms 시간이 걸렸습니다.";
* System.out.println(message);
*
* return result; } } );
*/
System.out.printf("total is %d\n", proxy.total());
System.out.printf("total is %f\n", proxy.avg());
}
}
다음으로 Before Advice를 구현해보자.
먼저 xml 파일을 다음과 같이 수정해주었다.
<bean id="target" class="spring.aop.entity.NewlecExam" p:kor="1" p:eng="1" p:math="1" p:com="1" />
<bean id="logAroundAdvice" class="spring.aop.advice.LogAroundAdvice" />
<bean id="logBeforeAdvice" class="spring.aop.advice.LogBeforeAdvice" />
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target" />
<property name="interceptorNames">
<list>
<value>logAroundAdvice</value>
<value>logBeforeAdvice</value>
</list>
</property>
</bean>
LogBeforeAdvice 클래스의 객체인 logBeforeAdvice가 추가되었으니 이제 실질적으로 LogBeforeAdvice 클래스를 생성해준다.
해당 클래스는 MethodBeforeAdvice 인터페이스를 구현하고 있으며 before() 함수를 가지고 있다.
public class LogBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("타겟 지시문 앞에서 실행될 문장");
}
}
이제 우리는 before() 함수 내에 우리가 타겟 지시문 이전에 실행할 코드를 작성해주기만 하면 된다.
다음으로 After Returning Advice를 구현해보자.
이전과 똑같이 xml 파일 먼저 수정해주었다.
<bean id="target" class="spring.aop.entity.NewlecExam" p:kor="1" p:eng="1" p:math="1" p:com="1" />
<bean id="logAroundAdvice" class="spring.aop.advice.LogAroundAdvice" />
<bean id="logBeforeAdvice" class="spring.aop.advice.LogBeforeAdvice" />
<bean id="logAfterReturningAdvice" class="spring.aop.advice.LogAfterReturningAdvice" />
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target" />
<property name="interceptorNames">
<list>
<value>logAroundAdvice</value>
<value>logBeforeAdvice</value>
<value>logAfterReturningAdvice</value>
</list>
</property>
</bean>
이후 LogAfterReturningAdvice 클래스를 생성하여 다음과 같이 구현해주었다.
public class LogAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("returnValue : " + returnValue + ", method : " + method.getName());
}
}
해당 클래스는 AfterReturningAdvice 인터페이스를 구현하고 있으며 afterReturning() 함수를 가지고 있다.
만약 타겟문을 실행한 후에 따로 실행해야할 코드가 있다면 해당 함수 안에 작성해주면 된다.
마지막으로 After Throwing Advice를 구현해보자.
마찬가지로 xml 파일을 수정해준다.
<bean id="target" class="spring.aop.entity.NewlecExam" p:kor="1" p:eng="1" p:math="1" p:com="1" />
<bean id="logAroundAdvice" class="spring.aop.advice.LogAroundAdvice" />
<bean id="logBeforeAdvice" class="spring.aop.advice.LogBeforeAdvice" />
<bean id="logAfterReturningAdvice" class="spring.aop.advice.LogAfterReturningAdvice" />
<bean id="logAfterThrowingAdvice" class="spring.aop.advice.LogAfterThrowingAdvice" />
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target" />
<property name="interceptorNames">
<list>
<value>logAroundAdvice</value>
<value>logBeforeAdvice</value>
<value>logAfterReturningAdvice</value>
<value>logAfterThrowingAdvice</value>
</list>
</property>
</bean>
이후 똑같이 LogAfterThrowingAdvice 클래스를 생성하여 다음과 같이 구현해준다.
package spring.aop.advice;
import org.springframework.aop.ThrowsAdvice;
public class LogAfterThrowingAdvice implements ThrowsAdvice{
public void afterThrowing(? e) throws Throwable {
...
}
}
여기서 ThrowsAdvice 인터페이스는 디폴트 메소드를 가지고 있는 인터페이스인데, 위에서 말했듯 After Throwing은 예외처리를 위한 Advice인데 타겟문에서 어떤 예외가 발생할지 알 수 없기 때문이다.
이제 우리는 위 코드에 있는 '?' 위치에 우리가 예외처리하고자 하는 예외를 적어주고 afterThrowing() 함수 안에 예외처리 구문을 적어주면 된다.
'🍃 Spring, Spring Boot > 스프링 프레임워크 기초' 카테고리의 다른 글
[Java / Spring] 0. 메이븐 프로젝트 생성시 pom.xml 오류 해결 (0) | 2022.04.17 |
---|---|
[Java / Spring] 9. Point Cut(Weaving, Join Point) (0) | 2022.04.17 |
[Java / Spring] 7. 순수 자바로 AOP 구현해보기 (0) | 2022.04.17 |
[Java / Spring] 6. XML Configuration을 Java Configuration으로 변경하기 (0) | 2022.04.11 |
[Java / Spring] 5. 어노테이션을 이용한 객체 생성 (0) | 2022.04.11 |