2023. 12. 18. 23:36ㆍ카테고리 없음
과제
어떤 공통 로직을 공유하는 클래스가 여러 개 있을 경우, 보통 OOP에서는 같은 베이스 클래스를 상속하거나 같은 인터페이스를 구현하는 형태로 애플리케이션을 개발한다.
AOP 관점에서는 충분히 모듈화 가능한 공통 관심사인데, 자바는 언어 구조상 클래스를 오직 한 개만 상속할 수 있기 때문에 동시에 여러 구현 클래스로부터 기능을 물려받아 쓰는 일은 불가능하다.
해결책
인트로덕션은 AOP 어드바이스의 특별한 타입이다. 객체가 어떤 인터페이스의 구현 클래스를 공급받아 동적으로 인터페이스를 구현한다.
게다가 여러 구현 클래스를 지닌 여러 인터페이스를 동시에 인트로듀스할 수 있어서 사실상 다중 상속도 가능하다
풀이
다음 MaxCalculator, Mincalculator 두 인터페이스에 각각 max(), min() 메서드를 정의한다.
@FunctionalInterface
public interface MaxCalculator {
DoubleBinaryOperator max(DoubleBinaryOperator binaryOperator);
}
@FunctionalInterface
public interface MinCalculator {
DoubleBinaryOperator min(DoubleBinaryOperator binaryOperator);
}
두 인터페이스의 구현 클래스에 println문을 넣어 메서드 실행 시점을 파악한다.
public class MaxCalculatorImpl implements MaxCalculator {
@Override
public DoubleBinaryOperator max(DoubleBinaryOperator binaryOperator) {
System.out.println("max!");
return binaryOperator;
}
}
public class MinCalculatorImpl implements MinCalculator{
@Override
public DoubleBinaryOperator min(DoubleBinaryOperator binaryOperator) {
System.out.println("min!");
return binaryOperator;
}
}
자바에서는 두 구현 클래스를 동시에 상속받을 수 없다.(인터페이스는 가능)
이때 인트로덕션을 쓰면 두 구현 클래스를 다중 상속한 것처럼 쓸 수 있다.(동적 프록시 이용)
인트로덕션 역시 어드바이스처럼 애스펙트 안에서 필드에 @DeclareParents를 붙여 선언한다.
@DeclareParents(
value = "com.spring.study.chapter02.aop.calculation._interface.ArithmeticCalculatorImpl",
defaultImpl = MaxCalculatorImpl.class
)
public MaxCalculator maxCalculator;
@DeclareParents(
value = "com.spring.study.chapter02.aop.calculation._interface.ArithmeticCalculatorImpl",
defaultImpl = MinCalculatorImpl.class
)
public MinCalculator minCalculator;
인트로덕션 대상 클래스는 @DeclareParents의 value 속성으로 지정하며 이 애너테이션을 붙인 필드형에 따라 들여올 인터페이스가 결정된다.
이 새 인터페이스에서 사용할 구현 클래스는 defalutImpl 속성에 명시한다.
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(CalculatorConfiguration.class);
ArithmeticCalculator arithmeticCalculator =
context.getBean("arithmeticCalculator", ArithmeticCalculator.class);
MaxCalculator maxCalculator = (MaxCalculator) arithmeticCalculator;
MinCalculator minCalculator = (MinCalculator) arithmeticCalculator;
System.out.println(maxCalculator.max(Math::max).applyAsDouble(1, 2));
System.out.println(minCalculator.min(Math::min).applyAsDouble(1, 2));
}
=>
max!
2.0
min!
1.0