@Bean // @Configuration 이 붙은 클래스 내부 메소드
public Advisor advisor(Object o) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.proxy.app..*(..))");
Advice advice = new SomethingImplemented(o);//Advice는 인터페이스,구현체명은 임의로 정함
//advisor = pointcut + advice
return new DefaultPointcutAdvisor(pointcut, advice);
}
원하는 곳에 원하는 기능을 AOP를 통해 적용하는 행위를 말함
아래 세가지에 위빙을 수행할 수 있음(단, 스프링AOP는 세 번째에만)
원래 프록시를 사용할 수 있는 기술이 JDK Proxy와 CGLIB Proxy가 있었다.
스프링은 이 둘을 추상화 했고, 개발자는 Advice라는 인터페이스만 잘 구현하면 프록시를 원하는 대로 사용할 수 있도록 돕고 있다. (참고로 위 두 기술은 원하는 대로 교체해 사용 가능)
MethodInterceptor —(상속)—> Interceptor —(상속)—> Advice
public interface MethodInterceptor implements Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 이전 로직
Object result = invocation.proceed();
// 이후 로직
return result;
}
public void test() {
MemberService memberService = new MemberService();
ProxyFactory proxyFactory = new ProxyFactory(memberService);
proxyFactory.addAdvice(new CustomAdvice()); //바로 위 예제와 같은 커스텀 어드바이스
MemberService proxy = (MemberService) proxyFactory.getProxy();
proxy.anyMethod(); // 임의로 추가한 내용
}
**포인트 컷의 표현식은 &&, | , ! 등으로 결합이 가능하다** |
// 모든 공개 메서드 실행
execution(public * *(..))
// set 다음 이름으로 시작하는 모든 메서드 실행
execution(* set*(..))
// MemberService 인터페이스에 의해 정의된 모든 메서드의 실행
execution(* com.abc.service.MemberService.*(..))
// service 패키지에 정의된 메서드 실행
execution(* com.abc.service.*.*(..))
// 서비스 패키지 또는 해당 하위 패키지 중 하나에 정의된 메서드 실행
execution(* com.abc.service..*.*(..))
// 서비스 패키지 내의 모든 조인 포인트
within(com.abc.service.*)
// 서비스 패키지 또는 하위 패키지 중 하나 내의 모든 조인 포인트
within(com.abc.service..*)
// MemberService가 프록시가 인터페이스를 구현하는 모든 조인 포인트
this(com.abc.service.MemberService)
// MemberService 대상 객체가 인터페이스를 구현하는 모든 조인 포인트
target(com.abc.service.MemberService)
// 단일 매개변수를 사용하고 런타임에 전달된 인수가 Serializable과 같은 모든 조인 포인트
args(java.io.Serializable)
// 대상 객체에 @Transactional 애너테이션이 있는 모든 조인 포인트
@target(org.springframework.transaction.annotation.Transactional)
// 실행 메서드에 @Transactional 애너테이션이 있는 조인 포인트
@annotation(org.springframework.transaction.annotation.Transactional)
// 단일 매개 변수를 사용하고 전달된 인수의 런타임 유형이 @Classified 애너테이션을 갖는 조인 포인트
@args(com.abc.security.Classified)
// tradeService 라는 이름을 가진 스프링 빈의 모든 조인 포인트
bean(tradeService)
// 와일드 표현식 *Service 라는 이름을 가진 스프링 빈의 모든 조인 포인트
bean(*Service)
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class LogExecutionTime
자바의 @interface 와 다르게 annotation class로 생성하는 모습
@Aspect
@Component
class ExampleAspect {
private val log = LoggerFactory.getLogger(javaClass) // slf4j 객체 가져옴
@Before("@annotation(LogExecutionTime)")
fun before(joinPoint: JoinPoint) {
log.info("before")
}
@After("@annotation(LogExecutionTime)")
fun after(joinPoint: JoinPoint) {
log.info("after")
}
@Around("@annotation(LogExecutionTime)")
@Throws(Throwable::class)
fun logExecutionTime(joinPoint: ProceedingJoinPoint): Any? { //Around라서 다른 매개변수
val start = System.currentTimeMillis()
var proceed = joinPoint.proceed() //꼭 호출해야지 메소드가 돌아감
val executionTime = System.currentTimeMillis() - start
log.info("${joinPoint.signature} excuted in ${executionTime}ms")
return proceed // 꼭 리턴해야 다음 로직이 수행됨
}
@AfterReturning(value = "@annotation(LogExecutionTime)", returning = "resource")
fun afterReturning(resource: Any?) {
log.info("after Returning $resource")
}
@AfterThrowing(value = "@annotation(LogExecutionTime)", throwing = "exception")
fun afterThrowing(exception: Exception) {
log.info("after Throwing", exception)
}
}
before(), after()는 메소드 실행 전 후 로그 찍는 로직
logExecutionTime은 메소드 실행 시간을 재는 로직
afterReturning, afterThrowing은 메소드의 리턴후 혹은 예외 발생시의 모습
@Slf4j
@Aspect
@Component
public class LogIntroduction {
@Pointcut("execution(* com.dragonguard.backend..*Controller*.*(..))")
public void allController() {
}
@Pointcut("execution(* com.dragonguard.backend..*Service*.*(..))")
public void allService() {
}
@Pointcut("execution(* com.dragonguard.backend..*Repository*.*(..))")
public void allRepository() {
}
@Before("allController()")
public void controllerLog(JoinPoint joinPoint) {
log.info(
"METHOD : {}, ARGS : {}",
joinPoint.getSignature().toShortString(),
joinPoint.getArgs());
}
@Before("allService() || allRepository()")
public void serviceAndRepositoryLog(JoinPoint joinPoint) {
log.debug(
"METHOD : {}, ARGS : {}",
joinPoint.getSignature().toShortString(),
joinPoint.getArgs());
}
}