今天來講的一個你可能不曾注意的小東西,那就是Spring依賴注入支持注入Bean的類型,這個小東西可能看似沒有用但是實際又有點小用。
其實本來這周沒打算寫文章,但是突然之間就想到了之前有個妹子問過這個問題,并且網(wǎng)上這塊東西說的也不多,所以就趕在周末的末尾匆匆寫下了這篇文章。
這東西本身也沒有什么復(fù)雜的原理,所以本文也并沒有什么太多深入剖析源碼的東西。
1、普通對象
這沒什么好說的,大家都這么用的,比如需要用到UserService,直接@Autowired就可以了。
@Autowired
private UserService userService;
2、Collection及其子接口
除了支持注入一個單一的對象之外,@Autowired還支持注入一個Collection對象。
比如說,現(xiàn)在有個消息通知的接口MessageNotifier
。
這種接口一般都會有不同的實現(xiàn),比如說通過郵件通知,或者app,短信等等,所以就有多種實現(xiàn),此時如果需要注入MessageNotifier
,就可以使用注入Collection的方式,比如
@Autowired
private List<MessageNotifier> messageNotifiers;
不過這種方式有個規(guī)定,那就是注入的類型必須是Collection及其子接口,如果你直接注入一個ArrayList
,那么此時是不支持的。
3、數(shù)組
同理,@Autowired可實現(xiàn)了注入一個數(shù)組的功能。
@Autowired
private MessageNotifier[] messageNotifiers;
代碼如下:
4、Map
同樣的,@Autowired還可以注入一個Map。
@Autowired
private Map<String, MessageNotifier> messageNotifierMap;
此時注入的map,key的類型就是bean的名稱,這種方式可以配合策略模式使用。
不過,這種方式只支持注入的是Map接口,不支持子類型接口,代碼如下。
5、@Lazy
當(dāng)一個注入的字段加了@Lazy注解之后,那么此時就代表這個字段是延遲注入。
@Autowired
@Lazy
private MessageNotifier messageNotifier;
延遲注入并不是不注入,而是注入目標(biāo)對象類型的代理對象,真正的目標(biāo)是當(dāng)需要用到的時候在創(chuàng)建。
如圖所示,當(dāng)注入的MessageNotifier
時加了@Lazy注解,那么此時注入的其實是MessageNotifier
的代理對象,而真正的MessageNotifier
對象并沒有創(chuàng)建,圖中代理對象我稱為MessageNotifierProxy
。
由于注入的是對象是代理對象MessageNotifierProxy
,那么真正被使用的就是MessageNotifierProxy
,一旦調(diào)用了MessageNotifierProxy
的方法,此時MessageNotifierProxy
會去Spring容器中查找真正的MessageNotifier
對象,然后再調(diào)用MessageNotifier
對象的方法。
代碼如下:
這就是@Lazy延遲注入的原理。并不是不注入,而是注入一個代理對象,可以理解為一個占位符,一個空殼子,先占著位置,等用到這個殼子的時候,這個殼子會去查找到真正的對象,調(diào)用真正對象的方法。
@Lazy的一個使用場景就是用來解決Spring無法處理的循環(huán)依賴場景,比如使用了@Async注解的循環(huán)依賴的場景,不了解的小伙伴可以看一下 @Async注解的坑,小心 這篇文章
6、Optional
Optional是JDK1.8提供的一個api,可以優(yōu)雅的解決判空的問題。
@Autowired也支持了注入Optional類型。
@Autowired
private Optional<MessageNotifier> messageNotifier;
代碼如下:
注入Optional這種方式可以解決注入的對象不存在的導(dǎo)致異常問題,也就是安全注入。
比如說,MessageNotifier
這個對象Spring容器中并沒有,如果直接注入,此時會拋NoSuchBeanDefinitionException
異常
而直接通過注入Optional的方式就可以解決這個問題。
除了通過Optional的方式之外,也可以直接把@Autowired的required
的屬性設(shè)置為false來解決注入對象不存在的問題。
那Optional存在的作用是啥?
其實Optional的作用僅僅是不用寫為空的判斷,這也是Optional這個類的作用作用,除了這個,跟直接@Autowired對象并沒有其它區(qū)別。
注入Optional這種方式其實用的不多,在我的映像中,我在源碼中幾乎沒有看見這種注入方式。
7、ObjectFactory和ObjectProvider
ObjectFactory和ObjectProvider是Spring提供的兩接口
ObjectFactory
ObjectProvider繼承了ObjectFactory
ObjectProvider
@Autowired也可以直接注入這兩個接口。
@Autowired
private ObjectFactory<MessageNotifier> messageNotifierObjectFactory;
@Autowired
private ObjectProvider<MessageNotifier> messageNotifierObjectProvider;
代碼如下:
從這段代碼也可以看出,最終注入的其實是DependencyObjectProvider
實現(xiàn)。
ObjectFactory也是用來做延遲注入的操作,跟@Lazy作用差不多,但是實現(xiàn)原理不一樣。
用上面的例子來說,注入ObjectFactory的時候并有創(chuàng)建MessageNotifier對象。
當(dāng)需要使用MessageNotifier的時候需要通過ObjectFactory的getObject方法獲取,此時才會真正創(chuàng)建MessageNotifier對象。
MessageNotifier messageNotifier = messageNotifierObjectFactory.getObject();
getObject實現(xiàn)如下
getObject
所以@Async注解導(dǎo)致的循環(huán)依賴異常不僅可以通過@Lazy注解解決,也可以通過注入ObjectFactory的方式解決。
同理,ObjectProvider也有延遲注入的功能,但是除了延遲注入之外,ObjectProvider額外提供了跟Optional安全注入的功能,這個功能ObjectFactory是沒有的。
上面的例子中,當(dāng)使用ObjectFactory的getObject方法時,如果Spring容器中不存在MessageNotifier對象,此時也會拋NoSuchBeanDefinitionException
異常。
但是ObjectProvider額外提供的getIfAvailable方法就支持獲取不存在的對象的功能,當(dāng)通過getIfAvailable獲取的對象不存在時,只會返回null,并不會出拋異常。
getIfAvailable方法
對比一下與getObject方法的實現(xiàn),就是在獲取對象的時候是否要求對象獲取的對象不是必須的,這樣獲取不到就不會拋異常了。
ObjectFactory和ObjectProvider在框架內(nèi)部中使用的還是比較多的。
就比如說,在MybatisPlus自動裝配的時候就大量使用ObjectProvider
并且泛型類型就是數(shù)組或者是集合,跟前面說的都對應(yīng)上了。
通過這種方式就可以安全的注入,當(dāng)Spring容器有這些對象的時候MybatisPlus就使用這些,沒有也不會報錯。
8、JSR-330 Provider
首先,來講一下什么是JSR-330。
JSR是Java Specification Requests的縮寫,是一種Java標(biāo)準(zhǔn)規(guī)范。
而330算是一個版本,除了330,聽到的比較多的還有250。
這個規(guī)范定義了一些IOC的注解,我們熟知的比如@Resource、@PostConstruct、@PreDestroy注解都是JSR-250中提出的。
一些IOC的框架會基于這個標(biāo)準(zhǔn)來實現(xiàn)這些接口的功能,比如Spring、Dagger2等IOC框架都實現(xiàn)了這些注解的功能。
所以,如果你不使用Spring框架,使用其它的IOC框架,那么@Resource、@PostConstruct、@PreDestroy注解都是可以生效的。
在JSR-330中,提出了javax.inject.Provider
這個接口
不過,想使用JSR-330這個接口,需要引入依賴
<dependency>
<groupId>javax.inject<span class="hljs-name"groupId>
<artifactId>javax.inject<span class="hljs-name"artifactId>
<version>1<span class="hljs-name"version>
<span class="hljs-name"dependency>
Spring也支持注入這個類型的接口
這個接口的功能跟前面提到的ObjectFactory功能是一樣的,也支持延遲注入的功能。
總結(jié)
到這Spring能夠注入的Bean的8種類型就講完了,其實這8種類型可以分為以下幾種功能:
- 單一注入,就是注入一個單一的對象
- 集合注入,可以注入數(shù)組或者集合
- 延遲注入,比如@Lazy、ObjectFactory、ObjectProvider、JSR-330 Provider
- 安全注入,不存在不會拋異常,比如Optional、ObjectProvider
這幾種方式并不是互斥的,比如說延遲注入也可以注入的是一個集合,前面舉的MyBaisPlus自動裝配時ObjectProvider的使用就是很好的例子。
同時雖然本文舉例的是@Autowird注解和字段注入的方式,但上面提到的注入的Bean類型跟使用注解和注入方式?jīng)]什么關(guān)系,@Resource注解,構(gòu)造器注入,setter注入都是一樣的。
-
接口
+關(guān)注
關(guān)注
33文章
8669瀏覽量
151540 -
源碼
+關(guān)注
關(guān)注
8文章
649瀏覽量
29317 -
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
14362
發(fā)布評論請先 登錄
相關(guān)推薦
評論