0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Spring依賴(lài)注入Bean類(lèi)型的8種情況

jf_78858299 ? 來(lái)源:JAVA旭陽(yáng) ? 作者:JAVA旭陽(yáng) ? 2023-05-11 10:53 ? 次閱讀

今天來(lái)講的一個(gè)你可能不曾注意的小東西,那就是Spring依賴(lài)注入支持注入Bean的類(lèi)型,這個(gè)小東西可能看似沒(méi)有用但是實(shí)際又有點(diǎn)小用。

其實(shí)本來(lái)這周沒(méi)打算寫(xiě)文章,但是突然之間就想到了之前有個(gè)妹子問(wèn)過(guò)這個(gè)問(wèn)題,并且網(wǎng)上這塊東西說(shuō)的也不多,所以就趕在周末的末尾匆匆寫(xiě)下了這篇文章。

這東西本身也沒(méi)有什么復(fù)雜的原理,所以本文也并沒(méi)有什么太多深入剖析源碼的東西。

1、普通對(duì)象

這沒(méi)什么好說(shuō)的,大家都這么用的,比如需要用到UserService,直接@Autowired就可以了。

@Autowired
private UserService userService;

2、Collection及其子接口

除了支持注入一個(gè)單一的對(duì)象之外,@Autowired還支持注入一個(gè)Collection對(duì)象。

比如說(shuō),現(xiàn)在有個(gè)消息通知的接口MessageNotifier。

這種接口一般都會(huì)有不同的實(shí)現(xiàn),比如說(shuō)通過(guò)郵件通知,或者app,短信等等,所以就有多種實(shí)現(xiàn),此時(shí)如果需要注入MessageNotifier,就可以使用注入Collection的方式,比如

@Autowired
private List<MessageNotifier> messageNotifiers;

不過(guò)這種方式有個(gè)規(guī)定,那就是注入的類(lèi)型必須是Collection及其子接口,如果你直接注入一個(gè)ArrayList,那么此時(shí)是不支持的。

圖片

3、數(shù)組

同理,@Autowired可實(shí)現(xiàn)了注入一個(gè)數(shù)組的功能。

@Autowired
private MessageNotifier[] messageNotifiers;

代碼如下:

圖片

4、Map

同樣的,@Autowired還可以注入一個(gè)Map。

@Autowired
private Map<String, MessageNotifier> messageNotifierMap;

此時(shí)注入的map,key的類(lèi)型就是bean的名稱(chēng),這種方式可以配合策略模式使用。

不過(guò),這種方式只支持注入的是Map接口,不支持子類(lèi)型接口,代碼如下。

圖片

5、@Lazy

當(dāng)一個(gè)注入的字段加了@Lazy注解之后,那么此時(shí)就代表這個(gè)字段是延遲注入。

@Autowired
@Lazy
private MessageNotifier messageNotifier;

延遲注入并不是不注入,而是注入目標(biāo)對(duì)象類(lèi)型的代理對(duì)象,真正的目標(biāo)是當(dāng)需要用到的時(shí)候在創(chuàng)建。

圖片

如圖所示,當(dāng)注入的MessageNotifier時(shí)加了@Lazy注解,那么此時(shí)注入的其實(shí)是MessageNotifier的代理對(duì)象,而真正的MessageNotifier對(duì)象并沒(méi)有創(chuàng)建,圖中代理對(duì)象我稱(chēng)為MessageNotifierProxy。

由于注入的是對(duì)象是代理對(duì)象MessageNotifierProxy,那么真正被使用的就是MessageNotifierProxy,一旦調(diào)用了MessageNotifierProxy的方法,此時(shí)MessageNotifierProxy會(huì)去Spring容器中查找真正的MessageNotifier對(duì)象,然后再調(diào)用MessageNotifier對(duì)象的方法。

代碼如下:

圖片

這就是@Lazy延遲注入的原理。并不是不注入,而是注入一個(gè)代理對(duì)象,可以理解為一個(gè)占位符,一個(gè)空殼子,先占著位置,等用到這個(gè)殼子的時(shí)候,這個(gè)殼子會(huì)去查找到真正的對(duì)象,調(diào)用真正對(duì)象的方法。

@Lazy的一個(gè)使用場(chǎng)景就是用來(lái)解決Spring無(wú)法處理的循環(huán)依賴(lài)場(chǎng)景,比如使用了@Async注解的循環(huán)依賴(lài)的場(chǎng)景,不了解的小伙伴可以看一下 @Async注解的坑,小心 這篇文章

6、Optional

Optional是JDK1.8提供的一個(gè)api,可以?xún)?yōu)雅的解決判空的問(wèn)題。

@Autowired也支持了注入Optional類(lèi)型。

@Autowired
private Optional<MessageNotifier> messageNotifier;

代碼如下:

圖片

注入Optional這種方式可以解決注入的對(duì)象不存在的導(dǎo)致異常問(wèn)題,也就是安全注入。

比如說(shuō),MessageNotifier這個(gè)對(duì)象Spring容器中并沒(méi)有,如果直接注入,此時(shí)會(huì)拋NoSuchBeanDefinitionException異常

圖片

而直接通過(guò)注入Optional的方式就可以解決這個(gè)問(wèn)題。

除了通過(guò)Optional的方式之外,也可以直接把@Autowired的required的屬性設(shè)置為false來(lái)解決注入對(duì)象不存在的問(wèn)題。

那Optional存在的作用是啥?

其實(shí)Optional的作用僅僅是不用寫(xiě)為空的判斷,這也是Optional這個(gè)類(lèi)的作用作用,除了這個(gè),跟直接@Autowired對(duì)象并沒(méi)有其它區(qū)別。

注入Optional這種方式其實(shí)用的不多,在我的映像中,我在源碼中幾乎沒(méi)有看見(jiàn)這種注入方式。

7、ObjectFactory和ObjectProvider

ObjectFactory和ObjectProvider是Spring提供的兩接口

圖片

ObjectFactory

ObjectProvider繼承了ObjectFactory

圖片

ObjectProvider

@Autowired也可以直接注入這兩個(gè)接口。

@Autowired
private ObjectFactory<MessageNotifier> messageNotifierObjectFactory;

@Autowired
private ObjectProvider<MessageNotifier> messageNotifierObjectProvider;

代碼如下:

圖片

從這段代碼也可以看出,最終注入的其實(shí)是DependencyObjectProvider實(shí)現(xiàn)。

ObjectFactory也是用來(lái)做延遲注入的操作,跟@Lazy作用差不多,但是實(shí)現(xiàn)原理不一樣。

用上面的例子來(lái)說(shuō),注入ObjectFactory的時(shí)候并有創(chuàng)建MessageNotifier對(duì)象。

當(dāng)需要使用MessageNotifier的時(shí)候需要通過(guò)ObjectFactory的getObject方法獲取,此時(shí)才會(huì)真正創(chuàng)建MessageNotifier對(duì)象。

MessageNotifier messageNotifier = messageNotifierObjectFactory.getObject();

getObject實(shí)現(xiàn)如下

圖片

getObject

所以@Async注解導(dǎo)致的循環(huán)依賴(lài)異常不僅可以通過(guò)@Lazy注解解決,也可以通過(guò)注入ObjectFactory的方式解決。

同理,ObjectProvider也有延遲注入的功能,但是除了延遲注入之外,ObjectProvider額外提供了跟Optional安全注入的功能,這個(gè)功能ObjectFactory是沒(méi)有的。

上面的例子中,當(dāng)使用ObjectFactory的getObject方法時(shí),如果Spring容器中不存在MessageNotifier對(duì)象,此時(shí)也會(huì)拋NoSuchBeanDefinitionException異常。

但是ObjectProvider額外提供的getIfAvailable方法就支持獲取不存在的對(duì)象的功能,當(dāng)通過(guò)getIfAvailable獲取的對(duì)象不存在時(shí),只會(huì)返回null,并不會(huì)出拋異常。

圖片

getIfAvailable方法

對(duì)比一下與getObject方法的實(shí)現(xiàn),就是在獲取對(duì)象的時(shí)候是否要求對(duì)象獲取的對(duì)象不是必須的,這樣獲取不到就不會(huì)拋異常了。

ObjectFactory和ObjectProvider在框架內(nèi)部中使用的還是比較多的。

就比如說(shuō),在MybatisPlus自動(dòng)裝配的時(shí)候就大量使用ObjectProvider

圖片

并且泛型類(lèi)型就是數(shù)組或者是集合,跟前面說(shuō)的都對(duì)應(yīng)上了。

通過(guò)這種方式就可以安全的注入,當(dāng)Spring容器有這些對(duì)象的時(shí)候MybatisPlus就使用這些,沒(méi)有也不會(huì)報(bào)錯(cuò)。

8、JSR-330 Provider

首先,來(lái)講一下什么是JSR-330。

JSR是Java Specification Requests的縮寫(xiě),是一種Java標(biāo)準(zhǔn)規(guī)范。

而330算是一個(gè)版本,除了330,聽(tīng)到的比較多的還有250。

這個(gè)規(guī)范定義了一些IOC的注解,我們熟知的比如@Resource、@PostConstruct、@PreDestroy注解都是JSR-250中提出的。

一些IOC的框架會(huì)基于這個(gè)標(biāo)準(zhǔn)來(lái)實(shí)現(xiàn)這些接口的功能,比如Spring、Dagger2等IOC框架都實(shí)現(xiàn)了這些注解的功能。

所以,如果你不使用Spring框架,使用其它的IOC框架,那么@Resource、@PostConstruct、@PreDestroy注解都是可以生效的。

在JSR-330中,提出了javax.inject.Provider這個(gè)接口

圖片

不過(guò),想使用JSR-330這個(gè)接口,需要引入依賴(lài)

<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也支持注入這個(gè)類(lèi)型的接口

圖片

這個(gè)接口的功能跟前面提到的ObjectFactory功能是一樣的,也支持延遲注入的功能。

總結(jié)

到這Spring能夠注入的Bean的8種類(lèi)型就講完了,其實(shí)這8種類(lèi)型可以分為以下幾種功能:

  • 單一注入,就是注入一個(gè)單一的對(duì)象
  • 集合注入,可以注入數(shù)組或者集合
  • 延遲注入,比如@Lazy、ObjectFactory、ObjectProvider、JSR-330 Provider
  • 安全注入,不存在不會(huì)拋異常,比如Optional、ObjectProvider

這幾種方式并不是互斥的,比如說(shuō)延遲注入也可以注入的是一個(gè)集合,前面舉的MyBaisPlus自動(dòng)裝配時(shí)ObjectProvider的使用就是很好的例子。

同時(shí)雖然本文舉例的是@Autowird注解和字段注入的方式,但上面提到的注入的Bean類(lèi)型跟使用注解和注入方式?jīng)]什么關(guān)系,@Resource注解,構(gòu)造器注入,setter注入都是一樣的。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8448

    瀏覽量

    150724
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    632

    瀏覽量

    29110
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    338

    瀏覽量

    14295
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    java spring教程

    Spring核心概念介紹控制反轉(zhuǎn)(IOC)依賴(lài)注入(DI)集合對(duì)象注入Bean的管理BeanFactoryApplicationConte
    發(fā)表于 09-11 11:09

    什么是java spring

    。在SSH項(xiàng)目中管理事務(wù)以及對(duì)象的注入Spring是非侵入式的:基于Spring開(kāi)發(fā)的系統(tǒng)中的對(duì)象一般不依賴(lài)Spring的類(lèi)。組成
    發(fā)表于 09-11 11:16

    spring實(shí)例

    ;UTF-8"?><!DOCTYPE beans PUBLIC"-//SPRING//DTD BEAN//EN""http://www.springframework.org
    發(fā)表于 09-11 11:22

    怎么閱讀Spring源碼

    注入)。如果其中有一個(gè)類(lèi)container里沒(méi)找到,則拋出異常,比如常見(jiàn)的spring無(wú)法找到該類(lèi)定義,無(wú)法wire的異常。還有就是嵌套bean則用了一下遞歸,container會(huì)放到
    發(fā)表于 05-04 15:21

    三大框架之Spring

    ;出現(xiàn)了Spring,可以自動(dòng)創(chuàng)建需要被調(diào)用的對(duì)象以及進(jìn)行屬性注入,也可以維護(hù)這些bean(具體的java類(lèi))之間的關(guān)系;
    發(fā)表于 05-27 07:21

    Spring工作原理

    依賴(lài)關(guān)系核心:bean工廠(chǎng);在Spring中,bean工廠(chǎng)創(chuàng)建的各個(gè)實(shí)例稱(chēng)作bean二.AOP(Aspect-Oriented Progr
    發(fā)表于 07-10 07:41

    spring教程ppt

    主要內(nèi)容Spring 概述Spring 整體結(jié)構(gòu)Spring實(shí)例Spring核心概念介紹控制反轉(zhuǎn)(IOC)依賴(lài)
    發(fā)表于 09-11 11:00 ?138次下載
    <b class='flag-5'>spring</b>教程ppt

    解析加載及實(shí)例化Bean的順序(零配置)

    作者丨低調(diào)的JVM 來(lái)自丨CSDN https://blog.csdn.net/qq_27529917/article/details/79329809 在使用Spring時(shí),Bean之間會(huì)有些依賴(lài)
    的頭像 發(fā)表于 08-04 16:08 ?1291次閱讀

    Spring開(kāi)發(fā)過(guò)程中依賴(lài)注入的幾個(gè)知識(shí)點(diǎn)

    轉(zhuǎn)自丨h(huán)ttps://juejin.cn/post/6844904056230690824 本章的內(nèi)容主要是想探討我們?cè)谶M(jìn)行 Spring 開(kāi)發(fā)過(guò)程當(dāng)中,關(guān)于依賴(lài)注入的幾個(gè)知識(shí)點(diǎn)。感興趣的讀者可以
    的頭像 發(fā)表于 08-27 09:18 ?1618次閱讀

    bean放入Spring容器中有哪些方式

    bean放入Spring容器中有哪些方式?
    的頭像 發(fā)表于 09-19 15:25 ?685次閱讀

    從源碼層面深度剖析Spring循環(huán)依賴(lài)

    參考圖中 spring 解決循環(huán)依賴(lài) 的過(guò)程可知,spring 利用三級(jí)緩中的 objectFactory 生成并返回一個(gè) early 對(duì)象,提前暴露這個(gè) early 地址,供其他對(duì)象依賴(lài)
    的頭像 發(fā)表于 12-22 10:34 ?506次閱讀

    Spring Dependency Inject與Bean Scops注解

    DependsOn`注解可以配置Spring IoC容器在初始化一個(gè)Bean之前,先初始化其他的Bean對(duì)象。下面是此注解使用示例代碼:
    的頭像 發(fā)表于 04-07 11:35 ?648次閱讀
    <b class='flag-5'>Spring</b> Dependency Inject與<b class='flag-5'>Bean</b> Scops注解

    Spring容器原始Bean是如何創(chuàng)建的?Spring源碼中方法的執(zhí)行順序

    這個(gè)話(huà)題其實(shí)非常龐大,我本來(lái)想從 getBean 方法講起,但一想這樣講完估計(jì)很多小伙伴就懵了,所以我們還是一步一步來(lái),今天我主要是想和小伙伴們講講 Spring 容器創(chuàng)建 Bean 最最核心的 createBeanInstance 方法,這個(gè)方法專(zhuān)門(mén)用來(lái)創(chuàng)建一個(gè)原始
    的頭像 發(fā)表于 08-04 10:12 ?567次閱讀
    <b class='flag-5'>Spring</b>容器原始<b class='flag-5'>Bean</b>是如何創(chuàng)建的?<b class='flag-5'>Spring</b>源碼中方法的執(zhí)行順序

    Spring依賴(lài)注入的方式

    Spring 是一個(gè)開(kāi)源的輕量級(jí)框架,可以用于構(gòu)建企業(yè)級(jí)應(yīng)用程序。其最重要的特性之一是依賴(lài)注入(Dependency Injection,DI),這是一設(shè)計(jì)模式,它可以幫助我們解耦代
    的頭像 發(fā)表于 11-22 15:12 ?444次閱讀

    Spring依賴(lài)注入的四方式

    Spring框架中,依賴(lài)注入是一核心的概念和機(jī)制。通過(guò)依賴(lài)注入,我們可以讓對(duì)象之間的
    的頭像 發(fā)表于 12-03 15:11 ?1826次閱讀