스프링 레시피 CH2.19 AOP를 이용해 POJO에 상태 추가하기

2023. 12. 19. 23:17spring

과제

기존 객체에 새로운 상태를 추가해서 호출 횟수, 최종 수정 일자 등 사용 내역을 파악하고 싶은 경우가 있다.

모든 객체가 동일한 베이스 클래스를 상속하는 건 말이 안된다. 레이어 구조가 다른 여러 클래스에 상태를 추가하기란 더욱 어렵다.

해결책

상태 필드가 위치한 구현 클래스의 인터페이스를 기존 객체에 들여온 다음, 특정 조건에 따라 상태값을 바꾸는 어드바이스를 작성한다.

풀이

각 Calculator 객체의 호출 횟수를 기록하려고 한다.

원본 클래스에는 호출 횟수를 담을 카운터 필드가 없기 때문에 AOP 인트로덕션을 적용한다.

 

먼저 Counter 인터페이스를 작성한다.

public interface Counter {
    void increase();
    int getCount();
}

 

그리고 간단한 구현 클래스를 만든다.

public class CounterImpl implements Counter {

    private int count;

    @Override
    public void increase() {
        count++;
    }

    @Override
    public int getCount() {
        return count;
    }
}

 

Counter 인터페이스를 CounterImpl로 구현한 모든 xxxxCalculator 객체에 들여오기 위해 다음과 같이 타입 매치 표현식을 이용해 인트로덕션을 적용한다.

@DeclareParents(
        value = "com.spring.study.chapter02.aop.calculation._interface.*CalculatorImpl",
        defaultImpl = CounterImpl.class
)
public Counter counter;

 

특정 조건에 따라 상태값을 바꾸는 어드바이스를 작성한다.

*주의 : 위에서 CounterImpl로 구현한 xxxxCalculator 객체는 동적 프록시 객체이므로 target이 아닌 this 객체를 가져와야 한다.

@After("execution(* com.spring.study.chapter02.aop.calculation._interface.*Calculator.*(..)) && this(counter)")
public void increaseCount(Counter counter) {
    counter.increase();
}

 

public static void main(String[] args) {
    ApplicationContext context =
            new AnnotationConfigApplicationContext(CalculatorConfiguration.class);

    ArithmeticCalculator arithmeticCalculator =
            context.getBean("arithmeticCalculator", ArithmeticCalculator.class);
    arithmeticCalculator.add(1, 2);

    UnitCalculator unitCalculator =
            context.getBean("unitCalculator", UnitCalculator.class);
    unitCalculator.kilogramToPound(1);

    Counter arithmeticCounter = (Counter) arithmeticCalculator;
    System.out.println(arithmeticCounter.getCount());
}
=>
1.0 + 2.0 = 3.0
1.0 kilogram = 2.2 pound
1