관점 지향 프로그래밍을 뜻하며, 어떤 로직을 기준으로 핵심적인 관점과 부가적인 관점으로 나눠 관점을 기준으로 각각 모듈화한다.
모듈화란, 공통된 로직이나 기능을 하나의 단위로 묶는 것을 말한다.
OOP의 모듈화의 핵심 단위는 클래스이고, AOP의 모듈화의 핵심 단위는 관점이라는 것에 차이가 있다.
class MyClassEx {
void A(){
System.out.println("Log Before"); //Log 추적
System.out.println("A() Method Called."); // 핵심 기능
System.out.println("Log After"); //Log 추적
}
void B(){
System.out.println("Log Before"); //Log 추적
System.out.println("B() Method Called."); // 핵심 기능
System.out.println("Log After"); //Log 추적
}
void C(){
System.out.println("Log Before"); //Log 추적
System.out.println("C() Method Called."); // 핵심 기능
System.out.println("Log After"); //Log 추적
}
}
public class AopMain {
public static void main(String[] args) throws Exception {
MyClass myClass = new MyClass();
MyAdvice myAdvice = new MyAdvice();
for (Method m : myClass.getClass().getDeclaredMethods())
myAdvice.invoke(m,new MyClass());
}
}
//Advice
class MyAdvice { // 부가 기능이라 가정
void invoke(Method m, Object obj) throws Exception{
// Pointcut
if(null != m.getAnnotation(Transactional.class)) {
System.out.println("[Log Before] { ");
}
m.invoke(obj);
if(null != m.getAnnotation(Transactional.class)) {
System.out.println(" } [Log After]");
System.out.println();
}
}
}
// Target
class MyClass { // 핵심 기능이라 가정
@Transactional // Join Point
void A(){
System.out.println("A() Method Called.");
}
@Transactional
void B(){
System.out.println("B() Method Called.");
}
@Transactional
void C(){
System.out.println("C() Method Called.");
}
void D(){
System.out.println("D() Method Called.");
}
void E(){
System.out.println("E() Method Called.");
}
}
// 출력값
[Log Before] {
A() Method Called.
} [Log After]
[Log Before] {
B() Method Called.
} [Log After]
[Log Before] {
C() Method Called.
} [Log After]
D() Method Called.
E() Method Called.
Spring Framework에서 AOP 방식이 적용되는 흐름을 위 그림으로 보면 조금 더 이해하기 쉬울것 같다.
개념을 어떻게 적용하고 활용할수 있을까. 우선 기본 용어들을 먼저 정리 해보았다.
@SpringBootApplication
public class AopMain2 {
public static void main(String[] args) {
GenericApplicationContext context =
new AnnotationConfigApplicationContext(config.class);
MyClass2 myClass = context.getBean(MyClass2.class);
myClass.aaa();
myClass.bbb();
myClass.ccc();
myClass.ddd();
myClass.eee();
}
}
//Target
public class MyClass2 {
@MyPoint // Pointcut
void aaa(){
System.out.println("aaa() Method Called.");
}
@MyPoint
void bbb(){
System.out.println("bbb() Method Called.");
}
void ccc(){
System.out.println("ccc() Method Called.");
}
@MyPoint
void ddd(){
System.out.println("ddd() Method Called.");
}
void eee(){
System.out.println("eee() Method Called.");
}
}
@Retention(RetentionPolicy.CLASS) // Retention(유지 범위) 기본값은 CLASS
@Documented // 자바 도큐먼트 작성
@Target(ElementType.METHOD) // 메소드 위에 어노테이션을 설정
public @interface MyPoint { }
@Configuration
@ComponentScan
public class config {
@Bean
public MyClass2 getMyClass(){
return new MyClass2();
}
}
@Aspect // Advice 설정
@Component // Bean 등록 해야한다
public class MyAdvice2 {
/* @Around("execution(* com.codestates.chapter2.aop.MyClass2.aaa*(..))")
위와 같이 execution 표현식으로 적용할 Joint Point를 설정할수 있다. */
@Around("@annotation(MyPoint)") // 적용할 Joint Point 설정
public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[Advice Before] { ");
Object result = pjp.proceed();
System.out.println(" } [Advice After]");
System.out.println();
return result;
}
}
출력값
[Advice Before] {
aaa() Method Called.
} [Advice After]
[Advice Before] {
bbb() Method Called.
} [Advice After]
[Advice Before] {
ddd() Method Called.
} [Advice After]
ccc() Method Called.
eee() Method Called.
Advice의 세부 종류와 Pointcut의 표현식, JoinPoint 인터페이스에 대해서는 따로 정리를 해서 블로깅 예정이다.