잘못 사용된 pointcut은 심각한 문제를 일으킬 수 있다. 다음의 코드는 반환 타입을 고려하지 않고 패키지도, 메서드 이름도 상관 없고 단지 첫 번째 파라미터가 int가 있으면 되는 경우이다.
@Before("execution(* *(int, ..))")
얼핏 보면 PointcutBean의 calc가 대상인 것처럼 보이지만 사실 Spring이 관리하는 빈은 우리가 만든것 이외에도 어마어마하게 많고 그중에 어떤 빈의 어떤 메서드에 AOP가 적용되는지 알수 없다. 따라서 적절한 패키지와 클래스를 이용한 제한은 반드시 필요하다.
단위테스트를 통해서 빈의 메서드들을 동작시켜서 예상대로 AOP가 동작하는지 점검해 보자.
@SpringBootTest
public class PointcutTest {
@Autowired
PointcutBean pBean;
@Autowired
SimpleBean sBean;
@Test
public void methodCallTest() throws Exception {
sBean.setName("홍길동");
sBean.setNow(Calendar.getInstance());
sBean.getName();
sBean.getNow();
pBean.setOper("*");
pBean.calc(100, 200);
}
}
기타
execution 이외의 지정자
pontcut을 작성하면서 지시자로 execution 이 외에도 within이나 bean, @annotation등을 사용할 수도 있다.
within은 실제 타겟 빈의 타입을 기준으로 해당 클래스, 인터페이스 내의 모든 메서드를 pointcut으로 지정할 수 있다. 또는 빈에 적용된 에너테이션을 기준으로 pointcut을 지정할 때도 within이 사용된다. 타입을 지정하는 세부적인 작성 방식은 execution의 class 부분과 동일하다.
@Before("within(com.doding..service.*)")
public void serviceBeans() {}
@Before("@within(org.springframework.stereotype.Service)")
public void serviceBeans() {}
bean 은 스프링의 빈 이름을 이용하여 포인트컷을 지정할 때 사용된다. 따라서 빈의 구체적인 타입이나 클래스의 구조와 무관하게 빈의 이름만으로 pointcut을 지정한다.
@Before("bean(*Service)")
public void serviceBeans() {}
@annotation 지정하는 특정 애너테이션이 선언된메서드에 대해서 advice를 지정할 때 사용할 수 있다.
@Before("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}
pointcut 재사용
만약 하나의 pointcut이 여러개의 advice에 적용될 수 있다면 pointcut을 저장해서 재사용할 수 있다. 이것을 named pointcut이라고 하고 이를 위해 @Pointcut 애너테이션이 사용된다. @Pointcut이 선언된 메서드의 이름은 pointcut id가 되고 advice를 선언할 때 이 pointcut id를 이용하면 된다. 사용시 주의 사항은 마치 메서드를 호출하는 형태로 사용해야 하며 다른 클래스에 선언된 pointcut id를 사용해야 하는 경우 패키지를 포함한 전체 경로명을 써줘야 한다.
package com.doding.some.aop;
public class PointCuts {
@Pointcut("execution(* com.doding.service.*.*(..))")
public void serviceLayerExecution() {
// 내용은 필요 없음
}
}
package com.doding.other.aop;
@Aspect
@Component
public class LoggingAspect {
@Before("com.doding.some.aop.Pointcuts.serviceLayerExecution()")
public void logBeforeServiceLayerExecution(JoinPoint joinPoint) {
// 타겟 동작 전에 실행 할 일 정리
}
@After("com.doding.some.aop.Pointcuts.serviceLayerExecution()")
public void logAfterServiceLayerExecution(JoinPoint joinPoint) {
// 타겟 동작 후에 실행 할 일 정리
}
}
pointcut의 연결
&&, ||, !, and, or, not을 이용해 여러 개의 pointcut을 연결해서 사용할 수도 있다. 다음은 pointcutBean이라는 빈 이거나 com..SimpleBean 타입인 경우 적용되는 point cut을 선언한 예이다.