Seperti yang kita ketahui Spring menggunakan proksi untuk menambah fungsionalitas ( @Transactional
dan@Scheduled
misalnya). Ada dua opsi - menggunakan proxy dinamis JDK (kelas harus mengimplementasikan antarmuka yang tidak kosong), atau menghasilkan kelas anak menggunakan generator kode CGLIB. Saya selalu berpikir bahwa proxyMode memungkinkan saya untuk memilih antara proxy dinamis JDK dan CGLIB.
Tetapi saya dapat membuat contoh yang menunjukkan bahwa asumsi saya salah:
Kasus 1:
Singleton:
@Service
public class MyBeanA {
@Autowired
private MyBeanB myBeanB;
public void foo() {
System.out.println(myBeanB.getCounter());
}
public MyBeanB getMyBeanB() {
return myBeanB;
}
}
Prototipe:
@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;
}
}
Utama:
MyBeanA beanA = context.getBean(MyBeanA.class);
beanA.foo();
beanA.foo();
MyBeanB myBeanB = beanA.getMyBeanB();
System.out.println("counter: " + myBeanB.getCounter() + ", class=" + myBeanB.getClass());
Keluaran:
constructor invocation:0
0
0
counter: 0, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$2f3d648e
Di sini kita dapat melihat dua hal:
MyBeanB
hanya dipakai satu kali .- Untuk menambahkan
@Transactional
fungsiMyBeanB
, Spring menggunakan CGLIB.
Kasus 2:
Biarkan saya memperbaiki MyBeanB
definisi:
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {
Dalam hal ini outputnya adalah:
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$b06d71f2
Di sini kita dapat melihat dua hal:
MyBeanB
dipakai 3 kali.- Untuk menambahkan
@Transactional
fungsiMyBeanB
, Spring menggunakan CGLIB.
Bisakah Anda menjelaskan apa yang sedang terjadi? Bagaimana cara proxy benar-benar berfungsi?
PS
Saya sudah membaca dokumentasinya:
/**
* 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
*/
tetapi tidak jelas bagi saya.
Memperbarui
Kasus 3:
Saya menyelidiki satu lagi kasus, di mana saya mengekstrak antarmuka dari 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 {
dan dalam hal ini hasilnya adalah:
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class com.sun.proxy.$Proxy92
Di sini kita dapat melihat dua hal:
MyBeanB
dipakai 3 kali.- Untuk menambahkan
@Transactional
fungsiMyBeanB
, Spring menggunakan proxy dinamis JDK.
MyBeanB
kelas Anda tidak memperluas antarmuka apa pun, sehingga tidak mengejutkan bahwa log konsol Anda menunjukkan contoh proxy CGLIB. Dalam kasus 3 Anda memperkenalkan dan mengimplementasikan antarmuka, akibatnya Anda mendapatkan proxy JDK. Anda bahkan menggambarkan ini dalam teks pengantar Anda.
<aop:config proxy-target-class="true">
atau @EnableAspectJAutoProxy(proxyTargetClass = true)
, masing-masing.