package com.example.demo.pointcut;

import com.example.demo.member.MemberService;
import com.example.demo.member.annotation.ClassAop;
import com.example.demo.member.annotation.MethodAop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;

@Slf4j
@Import(ParameterTest.ParameterAspect.class)
@SpringBootTest
public class ParameterTest {

    @Autowired
    MemberService memberService;

    @Test
    void success(){
        log.info("memberService Proxy ={}",memberService.getClass());
        memberService.hello("helloA");
    }

    @Slf4j
    @Aspect
    static class ParameterAspect{
        @Pointcut("execution(* com.example.demo.member..*.*(..))")
        private void allMember(){
        }

        @Around("allMember()")
        public Object logArgs1(ProceedingJoinPoint joinPoint) throws Throwable {
            Object arg1 = joinPoint.getArgs()[0];
            log.info("[logArgs1]{}, arg={}",joinPoint.getSignature(),arg1);
            return joinPoint.proceed();
        }

        @Around("allMember() && args(arg, ..)")
        public Object logArgs1(ProceedingJoinPoint joinPoint,Object arg) throws Throwable {
            log.info("[logArgs2]{}, arg={}",joinPoint.getSignature(),arg);
            return joinPoint.proceed();
        }

        @Before("allMember() && args(arg,..)")
        public void logArgs3(String arg){
            log.info("[logArgs3] arg={}",arg);
        }

        //this : 스프링 컨테이너에 올라간 녀석을 의미 - class com.example.demo.member.MemberServiceImpl$$EnhancerBySpringCGLIB$$e12e6b06
        @Before("allMember() && this(obj)")
        public void thisArgs(JoinPoint joinPoint, MemberService obj){
            log.info("[this]{}, obj={}",joinPoint.getSignature(),obj.getClass());
        }

        //target : 실제 객체를 의미 - class com.example.demo.member.MemberServiceImpl
        @Before("allMember() && target(obj)")
        public void targetArgs(JoinPoint joinPoint, MemberService obj){
            log.info("[target]{}, obj={}",joinPoint.getSignature(),obj.getClass());
        }

        @Before("allMember() && @target(annotation)")
        public void atTarget(JoinPoint joinPoint, ClassAop annotation){
            log.info("[@target]{}, annotation={}",joinPoint.getSignature(),annotation);
        }

        @Before("allMember() && @within(annotation)")
        public void atWithin(JoinPoint joinPoint, ClassAop annotation){
            log.info("[@within]{}, annotation={}",joinPoint.getSignature(),annotation);
        }

        @Before("allMember() && @annotation(annotation)")
        public void atAnnotation(JoinPoint joinPoint, MethodAop annotation){
            log.info("[@annotation]{}, annotationValue={}",joinPoint.getSignature(),annotation.value());
        }

    }
}
  			@Around("allMember()")
        public Object logArgs1(ProceedingJoinPoint joinPoint) throws Throwable {
            Object arg1 = joinPoint.getArgs()[0];
            log.info("[logArgs1]{}, arg={}",joinPoint.getSignature(),arg1);
            return joinPoint.proceed();
        }
[logArgs1]String com.example.demo.member.MemberServiceImpl.hello(String), arg=helloA

@Around("allMember()")

전체 인수를 가져온다.

현재는 첫 번째 인수만을 가져오고 있다.

 @Around("allMember() && args(arg, ..)")
        public Object logArgs1(ProceedingJoinPoint joinPoint,Object arg) throws Throwable {
            log.info("[logArgs2]{}, arg={}",joinPoint.getSignature(),arg);
            return joinPoint.proceed();
        }
[logArgs2]String com.example.demo.member.MemberServiceImpl.hello(String), arg=helloA

@Around("allMember() && args(arg, ..)")

여러 인수들 중 arg 인수를 가져온다.

@Before("allMember() && args(arg,..)")
        public void logArgs3(String arg){
            log.info("[logArgs3] arg={}",arg);
        }
[logArgs3] arg=helloA

@Before("allMember() && args(arg,..)")

바로 위와 동일하지만 차이점은 @Before를 사용하여 ProceedJoinPoint를 사용하지 않아도 인수를 받아 올 수 있다.

 @Before("allMember() && this(obj)")
        public void thisArgs(JoinPoint joinPoint, MemberService obj){
            log.info("[this]{}, obj={}",joinPoint.getSignature(),obj.getClass());
        }
[this]String com.example.demo.member.MemberServiceImpl.hello(String), obj=class com.example.demo.member.MemberServiceImpl$$EnhancerBySpringCGLIB$$af58c20f

@Before("allMember() && this(obj)")

현재 스프링 컨테이너에 올라와있는 객체를 가져오기때문에 프록시로 변조된 memberService를 가져온다.

@Before("allMember() && target(obj)")
        public void targetArgs(JoinPoint joinPoint, MemberService obj){
            log.info("[target]{}, obj={}",joinPoint.getSignature(),obj.getClass());
        }
[target]String com.example.demo.member.MemberServiceImpl.hello(String), obj=class com.example.demo.member.MemberServiceImpl

@Before("allMember() && target(obj)")

실제 적용되는 객체 자체를 가져온다.

@Before("allMember() && @target(annotation)")
        public void atTarget(JoinPoint joinPoint, ClassAop annotation){
            log.info("[@target]{}, annotation={}",joinPoint.getSignature(),annotation);
        }
[@target]String com.example.demo.member.MemberServiceImpl.hello(String), annotation=@com.example.demo.member.annotation.ClassAop()

@Before("allMember() && @target(annotation)")

@target은 부모 클래스의 메서드까지 어드바이스를 적용한다.

@Before("allMember() && @within(annotation)")
        public void atWithin(JoinPoint joinPoint, ClassAop annotation){
            log.info("[@within]{}, annotation={}",joinPoint.getSignature(),annotation);
        }
[@within]String com.example.demo.member.MemberServiceImpl.hello(String), annotation=@com.example.demo.member.annotation.ClassAop()

@Before("allMember() && @within(annotation)")

@within은 자기 자신의 클래스에 정의된 메서드에만 어드바이스를 적용한다

@Before("allMember() && @annotation(annotation)")
        public void atAnnotation(JoinPoint joinPoint, MethodAop annotation){
            log.info("[@annotation]{}, annotationValue={}",joinPoint.getSignature(),annotation.value());
        }
[@annotation]String com.example.demo.member.MemberServiceImpl.hello(String), annotationValue=test value

@Before("allMember() && @annotation(annotation)")

메서드의 어노테이션을 전달 받는다.

 

@target과 @within의 차이

 

공부할때 @target은 부모까지 호출하고 @within은 지정한것만 호출한다고 했는데, 이 테스트 예제에서는 나오지 않아서 직접 실험해보았다.

BaseService라는 인터페이스가 아닌 클래스를 생성한다.

package com.example.demo.member;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BaseService {
    public void baseMethod() {
        log.info("BaseService.BaseMethod");
    }

    public void commonMethod() {
        log.info("BaseService.commonMethod");
    }
}

이후 MemberServiceImpl에 BaseService를 extends 한다.

package com.example.demo.member;

import com.example.demo.member.annotation.ClassAop;
import com.example.demo.member.annotation.MethodAop;
import org.springframework.stereotype.Component;

@ClassAop
@Component
public class MemberServiceImpl extends BaseService implements MemberService{
    @Override
    @MethodAop("test value")
    public String hello(String param) {
        return "ok";
    }
    public String internal(String param){
        return "ok";
    }

    @Override
    public void commonMethod() {

    }
}

테스트케이스에 메서드들을 추가한다.

baseMethod는 오버라이딩하지 않았으므로 부모클래스인 BaseService에서 호출된다.

@Test
    void success(){
        log.info("memberService Proxy ={}",memberService.getClass());
        memberService.hello("helloA");
        memberServiceImpl.baseMethod();
        memberServiceImpl.commonMethod();
    }

확인해보면 @target은 오버라이드하지 않은 baseMethod까지 인식하는것을 볼 수 있다.

memberService Proxy =class com.example.demo.member.MemberServiceImpl$$EnhancerBySpringCGLIB$$8a95afdc
[@annotation]String com.example.demo.member.MemberServiceImpl.hello(String), annotationValue=test value
[@target]String com.example.demo.member.MemberServiceImpl.hello(String), annotation=@com.example.demo.member.annotation.ClassAop()
[@within]String com.example.demo.member.MemberServiceImpl.hello(String), annotation=@com.example.demo.member.annotation.ClassAop()
[@target]void com.example.demo.member.MemberServiceImpl.baseMethod(), annotation=@com.example.demo.member.annotation.ClassAop()
BaseService.BaseMethod
[@target]void com.example.demo.member.MemberServiceImpl.commonMethod(), annotation=@com.example.demo.member.annotation.ClassAop()
[@within]void com.example.demo.member.MemberServiceImpl.commonMethod(), annotation=@com.example.demo.member.annotation.ClassAop()

'Java > 스프링 AOP' 카테고리의 다른 글

스프링 AOP - 내부호출 문제  (0) 2023.09.17
스프링 AOP - execution(2)  (0) 2023.09.14
스프링 AOP - execution(1)  (0) 2023.09.12
Spring AOP - 어드바이스 종류  (0) 2023.09.11
Spring AOP - 포인트컷 분리  (0) 2023.09.07

+ Recent posts