ดังที่เราทราบ 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)ตามลำดับ