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 |