1 回答

TA貢獻(xiàn)1796條經(jīng)驗 獲得超7個贊
為行為生成的代理@Transactional
與作用域代理具有不同的用途。
代理@Transactional
是一種包裝特定 bean 以添加會話管理行為的代理。所有方法調(diào)用都將在委托給實際 bean 之前和之后執(zhí)行事務(wù)管理。
如果你舉例說明的話,它看起來像
main?->?getCounter?->?(cglib-proxy?->?MyBeanB)
出于我們的目的,您基本上可以忽略它的行為(刪除@Transactional
后您應(yīng)該看到相同的行為,除非您沒有 cglib 代理)。
代理@Scope
的行為有所不同。文檔指出:
[...]您需要注入一個代理對象,該對象公開與作用域?qū)ο笙嗤墓步涌冢?strong>但也可以從相關(guān)作用域(例如 HTTP 請求)檢索真實目標(biāo)對象,并將方法調(diào)用委托給真實對象。
Spring 真正做的是為代表代理的工廠類型創(chuàng)建一個單例 bean 定義。然而,相應(yīng)的代理對象會在每次調(diào)用時查詢上下文以獲取實際的 bean。
如果你舉例說明的話,它看起來像
main?->?getCounter?->?(cglib-scoped-proxy?->?context/bean-factory?->?new?MyBeanB)
由于MyBeanB
是原型 bean,上下文將始終返回一個新實例。
出于本答案的目的,假設(shè)您MyBeanB
直接使用
MyBeanB?beanB?=?context.getBean(MyBeanB.class);
這本質(zhì)上就是 Spring 為滿足@Autowired
注入目標(biāo)所做的事情。
在你的第一個例子中,
@Service
@Scope(value = "prototype")
public class MyBeanB {?
您聲明原型 bean定義(通過注釋)。@Scope
有一個proxyMode
元素
指定組件是否應(yīng)配置為作用域代理,如果是,則代理是否應(yīng)基于接口或基于子類。
默認(rèn)為
ScopedProxyMode.DEFAULT
,這通常表示不應(yīng)創(chuàng)建作用域代理,除非在組件掃描指令級別配置了不同的默認(rèn)值。
因此 Spring 不會為生成的 bean 創(chuàng)建作用域代理。您可以使用以下命令檢索該 bean
MyBeanB?beanB?=?context.getBean(MyBeanB.class);
您現(xiàn)在擁有對 Spring 創(chuàng)建的新對象的引用MyBeanB
。這與任何其他 Java 對象一樣,方法調(diào)用將直接轉(zhuǎn)到引用的實例。
如果再次使用getBean(MyBeanB.class)
,Spring 將返回一個新實例,因為 bean 定義是針對原型 bean的。您沒有這樣做,因此所有方法調(diào)用都會轉(zhuǎn)到同一個對象。
在你的第二個例子中,
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {
您聲明一個通過 cglib 實現(xiàn)的作用域代理。當(dāng)從 Spring 請求這種類型的 bean 時
MyBeanB?beanB?=?context.getBean(MyBeanB.class);
Spring知道這MyBeanB
是一個作用域代理,因此返回一個滿足API的代理對象MyBeanB
(即實現(xiàn)其所有公共方法),該對象內(nèi)部知道如何MyBeanB
為每個方法調(diào)用檢索實際的bean類型。
嘗試跑步
System.out.println("singleton?:?"?+?(context.getBean(MyBeanB.class)?==?context.getBean(MyBeanB.class)));
這將返回true
暗示 Spring 返回一個單例代理對象(而不是原型 bean)的事實。
在代理實現(xiàn)內(nèi)部的方法調(diào)用上,Spring 將使用一個特殊getBean
版本,該版本知道如何區(qū)分代理定義和實際MyBeanB
bean 定義。這將返回一個新MyBeanB
實例(因為它是原型),Spring 將通過反射將方法調(diào)用委托給它(經(jīng)典Method.invoke
)。
您的第三個示例與第二個示例基本相同。
添加回答
舉報