Inertia

Spring In Action - Aspect-oriented Spring

Chapter4 - Aspect-oriented Spring

AOP

앞서도 간단히 알아봤지만 aspect는 여러 서비스 모듈에 공통적으로 사용되어지는 것(로깅, security ..)등을 모듈화 한것이고 이런식의 접근을 AOP라고 함.

AOP에서 사용하는 여러 단어들을 알 필요가 있는데

  • Advice : job of an aspect, @Before @After 등을 말함.
  • join points : possible oppoortunities
  • pointcuts : one or more join points applied for an advice, execution, within등을 말함
  • aspects : advice + pointcuts
  • introductions : 기존 클래스에 새로운 펑션이나 변수등을 추가할 수 있는 기능
  • weaving : target object에 aspect를 적용하는 것. spring은 런타임에 함.

spring의 aop는 AOP의 완벽한 구현이 아니라서 method join points만 지원한다. 다른 AOP는 필드나 생성자 join points까지도 지원하는 반면.

Selecting joint points with pointcuts

1
2
3
4
execution(* concert.Performance.perform(..)) 
execution(* concert.Performance.perform(..)) && within(concert.*)) // limit package concert
execution(* concert.Performance.perform()) and bean('woodstock') // with specific bean
execution(* concert.Performance.perform()) and !bean('woodstock') // without specific bean

Creating annotated aspects

@Pointcut annotation을 사용하면 이것을 한번 생성한후 참조해서 반복해서 사용할 수 있음. 참조할때는 펑션 이름을 사용하면 됨. 아래 예제 참고.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Aspect
public class Audience {
@Before("execution(** concert.Performance.perform(..))")
public void silenceCellPhones() {
System.out.println("Silencing cell phones");
}

@Pointcut("execution(** concert.Performance.perform(..))")
public void performance() {}
@Before("performance()") // refer by pointcut name
public void silenceCellPhones2() {
System.out.println("Silencing cell phones");
}

}

이렇게 어노테이션을 달았다고 aop가 동작하는 것은 아니고 아래처럼 @EnableAspectJAutoProxy와 빈등을 생성해줘야 실제로 aop가 동작하게 됨. 실제로 AspectJ의 annotation을 사용하고 있지만 실제로는 스프링의 aop를 사용하고 있는 것이니 AspectJ의 기능을 풀로 사용할 수 없으니까 주의 해야 함.

1
2
3
4
5
6
7
8
9
@Configuration
@EnableAspectJAutoProxy //enable auto-proxying
@ComponentScan
public class ConcertConfig {
@Bean
public Audience audience() {
return new Audience();
}
}

@Around는 보다 많은 것을 할수 있는데 jp.proceed를 부름으로서 pointcut의 대상 함수가 실행이 되게 된다. 따라서 원래 대상 함수를 블락킹 할 수도 있어 더 강력하다.

1
2
3
4
5
6
7
8
9
10
11
@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Silencing cell phones");
System.out.println("Taking seats");
jp.proceed();
System.out.println("CLAP CLAP CLAP!!!");
} catch (Throwable e) {
System.out.println("Demanding a refund");
}
}

아래를 보면 args로 펑션의 파람을 받아서 advice로 전달할 수 있다.

1
2
3
4
5
6
7
8
9
@Pointcut(
"execution(* soundsystem.CompactDisc.playTrack(int)) " +
"&& args(trackNumber)")
public void trackPlayed(int trackNumber) {}
@Before("trackPlayed(trackNumber)")
public void countTrack(int trackNumber) {
int currentCount = getPlayCount(trackNumber);
trackCounts.put(trackNumber, currentCount + 1);
}

또한 AOP에는 introductions 이라는 새로운 method를 붙일 수 있는 기능도 있다. @DeclareParents라는 어노테이션으로

1
2
3
4
5
6
@Aspect
public class EncoreableIntroducer {
@DeclareParents(value="concert.Performance+",
defaultImpl=DefaultEncoreable.class)
public static Encoreable encoreable;
}

스프링이 지원하지 않는 AspectJ의 기능이 필요하다면 AspectJ의 기능을 사용할 수 도 있다.