Bildiğimiz gibi Spring, işlevsellik eklemek için vekilleri kullanıyor ( @Transactional
ve @Scheduled
örneğin). İki seçenek vardır - JDK dinamik proxy kullanma (sınıfın boş olmayan arabirimler uygulaması gerekir) veya CGLIB kod üretecini kullanarak bir alt sınıf oluşturma. Her zaman proxyMode'un JDK dinamik proxy ve CGLIB arasında seçim yapmamı sağladığını düşündüm.
Ancak varsayımımın yanlış olduğunu gösteren bir örnek oluşturabildim:
Dava 1:
Singleton:
@Service
public class MyBeanA {
@Autowired
private MyBeanB myBeanB;
public void foo() {
System.out.println(myBeanB.getCounter());
}
public MyBeanB getMyBeanB() {
return myBeanB;
}
}
Prototip:
@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;
}
}
Ana:
MyBeanA beanA = context.getBean(MyBeanA.class);
beanA.foo();
beanA.foo();
MyBeanB myBeanB = beanA.getMyBeanB();
System.out.println("counter: " + myBeanB.getCounter() + ", class=" + myBeanB.getClass());
Çıktı:
constructor invocation:0
0
0
counter: 0, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$2f3d648e
Burada iki şey görebiliriz:
MyBeanB
yalnızca bir kez başlatıldı .- İçin
@Transactional
işlevsellik eklemek içinMyBeanB
, Bahar CGLIB kullandı.
Durum 2:
MyBeanB
Tanımı düzeltmeme izin verin :
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {
Bu durumda çıktı:
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$b06d71f2
Burada iki şey görebiliriz:
MyBeanB
3 kez başlatıldı .- İçin
@Transactional
işlevsellik eklemek içinMyBeanB
, Bahar CGLIB kullandı.
Neler olduğunu açıklayabilir misiniz? Proxy modu gerçekten nasıl çalışır?
PS
Belgeleri okudum:
/**
* 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
*/
ama benim için net değil.
Güncelleme
Durum 3:
Arayüzü buradan çıkardığım bir vaka daha araştırdım 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 {
ve bu durumda çıktı:
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class com.sun.proxy.$Proxy92
Burada iki şey görebiliriz:
MyBeanB
3 kez başlatıldı .- İçin
@Transactional
işlevsellik eklemek içinMyBeanB
, Spring bir JDK dinamik proxy kullandı.
MyBeanB
sınıfınız herhangi bir arabirimi genişletmez, bu nedenle konsol günlüğünüzün CGLIB proxy örneklerini göstermesi şaşırtıcı değildir. 3. bir arabirim tanıtır ve uygularsanız, sonuç olarak bir JDK proxy'si alırsınız. Bunu tanıtım metninizde bile tanımlayabilirsiniz.
<aop:config proxy-target-class="true">
veya aracılığıyla yapılandırılır @EnableAspectJAutoProxy(proxyTargetClass = true)
.