Wie wir wissen, verwendet Spring Proxys, um Funktionen hinzuzufügen ( @Transactional
und @Scheduled
zum Beispiel). Es gibt zwei Möglichkeiten: Verwenden eines dynamischen JDK-Proxys (die Klasse muss nicht leere Schnittstellen implementieren) oder Generieren einer untergeordneten Klasse mithilfe des CGLIB-Codegenerators. Ich habe immer gedacht, dass ich mit proxyMode zwischen einem dynamischen JDK-Proxy und CGLIB wählen kann.
Aber ich konnte ein Beispiel erstellen, das zeigt, dass meine Annahme falsch ist:
Fall 1:
Singleton:
@Service
public class MyBeanA {
@Autowired
private MyBeanB myBeanB;
public void foo() {
System.out.println(myBeanB.getCounter());
}
public MyBeanB getMyBeanB() {
return myBeanB;
}
}
Prototyp:
@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;
}
}
Main:
MyBeanA beanA = context.getBean(MyBeanA.class);
beanA.foo();
beanA.foo();
MyBeanB myBeanB = beanA.getMyBeanB();
System.out.println("counter: " + myBeanB.getCounter() + ", class=" + myBeanB.getClass());
Ausgabe:
constructor invocation:0
0
0
counter: 0, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$2f3d648e
Hier können wir zwei Dinge sehen:
MyBeanB
wurde nur einmal instanziiert .- Um die
@Transactional
Funktionalität für hinzuzufügenMyBeanB
, verwendete Spring CGLIB.
Fall 2:
Lassen Sie mich die MyBeanB
Definition korrigieren :
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {
In diesem Fall lautet die Ausgabe:
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$b06d71f2
Hier können wir zwei Dinge sehen:
MyBeanB
wurde 3 mal instanziiert .- Um die
@Transactional
Funktionalität für hinzuzufügenMyBeanB
, verwendete Spring CGLIB.
Können Sie erklären, was los ist? Wie funktioniert der Proxy-Modus wirklich?
PS
Ich habe die Dokumentation gelesen:
/**
* 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
*/
aber es ist mir nicht klar.
Aktualisieren
Fall 3:
Ich habe einen weiteren Fall untersucht, in dem ich die Schnittstelle extrahiert habe aus 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 {
und in diesem Fall ist die Ausgabe:
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class com.sun.proxy.$Proxy92
Hier können wir zwei Dinge sehen:
MyBeanB
wurde 3 mal instanziiert .- Um die
@Transactional
Funktionalität für hinzuzufügenMyBeanB
, verwendete Spring einen dynamischen JDK-Proxy.
MyBeanB
Klasse keine Schnittstellen, sodass es nicht verwunderlich ist, dass in Ihrem Konsolenprotokoll CGLIB-Proxy-Instanzen angezeigt werden. In Fall 3, in dem Sie eine Schnittstelle einführen und implementieren, erhalten Sie folglich einen JDK-Proxy. Sie beschreiben dies sogar in Ihrem Einführungstext.
<aop:config proxy-target-class="true">
bzw. konfiguriert @EnableAspectJAutoProxy(proxyTargetClass = true)
.