Spring Boot proxy-target-class 속성
개요
관례적으로 클래스를 인터페이스로 구현하는 이유에 대해 찾아보게 되었고 이후 proxy-target-class라는 것을 알게 되었습니다.
따라서 해당 설정을 실험해보고자 합니다.
application.yml
spring:
aop:
proxy-target-class: false
기본값이 true입니다.
해당값을 false로 바꾸면 어떤 일이 일어날까요?
- false - Jdk Dynamic Proxy를 사용한다는 의미로 인터페이스로 구현되어 있지 않다면 AOP가 적용되지 않음!
- true - CGLIB가 대상 클래스를 상속받아 프락시를 구현해서 인터페이스가 없어도 된다, 클래스가 final인 경우 프락시 생성이 불가능하다.
proxy-target-class 속성이란?
"description": "Whether subclass-based (CGLIB) proxies are to be created (true)
, as opposed to standard Java interface-based proxies (false).",
proxy-target-class 속성을 타고 들어가 보면 spring-boot-autuconfigure 모듈의 spring-configuration-metadata.json 파일에 위와 같은 설명이 적혀있습니다.
true인 경우 CGLIB 방식으로 proxy가 생성되고, 반대인 경우에는 자바의 interface 방식으로 적용된다고 합니다.
테스트
proxy-target-class : true인 경우에는 해당 클래스가 인터페이스로 구현되었는지 유무에 관계없이 CGLIB 방식의 Proxy가 적용됩니다.
Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies
by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.
proxy-target-class : false인 경우에는 타깃 클래스가 인터페이스로 구현되어 있지 않은 경우에 예외가 발생합니다.
proxy-target-class: false이며 Injecting을 하는 부분을 구체 클래스가 아닌 interface로 주입받으면 JdkDynamicAopProxy 방식이 적용된 것을 알 수 있습니다.
Spring Boot에서 CGLIB가 기본전략인 이유
- final이 붙으면 오버라이딩이 불가능하므로 CGLIB프록시가 작동하지 않는다.
- Enhancer의존성을 추가해야 한다.
- 3.2 version Spring core 패키지에 포함되어서 의존성 추가하지 않아도 된다.
- Default 생성자가 필요하다.
- 4.0 version Objensis 라이브러리를 포함하면서 필요 없어졌다.
- 타깃의 생성자를 두 번 호출한다.
- 4.0 version Objensis 라이브러리를 포함하면서 한 번만 호출된다.
인터페이스 기반 프락시는 때때로 ClassCast Exceptions를 추적하기 어렵게 한다. 특히, @Bean이 JDK 프락시로 대체되어 원래 클래스 형식으로 주입되지 않을 수 있었다.
스프링 프레임워크는 요즘 CGLIB의 음영 복사가 있기 때문에, 즉시 사용하지 않을 이유가 거의 없었다.
마무리
만약 특정 이유에 따라 proxy-target-class가 fasle로 설정해야 하거나, 기본전략이 JdkDynamicProxy 방식이라면 클래스에 인터페이스를 구현하고 인터페이스로 빈을 주입해야 하는 상황이 있을 수 있습니다.
참고자료