ดังที่เราทราบ Spring ใช้พร็อกซีเพื่อเพิ่มฟังก์ชันการทำงาน ( @Transactional
และ@Scheduled
ตัวอย่าง) มีสองตัวเลือก - การใช้ JDK ไดนามิกพร็อกซี่ (คลาสต้องใช้อินเตอร์เฟสที่ไม่ว่าง) หรือสร้างคลาสย่อยโดยใช้ตัวสร้างโค้ด CGLIB ฉันคิดเสมอว่า proxyMode ช่วยให้ฉันสามารถเลือกระหว่าง JDK dynamic proxy และ 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
ถูก instantiated เพียงครั้งเดียว- เพื่อเพิ่ม
@Transactional
ฟังก์ชันการทำงานสำหรับMyBeanB
ฤดูใบไม้ผลิใช้ 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
ฤดูใบไม้ผลิใช้ CGLIB
คุณช่วยอธิบายสิ่งที่เกิดขึ้นได้ไหม โหมดพร็อกซี่ทำงานอย่างไร
PS
ฉันอ่านเอกสารแล้ว:
/**
* 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 dynamic proxy
MyBeanB
คลาสของคุณไม่ได้เพิ่มส่วนต่อประสานใด ๆ ดังนั้นจึงไม่น่าแปลกใจที่บันทึกคอนโซลของคุณ ในกรณีที่ 3 คุณแนะนำและใช้อินเทอร์เฟซดังนั้นคุณจะได้รับ JDK proxy คุณยังอธิบายสิ่งนี้ในข้อความเกริ่นนำของคุณ
<aop:config proxy-target-class="true">
หรือ@EnableAspectJAutoProxy(proxyTargetClass = true)
ตามลำดับ