說說Spring中為何要引入Lookup注解
前言
我們先探一探官方文檔關(guān)于Method Injection的章節(jié)是怎么說的:
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
用一句話概括就是 一個單例Bean A每次獲取另外一個Bean B的時候怎么保證這個Bean B是一個新的實例?
正文
ApplicationContextAware接口
官方文檔首先也提到了一個解決方案就是把A弄成容器的Aware( make bean A aware of the container),也就是實現(xiàn)ApplicationContextAware接口。
A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware interface, and by making a getBean("B") call to the container ask for (a typically new) bean B instance every time bean A needs it. The following example shows this approach
文檔里隨后就提供了一個示例來說明這個解決方案如何做。
// a class that uses a stateful Command-style class to perform some processing package fiona.apple; // Spring-API imports import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class CommandManager implements ApplicationContextAware { private ApplicationContext applicationContext; public Object process(Map commandState) { // grab a new instance of the appropriate Command Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } protected Command createCommand() { // notice the Spring API dependency! return this.applicationContext.getBean("command", Command.class); } public void setApplicationContext( ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
雖然解決了一開始提出的問題,但是Spring隨后就說到:
The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, lets you handle this use case cleanly.
也就是說 前面的方法是不可取的,因為業(yè)務(wù)代碼知道并耦合到Spring框架。那怎么降低這個耦合度呢?后半句就給出了答案:方法注入是Spring IOC容器的一個稍微高級的特性,它允許您干凈地處理這個用例。
Lookup Method方法注入
首先再次引入官方文檔中的闡述:
Lookup method injection is the ability of the container to override methods on container-managed beans and return the lookup result for another named bean in the container. The lookup typically involves a prototype bean, as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to dynamically generate a subclass that overrides the method.
簡要概括下這段話有三個意思:
- Lookup Method注入可以讓容器重寫容器中bean上的方法并返回容器中另一個bean的查找結(jié)果。
- Lookup通常會是一個原型bean,如文章開頭所說的。
- Spring框架通過使用CGLIB庫中的字節(jié)碼生成來動態(tài)生成覆蓋該方法的子類,從而實現(xiàn)這種方法注入。
使用Lookup方式需要注意以下幾點:
- For this dynamic subclassing to work, the class that the Spring bean container subclasses cannot be final, and the method to be overridden cannot be final, either.
- Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method.
- Concrete methods are also necessary for component scanning, which requires concrete classes to pick up.
- A further key limitation is that lookup methods do not work with factory methods and in particular not with @Bean methods in configuration classes, since, in that case, the container is not in charge of creating the instance and therefore cannot create a runtime-generated subclass on the fly.
這段話也可以概括為以下幾點:
- 使這個動態(tài)子類可以用,這個類不能是final,要重寫的方法也不能是final。
- 單元測試具有抽象方法的類需要您自己對該類進(jìn)行子類化,并提供抽象方法的存根實現(xiàn)。
- 具體的方法對于組件掃描也是必要的,這需要具體的類來獲取。
- 一個關(guān)鍵限制是Lookup方法不適用于工廠方法,尤其是配置類中的@Bean方法,因為在這種情況下,容器不負(fù)責(zé)創(chuàng)建實例,因此不能動態(tài)創(chuàng)建運行時生成的子類。
接下來Spring就拿上面本來基于ApplicationContextAware的方法來說,就可以用Lookup來替換了。對于前面代碼段中的CommandManager類,Spring容器動態(tài)重寫createCommand()方法的實現(xiàn)。CommandManager類沒有任何Spring依賴項,如修改后的示例所示
In the case of the CommandManager class in the previous code snippet, the Spring container dynamically overrides the implementation of the createCommand() method. The CommandManager class does not have any Spring dependencies, as the reworked example shows.
Xml配置lookup-method
public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand(); }
在包含要注入的方法的CommandManager類中,要注入的方法需要以下形式的簽名:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
如果方法是抽象的,則動態(tài)生成的子類將實現(xiàn)該方法。否則,動態(tài)生成的子類將重寫在原始類中定義的具體方法。
我們來看下Bean的配置:
當(dāng)需要myCommand這個Bean的新實例時,被標(biāo)識為commandManager的bean就會調(diào)用自己的createCommand()方法。如果需要的話,必須小心地將myCommand 這個bean部署為原型。如果是單例,則每次都返回相同的myCommand bean實例.
@Lookup注解
在基于注解的組件模型中,可以通過@lookup注釋聲明查找方法,如下例所示
public abstract class CommandManager { public Object process(Object commandState) { Command command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup("myCommand") protected abstract Command createCommand(); }
或者,你也可以依靠目標(biāo)bean根據(jù)lookup方法的聲明返回類型進(jìn)行解析
public abstract class CommandManager { public Object process(Object commandState) { MyCommand command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup protected abstract MyCommand createCommand(); }
需要注意的是你通常應(yīng)該用一個具體的存根實現(xiàn)聲明這種帶注解的查找方法,以便它們與Spring的組件掃描規(guī)則兼容,默認(rèn)情況下抽象類會被忽略。此限制不適用于顯式注冊或顯式導(dǎo)入的bean類。也就是說如果用組件掃描Bean的話因為抽象類默認(rèn)是被忽略的,但是你加上這個Lookup注解后就不會唄忽略。
Spring在最后也提供了其他兩種解決思路:
Another way of accessing differently scoped target beans is an ObjectFactory/ Provider injection point. See Scoped Beans as Dependencies。You may also find the ServiceLocatorFactoryBean (in the org.springframework.beans.factory.config package) to be useful.
- 通過ObjectFactory或者ObjectProvider.
- 通過ServiceLocatorFactoryBean.
這兩個方案我們之后會單獨寫文章來探討,下篇文章我打算來具體的使用下這個Lookup 方法注入并且從源碼角度來看下Spring如何巧妙地實現(xiàn)它的。
總結(jié)
到此這篇關(guān)于Spring中為何要引入Lookup注解的文章就介紹到這了,更多相關(guān)Spring為何引入Lookup內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中SimpleDateFormat方法超詳細(xì)分析
這篇文章主要給大家介紹了關(guān)于Java中SimpleDateFormat方法超詳細(xì)分析的相關(guān)資料,SimpleDateFormat 是一個以國別敏感的方式格式化和分析數(shù)據(jù)的具體類,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08JDBC數(shù)據(jù)庫連接過程及驅(qū)動加載與設(shè)計模式詳解
這篇文章主要介紹了JDBC數(shù)據(jù)庫連接過程及驅(qū)動加載與設(shè)計模式詳解,需要的朋友可以參考下2016-10-10Idea 2020.2安裝MyBatis Log Plugin 不可用的解決方法
小編在使用時發(fā)現(xiàn)Idea 2020.2 MyBatis Log Plugin 收費了,這個可以替代用,小編特此把解決方案分享到腳本之家平臺供大家參考,感興趣的朋友一起看看吧2020-11-11JAVA調(diào)用SAP WEBSERVICE服務(wù)實現(xiàn)流程圖解
這篇文章主要介紹了JAVA調(diào)用SAP WEBSERVICE服務(wù)實現(xiàn)流程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10spring-boot List轉(zhuǎn)Page的方法步驟
這篇文章主要介紹了spring-boot List轉(zhuǎn)Page的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03Java性能優(yōu)化之關(guān)于大對象復(fù)用的目標(biāo)和注意點
這篇文章主要介紹了Java性能優(yōu)化之關(guān)于大對象復(fù)用的目標(biāo)和注意點,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03