Study/Spring

[Spring 5 Recipes] Spring 5 Recipes 2장 정리 #6

꼽냥이 2021. 9. 24. 16:56
반응형

POJO 구성 방식

14. JoinPoint 정보 가져오기

AOP 에서 어드바이스는 여러 조인포인트, 즉 프로그램 실행지점 곳곳에 적용된다. 어드바이스가 정확하게 작동하려면 조인포인트에 대한 세부 정보가 필요한 경우가 존재한다.

 

어드바이스 메소드의 Signature 에 org.aspectj.lang.JoinPoint 형 인수를 선언하면 조인포인트 정보를 얻을 수 있다. 다음과 같이 logJoinPoint 어드바이스에서 조인포인트 정보를 액세스한다고 하자. 필요한 정보는 조인포인트 유형, 메소드 시그니처, 인수값, 대상 객체와 프록시 객체이다.

@Aspect
@Component
public class CalculatorLoggingAspect {
	
    private Logger log = LoggerFactory.getLogger(this.getClass());
    
    @Before("execution(* *.*(..))") 
    public void logJoinPoint(JoinPoint joinPoint) {
        log.info("Join point kind : {}", joinPoint.getKind());
        log.info("Signature declaring type : {}", joinPoint.getSignature().getDeclaringTypeName());
        log.info("Signature name : {}", joinPoint.getSignature().getName());
        log.info("Arguments : {}", Arrays.toString(joinPoint.getArgs()));
        log.info("Target class : {}", joinPoint.getTarget().getClass().getName());
        log.info("This class : {}", joinPoint.getThis().getClass().getName());
    }
}

 

프록시로 감싼 원본 빈은 Target Object 라고 하며 프록시 객체는 this 로 참조한다. 대상 객체와 프록시 객체는 각각 조인포인트에서 getTarget(), getThis() 메소드로 가져올 수 있다. 실행 결과는 다음과 같다.

더보기

Join point kind : method-execution
Signature declaring type : com.apress.springrecipes.calculator.ArithmeticCalculator
Signature name : add
Arguments : [1.0, 2.0]
Target class : com.apress.springrecipes.calculator.ArithmeticCalculatorImpl
This class : com.sun.proxy.$Proxy18

 

15. @Order 로 애스펙트 우선순위 설정

같은 조인포인트에 애스펙트를 여러 개 적용할 경우, 우선순위를 정해야 한다. 애스펙트 간 우선순위는 Ordered 인터페이스를 구현하거나 @Order 어노테이션을 붙여 지정할 수 있다.

 

@Aspect
@Component
public class CalculatorVaildationAspect {
	
    @Before("execution(* *.*(double, double))")
    public void validateBefore(JoinPoint joinPoint) {
    	for (Object arg : joinPoint.getArgs()) {
        	validate((Double) arg);
        }
    }
    
    private void validate(double a) {
    	if (a < 0) {
        	throw new IllegalArgumentException("Positive Numbers only");
        }
    }
}

@Aspect
@Component
public class CalculatorLoggingAspect {
	
    @Before("execution(* *.*(..))")
    public void logBefore(JoinPoint joinPoint) {
    	// ...
    }
}

 

위와 같이 두 애스펙트가 존재할 경우 어느 쪽을 먼저 적용해야 할 지 알 수가 없다. 이런 경우 어느 한 애스팩트가 다른 것보다 먼저 실행되게 하기 위해 우선순위를 설정해야 한다. 두 애스펙트 모두 Ordered 인터페이스를 구현하거나 @Order 어노테이션을 활용하면 된다.

 

먼저 Ordered 인터페이스를 구현할 경우, getOrder() 메소드가 반환하는 값이 작을수록 우선순위가 높다. 다음과 같이 작성할 경우 검증 애스펙트가 로깅 애스펙트보다 우선순위가 더 높아진다.

@Aspect
@Component
public class CalculatorVaildationAspect implements Ordered {
	
    ...
    public int getOrder() {
    	return 0;
    }
}

@Aspect
@Component
public class CalculatorLoggingAspect implements Ordered {
	
    ...
    public int getOrder() {
    	return 1;
    }
}

 

아래와 같이 @Order 에 우선순위값을 넣으면 더 깔끔하게 구현이 가능하다.

@Aspect
@Component
@Order(0)
public class CalculatorVaildationAspect { ... }

@Aspect
@Component
@Order(1)
public class CalculatorLoggingAspect { ... }

 

16. 애스펙트 포인트컷 재사용

포인트컷 표현식을 여러 번 되풀이해 사용할 경우에, 어드바이스 어노테이션에 직접 써넣는 것보다 재사용할 방법을 필요로 하게 된다. @Pointcut 어노테이션을 활용하면 포인트컷만 따로 정의해 여러 어드바이스에서 재사용이 가능하다.

 

애스펙트에서 포인트컷은 @Pointcut 을 붙인 단순 메소드로 선언할 수 있다. 포인트컷과 어플리케이션 로직이 뒤섞이는 것은 바람직하지 않으니 메소드 바디는 보통 비워두고 포인트컷의 가시성은 메소드의 수정자로 조정한다. 이렇게 선언한 포인트컷은 다른 어드바이스가 메소드명으로 참조할 수 있다.

 

@Aspect
@Component
public class CalculatorLogginAspect {
	
    ...
    @Pointcut("execution(* *.*(..))")
    private void loggingOperation() {}
    
    @Before("loggingOperation()")
    public void logBefore() { ... }
    
    @After("loggingOperation()")
    public void logAfter() { ... }
}

 

여러 애스펙트가 포인트컷을 공유하는 경우라면 공통 클래스 한 곳에 포인트컷을 모아두는 편이 좋다. 이 때 포인트컷 메소드는 public 으로 선언한다.

@Aspect
public class CalculatorPointcuts {
	
    @Pointcut("execution(* *.*(..))")
    public void loggingOperation() {}
}

 

외부 클래스에 있는 포인트컷을 참조할 때는 클래스명도 함께 적는다. 만약 다른 패키지에 있는 경우는 패키지명까지 기재한다.

@Aspect
@Component
public class CalculatorLoggingAspect {
	
    @Before("CalculatorPointcuts.loggingOperation()")
    public void logBefore() { ... }
    
    @After("CalculatorPointcuts.loggingOperation()")
    public void logAfter() { ... }
}

 

#Reference.

 

스프링 5 레시피(4판)

이 책은 스프링 5에 새로 탑재된 기능 및 다양한 구성 옵션 등 업데이트된 프레임워크 전반을 실무에 유용한 해법을 제시하는 형식으로 다룹니다. IoC 컨테이너 같은 스프링 기초부터 스프링 AOP/A

www.hanbit.co.kr