우리가 알고 있듯이 봄이 기능을 추가하는 프록시를 사용하는 ( @Transactional
그리고 @Scheduled
예를 들어). JDK 동적 프록시 (클래스가 비어 있지 않은 인터페이스를 구현해야 함)를 사용하거나 CGLIB 코드 생성기를 사용하여 하위 클래스를 생성하는 두 가지 옵션이 있습니다. 나는 항상 proxyMode를 사용하여 JDK 동적 프록시와 CGLIB 중에서 선택할 수 있다고 생각했습니다.
그러나 나는 내 가정이 잘못되었음을 보여주는 예를 만들 수있었습니다.
사례 1 :
하나씩 일어나는 것:
@Service
public class MyBeanA {
@Autowired
private MyBeanB myBeanB;
public void foo() {
System.out.println(myBeanB.getCounter());
}
public MyBeanB getMyBeanB() {
return myBeanB;
}
}
원기:
@Service
@Scope(value = "prototype")
public class MyBeanB {
private static final AtomicLong COUNTER = new AtomicLong(0);
private Long index;
public MyBeanB() {
index = COUNTER.getAndIncrement();
System.out.println("constructor invocation:" + index);
}
@Transactional // just to force Spring to create a proxy
public long getCounter() {
return index;
}
}
본관:
MyBeanA beanA = context.getBean(MyBeanA.class);
beanA.foo();
beanA.foo();
MyBeanB myBeanB = beanA.getMyBeanB();
System.out.println("counter: " + myBeanB.getCounter() + ", class=" + myBeanB.getClass());
산출:
constructor invocation:0
0
0
counter: 0, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$2f3d648e
여기서 우리는 두 가지를 볼 수 있습니다.
MyBeanB
오직 인스턴스화되었다 한 번만 .@Transactional
기능 을 추가하려면MyBeanB
Spring은 CGLIB를 사용했습니다.
사례 2 :
MyBeanB
정의를 수정하겠습니다 :
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {
이 경우 출력은 다음과 같습니다.
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$b06d71f2
여기서 우리는 두 가지를 볼 수 있습니다.
MyBeanB
3 인스턴스화되었습니다 번 되었습니다.- 에 대한
@Transactional
기능 을 추가하기 위해MyBeanB
Spring은 CGLIB를 사용했습니다.
무슨 일인지 설명해 주시겠습니까? 프록시 모드는 실제로 어떻게 작동합니까?
추신
설명서를 읽었습니다.
/**
* Specifies whether a component should be configured as a scoped proxy
* and if so, whether the proxy should be interface-based or subclass-based.
* <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates
* that no scoped proxy should be created unless a different default
* has been configured at the component-scan instruction level.
* <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
* @see ScopedProxyMode
*/
그러나 그것은 분명하지 않습니다.
최신 정보
사례 3 :
인터페이스를 추출한 사례를 한 번 더 조사했습니다 MyBeanB
.
public interface MyBeanBInterface {
long getCounter();
}
@Service
public class MyBeanA {
@Autowired
private MyBeanBInterface myBeanB;
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
public class MyBeanB implements MyBeanBInterface {
이 경우 출력은 다음과 같습니다.
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class com.sun.proxy.$Proxy92
여기서 우리는 두 가지를 볼 수 있습니다.
MyBeanB
3 인스턴스화되었습니다 번 되었습니다.- 의
@Transactional
기능 을 추가하기 위해MyBeanB
Spring은 JDK 동적 프록시를 사용했습니다.
MyBeanB
클래스가 인터페이스를 확장하지 않으므로 콘솔 로그에 CGLIB 프록시 인스턴스가 표시되는 것은 놀라운 일이 아닙니다. 3의 경우 인터페이스를 도입하고 구현하면 JDK 프록시가 생성됩니다. 당신은 심지어 당신의 소개 텍스트에 이것을 설명합니다.
<aop:config proxy-target-class="true">
또는 @EnableAspectJAutoProxy(proxyTargetClass = true)
각각.