[Spring] Spring AOP 구현해보기 (@Aspect, @Pointcut, @Around)
이전 글 [Spring] Spring AOP AOP AOP : Aspect Oriented Programming의 약자로, 여러 객체에 공통으로 적용할 수 있는 기능을 분리해서 재사용성을 높여주는 프로그래밍 기법입니다. AOP는 핵심 기능과 공통 기능
muscleking3426.tistory.com
프록시 생성 방식
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import aspect.ExeTimeAspect;
import chap07.Calculator;
import chap07.RecCalculator;
@Configuration
@EnableAspectJAutoProxy
public class AppCtx {
@Bean
public ExeTimeAspect exeTimeAspect() {
return new ExeTimeAspect();
}
@Bean
public Calculator calculator() {
return new RecCalculator();
}
}
package main;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import chap07.Calculator;
import chap07.RecCalculator;
import config.AppCtx;
public class MainAspect {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppCtx.class);
Calculator cal = ctx.getBean("calculator", RecCalculator.class);
long fiveFact = cal.factorial(5);
System.out.println("cal.factorial(5) = " + fiveFact);
System.out.println(cal.getClass().getName());
ctx.close();
}
}
MainAspect에서 getBean메서드에 Calculator를 상속받는 RecCalculator 클래스를 사용하면 문제가 없을까요?
2월 05, 2024 9:24:55 오후 org.springframework.context.support.AbstractApplicationContext prepareRefresh
정보: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4b9af9a9: startup date [Mon Feb 05 21:24:55 KST 2024]; root of context hierarchy
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'calculator' is expected to be of type 'chap07.RecCalculator' but was actually of type 'com.sun.proxy.$Proxy17'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:384)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1091)
at main.MainAspect.main(MainAspect.java:15)
실행결과는 BeanNotOfRequiredTypeException 이 발생하였습니다. 왜 발생하였을까요? RecCalculator는 Calculaotor를 상속받는데 말이죠..
메시지를 더 자세히 보시면
Bean named 'calculator' is expected to be of type 'chap07.RecCalculator' but was actually of type 'com.sun.proxy.$Proxy17'
getBean()에 사용한 타입이 RecCalculator인데 실제 타입은 $Proxy17이라는 메시지가 나옵니다. $Proxy17은 스프링이 런타임에 생성한 프록시 객체의 클래스 이름입니다. $Proxy17 클래스는 RecCalculator 클래스가 상속받은 Calculator 인터페이스를 상속받게 됩니다.
스프링 AOP를 위한 프록시 객체를 생성할 때 실제 생성할 빈 객체가 인터페이스를 상속하면 인터페이스를 이용해서 프록시를 생성합니다.
앞서, RecCalculator 클래스가 Calculator 인터페이스를 상속하므로 Calculator 인터페이스를 상속받은 프록시 객체를 생성했습니다.
따라서, 이 코드처럼 빈의 실제 타입이 RecCalculator라고 하더라도 "calculator" 이름에 해당하는 빈 객체의 타입은 Calculator 인터페이스를 상속받는 프록시 타입이 됩니다.
//AOP 적용시 RecCalculator 가 상속받은 Calculator 인터페이스를 이용해서 프록시 생성
@Bean
public ExeTimeAspect exeTimeAspect() {
return new ExeTimeAspect();
}
Calculator cal = ctx.getBean("calculator", RecCalculator.class);
// "calculator" 빈의 실제 타입은 Calculator를 상속한 프록시 타입이므로 RecCalculator로 타입 변환 불가
// 익셉션 발생
빈 객체가 인터페이스 상속 시, 클래스를 이용해서 프록시를 이용하여 생성하는 법
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppCtx {
@EnableAspectJAutoProxy 어노테이션의 proxyTargetClass 속성을 true로 지정하면 인터페이스가 아닌 자바 클래스를 상속받아 프록시를 생성합니다. 스프링이 프록시를 이용해 생성한 빈 객체를 구할 때 getBean() 메서드에 실제 클래스를 이용해서 빈 객체를 구할 수 있게 됩니다.
//"calculator" 프록시의 실제 타입은 RecCalculator를 상속받았으므로 RecCalculator 변환 가능
RecCalculator cal = ctx.getBean("calculator", RecCalculator.class);
'Spring' 카테고리의 다른 글
[Spring] Spring AOP - Advice 적용순서 (0) | 2024.02.14 |
---|---|
[Spring] AOP - execution 명시자 표현식 (0) | 2024.02.05 |
[Spring] Spring AOP 구현해보기 (@Aspect, @Pointcut, @Around) (1) | 2024.02.02 |
[Spring] Spring AOP (0) | 2024.01.31 |
[Spring] AOP 프로그래밍 - 프록시(Proxy) 객체 (0) | 2024.01.30 |