티스토리 뷰
1. AOP란?
Aspect Oriented Programming이다.
customer가 원하는 주 기능을 구현하는 것 말고도 개발자나 운영자에게 필요한 기능이 있을 수 있다. 예를 들어 보안 처리, 트랜잭션 처리, 로그처리 같은 것이다.
이러한 기능들은 주 기능 앞뒤에서 실행하기 때문에 Cross-cutting Concern 코드라고 한다.
2. AOP 구현 방식
이 Cross-cutting 관점 코드를 주 기능의 앞뒤에 굳이 작성하여 넣는다면 필요가 없어질 시에 불편함을 만들어낼 수 있다.
그래서 Cross-cutting 관점 코드는 따로 둔 후에, Cross-cutting 관점 코드를 거친 후 Core 관점 코드를 사용하도록 하는 것이 AOP 구현 방식이다.
즉, Cross-cutting 관점 코드 -> Core 관점 코드 -> Cross-cutting 관점 코드 이렇게 구현되기 때문에 마치 Core관점 코드에 꽂아 넣은 것처럼 실행되며 필요가 없어지더라도 Core 관점 코드를 수정하지 않아도 된다.
3. 스프링 없이 AOP 구현
스프링 없이 자바 코드로 AOP 구현을 하려면 프락시 클래스에 구현해야한다.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Program {
public static void main(String[] args) {
final Exam exam = new NewlecExam(1,1,1);
//스프링 없이 AOP 구현. 프락시 클래스에 구현
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 {
//Cross-cutting Concern
long start = System.currentTimeMillis();
//Core Concern
Object result = method.invoke(exam, args);
//Cross-cutting Concern
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
String message = (end-start) + "";
System.out.println(message);
return result;
}
}
);
System.out.println(proxy.total());
System.out.println(proxy.avg());
}
}
Cross-cutting 관점 코드를 작성하고 method의 invoke 함수를 이용하여 수행할 주 기능 업무 즉, Core 관점 코드를 작성한다. invoke 함수의 인수로 수행할 클래스 인스턴스를 전달한다.
물론 return 값까지 받을 수 있다. 그 이후에 또 Cross-cutting 관점 코드를 작성한 후 마친다.
이후에 proxy 클래스 인스턴스로 주 기능 함수를 호출하면 AOP 를 구현한 것이다. 또, 그냥 Core 관점 코드 즉, 주 기능 업무만 수행하고 싶다면 exam 인스턴스의 함수를 호출하면된다.
4. 스프링으로 AOP 구현 (xml)
Advice
핵심 기능에 제공할 부가기능을 담고있는 모듈을 말한다.
Advice는 종류에 따라 Cross-cutting 관점 코드가 핵심 기능 코드를 기준으로 어디에서 실행될 것이냐를 정한다.
- Around Advice : 타겟의 메서드가 호출되기 이전(before) 시점과 이후 (after) 시점에 모두 처리해야 할 필요가 있는 부가기능을 정의한다. 즉, Joinpoint 앞과 뒤에서 실행되는 Advice
- Before Advice: 타겟의 메서드가 실행되기 이전(before) 시점에 처리해야 할 필요가 있는 부가기능을 정의한다. 즉, Jointpoint 앞에서 실행되는 Advice
- After returnning Advice : 타겟의 메서드가 정상적으로 실행된 이후(after) 시점에 처리해야 할 필요가 있는 부가기능을 정의한다. 즉, Jointpoint 메서드 호출이 정상적으로 종료된 뒤에 실행되는 Advice
- After throwing : 타겟의 메서드가 예외를 발생된 이후(after) 시점에 처리해야 할 필요가 있는 부가기능을 정의한다. 즉, 예외가 던져 질때 실행되는 Advice
xml 방식
xml 방식으로 프록시 클래스를 이용하여 AOP를 구현할 수 있다.
xml 설정 파일에 다음과 같이 Advice 들을 설정할 수 있다.
<bean id = "target" class="spring.aop.entity.NewlecExam" p:kor="10" p:eng="20" p:com="30" />
<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="exam" 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>
타겟이 될 클래스 인스턴스 Bean을 생성한다.
각 Advice 역할 클래스 인스턴스 Bean들을 생성한다.
프록시 클래스 인스턴스 Bean을 생성하며 property 태그를 이용해 target과 interceptorNames를 설정한다. interceptorNames 에 list로 Advice 역할 클래스 이름들을 모두 넣어준다.
이제 프록시 클래스 Bean을 가져와서 함수를 호출하면 각 Advice 클래스의 메소드들이 각 시점에 실행된다.
public class Program {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Exam exam = (Exam) context.getBean("exam");
System.out.println(exam.total());
}
}
exam의 total() 함수를 호출하니까 설정했던 모든 Advice 클래스의 메소드들이 실행된다. 순서는 before → (예외가 있다면) afterThrowing → afterReturning → around 순이다.
이제 각 Advice 클래스들을 보면서 메소드를 보자.
1. Around Advice
package spring.aop.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LogAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = methodInvocation.proceed();
long end = System.currentTimeMillis();
String message = (end-start) + "ms";
System.out.println(message);
return result;
}
}
Around 어드바이스 클래스는 MethodInterceptor 인터페이스를 구현한다. invoke 메소드가 실행되는데, 중간에 보면 proceed()함수로 핵심 기능을 수행하는 코드가 있음을 볼 수 있다.
2. Before Advice
package spring.aop.advice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class LogBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("before입니다.");
}
}
Before 어드바이스 클래스는 MethodBeforeAdvice 인터페이스를 구현하는 클래스이다. before 메소드가 실행된다.
3. After-returning Advice
package spring.aop.advice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class LogAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("after입니다.");
}
After-returning 어드바이스 클래스는 AfterReturningAdvice 인터페이스를 구현하는 클래스다. afterReturning 메소드를 수행하게 된다.
4. After-throwing Advice
package spring.aop.advice;
import org.springframework.aop.ThrowsAdvice;
public class LogAfterThrowingAdvice implements ThrowsAdvice {
public void afterThrowing(IllegalArgumentException e) throws Throwable {
System.out.println("exception 발생");
}
}
After-throwing 어드바이스 클래스는 ThrowsAdvice 인터페이스를 구현하는 클래스다. 여기서 afterThrowing 메소드는 예외가 생기면 실행되는데, 여기서는 IllegalArgumentException 로 정의해놓았다.
Join Point
어드바이스가 적용될 수 있는 위치를 말한다.
타겟 객체가 구현한 인터페이스의 모든 메서드는 조인 포인트가 된다.
즉, 우리가 위에서 exam의 모든 메서드가 실행될 때 어드바이스가 적용되는 것이 아니라 특정 메서드가 실행될 때만 어드바이스가 적용되도록 할 수 있다는 말이다.
특정 메서드를 지정해주지 않으면 디폴트로 모든 메서드가 조인 포인트가 된다.
Pointcuts
어드바이스를 적용할 타겟의 메서드를 선별하는 정규표현식이다.
포인트컷 표현식은 execution으로 시작하고 메서드의 Signature를 비교하는 방법을 주로 이용한다.
Weaving
위빙은 포인트컷에 의해서 결정된 타겟의 조인 포인트에 부가기능(어드바이스)를 삽입하는 과정을 뜻한다.
위빙은 AOP가 핵심기능(타겟)의 코드에 영향을 주지 않으면서 필요한 부가기능(어드바이스)를 추가할 수 있도록 해주는 핵심적인 처리과정이다.
xml 설정파일에 특정 메소드에만 어드바이스가 적용되도록 포인트컷을 설정해보자.
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="LogBeforeAdvice"/>
<property name="mappedNames">
<list>
<value>total</value>
<value>avg</value>
</list>
</property>
</bean>
advice에 어드바이스 빈을 참조해주고, mappedNames에게 설정할 특정 메소드들을 list로 작성한다.
이 포인트컷을 지정한 Bean을 프록시 클래스 Bean의 interceptorNames에 지정해주면 설정한 특정 메소드에만 어드바이스가 적용된다.
list의 value 즉, 함수명을 다음과 같이 정규표현식으로 표현할 수도 있다. 이 때 클래스가 약간 다르니 주의하도록 하자.
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="LogBeforeAdvice"/>
<property name="patterns">
<list>
<value>.*to.*</value>
</list>
</property>
</bean>
'백엔드 및 서버' 카테고리의 다른 글
[ Spring ] tiles 설명 및 설정 방법 (0) | 2021.01.27 |
---|---|
[ Spring ] Dispatcher Servlet 및 ViewResolver 설정 (0) | 2021.01.24 |
[ Spring ] DI (Dependency Injection) Annotation 방식 설명 및 사용법 (0) | 2021.01.22 |
[ Spring ] DI (Dependency Injection) xml 방식 설명 및 사용법 (0) | 2021.01.22 |
[ JSP / Servlet ] MVC 패턴이란? (0) | 2020.12.04 |