<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          最全的 Spring 依賴(lài)注入方式,你都會(huì)了嗎?

          共 8167字,需瀏覽 17分鐘

           ·

          2022-03-09 16:13

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)??

          3d973dcc6a52ea0b33c4a061a522c678.webp

          前言

          Spring 正如其名字,給開(kāi)發(fā)者帶來(lái)了春天,Spring 是為解決企業(yè)級(jí)應(yīng)用開(kāi)發(fā)的復(fù)雜性而設(shè)計(jì)的一款框架,其設(shè)計(jì)理念就是:簡(jiǎn)化開(kāi)發(fā)。

          Spring 框架中最核心思想就是:

          • IOC(控制反轉(zhuǎn)): 即轉(zhuǎn)移創(chuàng)建對(duì)象的控制權(quán),將創(chuàng)建對(duì)象的控制權(quán)從開(kāi)發(fā)者轉(zhuǎn)移到了 Spring 框架。
          • AOP(切面編程): 將公共行為(如記錄日志,權(quán)限校驗(yàn)等)封裝到可重用的模塊中,而使原本的模塊內(nèi)只需關(guān)注自身的個(gè)性化行為。

          本文,將主要介紹 Spring 中 IOC 的依賴(lài)注入,

          控制反轉(zhuǎn) IOC

          就 IOC 本身而言,其并不是什么新技術(shù),只是一種思想理念。IOC 的核心就是原先創(chuàng)建一個(gè)對(duì)象,我們需要自己直接通過(guò) new 來(lái)創(chuàng)建,而 IOC 就相當(dāng)于有人幫們創(chuàng)建好了對(duì)象,需要使用的時(shí)候直接去拿就行,IOC 主要有兩種實(shí)現(xiàn)方式:

          • DL(Dependency Lookup):依賴(lài)查找。

          這種就是說(shuō)容器幫我們創(chuàng)建好了對(duì)象,我們需要使用的時(shí)候自己再主動(dòng)去容器中查找,如:

          ApplicationContext?applicationContext?=?new?ClassPathXmlApplicationContext("/application-context.xml");
          Object?bean?=?applicationContext.getBean("object");
          • DI(Dependency Inject):依賴(lài)注入。

          依賴(lài)注入相比較依賴(lài)查找又是一種優(yōu)化,也就是我們不需要自己去查找,只需要告訴容器當(dāng)前需要注入的對(duì)象,容器就會(huì)自動(dòng)將創(chuàng)建好的對(duì)象進(jìn)行注入(賦值)。

          依賴(lài)注入 DI

          通過(guò) xml 的注入方式我們不做討論,在這里主要討論基于注解的注入方式,基于注解的常規(guī)注入方式通常有三種:

          • 基于屬性注入
          • 基于 setter 方法注入
          • 基于構(gòu)造器注入

          三種常規(guī)注入方式

          接下來(lái)就讓我們分別介紹一下三種常規(guī)的注入方式。

          屬性注入

          通過(guò)屬性注入的方式非常常用,這個(gè)應(yīng)該是大家比較熟悉的一種方式:

          @Service
          public?class?UserService?{
          ????@Autowired
          ????private?Wolf1Bean?wolf1Bean;//通過(guò)屬性注入
          }

          setter 方法注入

          除了通過(guò)屬性注入,通過(guò) setter 方法也可以實(shí)現(xiàn)注入:

          @Service
          public?class?UserService?{
          ????private?Wolf3Bean?wolf3Bean;
          ????
          ????@Autowired??//通過(guò)setter方法實(shí)現(xiàn)注入
          ????public?void?setWolf3Bean(Wolf3Bean?wolf3Bean)?{
          ????????this.wolf3Bean?=?wolf3Bean;
          ????}
          }

          構(gòu)造器注入

          當(dāng)兩個(gè)類(lèi)屬于強(qiáng)關(guān)聯(lián)時(shí),我們也可以通過(guò)構(gòu)造器的方式來(lái)實(shí)現(xiàn)注入:

          @Service
          public?class?UserService?{
          ??private?Wolf2Bean?wolf2Bean;
          ????
          ?????@Autowired?//通過(guò)構(gòu)造器注入
          ????public?UserService(Wolf2Bean?wolf2Bean)?{
          ????????this.wolf2Bean?=?wolf2Bean;
          ????}
          }

          接口注入

          在上面的三種常規(guī)注入方式中,假如我們想要注入一個(gè)接口,而當(dāng)前接口又有多個(gè)實(shí)現(xiàn)類(lèi),那么這時(shí)候就會(huì)報(bào)錯(cuò),因?yàn)?Spring 無(wú)法知道到底應(yīng)該注入哪一個(gè)實(shí)現(xiàn)類(lèi)。

          比如我們上面的三個(gè)類(lèi)全部實(shí)現(xiàn)同一個(gè)接口 IWolf,那么這時(shí)候直接使用常規(guī)的,不帶任何注解元數(shù)據(jù)的注入方式來(lái)注入接口 IWolf。

          @Autowired
          private?IWolf?iWolf;

          此時(shí)啟動(dòng)服務(wù)就會(huì)報(bào)錯(cuò):

          d8ac0131b9d3935724fe0d0858c8117c.webp圖片

          這個(gè)就是說(shuō)本來(lái)應(yīng)該注入一個(gè)類(lèi),但是 Spring 找到了三個(gè),所以沒(méi)法確認(rèn)到底應(yīng)該用哪一個(gè)。這個(gè)問(wèn)題如何解決呢?

          解決思路主要有以下 5 種:

          通過(guò)配置文件和 @ConditionalOnProperty 注解實(shí)現(xiàn)

          通過(guò) @ConditionalOnProperty 注解可以結(jié)合配置文件來(lái)實(shí)現(xiàn)唯一注入。下面示例就是說(shuō)如果配置文件中配置了 lonely.wolf=test1,那么就會(huì)將 Wolf1Bean 初始化到容器,此時(shí)因?yàn)槠渌麑?shí)現(xiàn)類(lèi)不滿(mǎn)足條件,所以不會(huì)被初始化到 IOC 容器,所以就可以正常注入接口:

          @Component
          @ConditionalOnProperty(name?=?"lonely.wolf",havingValue?=?"test1")
          public?class?Wolf1Bean?implements?IWolf{
          }

          當(dāng)然,這種配置方式,編譯器可能還是會(huì)提示有多個(gè) Bean,但是只要我們確保每個(gè)實(shí)現(xiàn)類(lèi)的條件不一致,就可以正常使用。

          通過(guò)其他 @Condition 條件注解

          除了上面的配置文件條件,還可以通過(guò)其他類(lèi)似的條件注解,如:

          • @ConditionalOnBean:當(dāng)存在某一個(gè) Bean 時(shí),初始化此類(lèi)到容器。
          • @ConditionalOnClass:當(dāng)存在某一個(gè)類(lèi)時(shí),初始化此類(lèi)的容器。
          • @ConditionalOnMissingBean:當(dāng)不存在某一個(gè) Bean 時(shí),初始化此類(lèi)到容器。
          • @ConditionalOnMissingClass:當(dāng)不存在某一個(gè)類(lèi)時(shí),初始化此類(lèi)到容器。

          類(lèi)似這種實(shí)現(xiàn)方式也可以非常靈活的實(shí)現(xiàn)動(dòng)態(tài)化配置。

          不過(guò)上面介紹的這些方法似乎每次都只能固定注入一個(gè)實(shí)現(xiàn)類(lèi),那么如果我們就是想多個(gè)類(lèi)同時(shí)注入,不同的場(chǎng)景可以動(dòng)態(tài)切換而又不需要重啟或者修改配置文件,又該如何實(shí)現(xiàn)呢?

          通過(guò) @Resource 注解動(dòng)態(tài)獲取

          如果不想手動(dòng)獲取,我們也可以通過(guò) @Resource 注解的形式動(dòng)態(tài)指定 BeanName 來(lái)獲?。?/p>

          @Component
          public?class?InterfaceInject?{
          ????@Resource(name?=?"wolf1Bean")
          ????private?IWolf?iWolf;
          }

          如上所示則只會(huì)注入 BeanName 為 wolf1Bean 的實(shí)現(xiàn)類(lèi)。

          通過(guò)集合注入

          除了指定 Bean 的方式注入,我們也可以通過(guò)集合的方式一次性注入接口的所有實(shí)現(xiàn)類(lèi):

          @Component
          public?class?InterfaceInject?{
          ????@Autowired
          ????List?list;

          ????@Autowired
          ????private?Map?map;
          }

          上面的兩種形式都會(huì)將 IWolf 中所有的實(shí)現(xiàn)類(lèi)注入集合中。如果使用的是 List 集合,那么我們可以取出來(lái)再通過(guò) instanceof 關(guān)鍵字來(lái)判定類(lèi)型;而通過(guò) Map 集合注入的話(huà),Spring 會(huì)將 Bean 的名稱(chēng)(默認(rèn)類(lèi)名首字母小寫(xiě))作為 key 來(lái)存儲(chǔ),這樣我們就可以在需要的時(shí)候動(dòng)態(tài)獲取自己想要的實(shí)現(xiàn)類(lèi)。

          @Primary 注解實(shí)現(xiàn)默認(rèn)注入

          除了上面的幾種方式,我們還可以在其中某一個(gè)實(shí)現(xiàn)類(lèi)上加上 @Primary 注解來(lái)表示當(dāng)有多個(gè) Bean 滿(mǎn)足條件時(shí),優(yōu)先注入當(dāng)前帶有 @Primary 注解的 Bean:

          @Component
          @Primary
          public?class?Wolf1Bean?implements?IWolf{
          }

          通過(guò)這種方式,Spring 就會(huì)默認(rèn)注入 wolf1Bean,而同時(shí)我們?nèi)匀豢梢酝ㄟ^(guò)上下文手動(dòng)獲取其他實(shí)現(xiàn)類(lèi),因?yàn)槠渌麑?shí)現(xiàn)類(lèi)也存在容器中。

          手動(dòng)獲取 Bean 的幾種方式

          在 Spring 項(xiàng)目中,手動(dòng)獲取 Bean 需要通過(guò) ApplicationContext 對(duì)象,這時(shí)候可以通過(guò)以下 5 種方式進(jìn)行獲?。?/p>

          直接注入

          最簡(jiǎn)單的一種方法就是通過(guò)直接注入的方式獲取 ApplicationContext 對(duì)象,然后就可以通過(guò) ApplicationContext 對(duì)象獲取 Bean :

          @Component
          public?class?InterfaceInject?{
          ????@Autowired
          ????private?ApplicationContext?applicationContext;//注入

          ????public?Object?getBean(){
          ????????return?applicationContext.getBean("wolf1Bean");//獲取bean
          ????}
          }

          通過(guò) ApplicationContextAware 接口獲取

          通過(guò)實(shí)現(xiàn) ApplicationContextAware 接口來(lái)獲取 ApplicationContext 對(duì)象,從而獲取 Bean。需要注意的是,實(shí)現(xiàn) ApplicationContextAware 接口的類(lèi)也需要加上注解,以便交給 Spring 統(tǒng)一管理(這種方式也是項(xiàng)目中使用比較多的一種方式):

          @Component
          public?class?SpringContextUtil?implements?ApplicationContextAware?{
          ????private?static?ApplicationContext?applicationContext?=?null;

          ????@Override
          ????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
          ????????this.applicationContext?=?applicationContext;
          ????}

          ????/**
          ?????*?通過(guò)名稱(chēng)獲取bean
          ?????*/
          ????public?static?T?getBeanByName(String?beanName){
          ????????return?(T)?applicationContext.getBean(beanName);
          ????}

          ????/**
          ?????*?通過(guò)類(lèi)型獲取bean
          ?????*/
          ????public?static?T?getBeanByType(Class?clazz){
          ????????return?(T)?applicationContext.getBean(clazz);
          ????}
          }

          封裝之后,我們就可以直接調(diào)用對(duì)應(yīng)的方法獲取 Bean 了:

          Wolf2Bean?wolf2Bean?=?SpringContextUtil.getBeanByName("wolf2Bean");
          Wolf3Bean?wolf3Bean?=?SpringContextUtil.getBeanByType(Wolf3Bean.class);

          通過(guò) ApplicationObjectSupport 和 WebApplicationObjectSupport 獲取

          這兩個(gè)對(duì)象中,WebApplicationObjectSupport 繼承了 ApplicationObjectSupport,所以并無(wú)實(shí)質(zhì)的區(qū)別。

          同樣的,下面這個(gè)工具類(lèi)也需要增加注解,以便交由 Spring 進(jìn)行統(tǒng)一管理:

          @Component
          public?class?SpringUtil?extends?/*WebApplicationObjectSupport*/?ApplicationObjectSupport?{
          ????private?static?ApplicationContext?applicationContext?=?null;

          ????public?static?T?getBean(String?beanName){
          ????????return?(T)?applicationContext.getBean(beanName);
          ????}

          ????@PostConstruct
          ????public?void?init(){
          ????????applicationContext?=?super.getApplicationContext();
          ????}
          }

          有了工具類(lèi),在方法中就可以直接調(diào)用了:

          @RestController
          @RequestMapping("/hello")
          @Qualifier
          public?class?HelloController?{
          ????@GetMapping("/bean3")
          ????public?Object?getBean3(){
          ????????Wolf1Bean?wolf1Bean?=?SpringUtil.getBean("wolf1Bean");
          ????????return?wolf1Bean.toString();
          ????}
          }

          通過(guò) HttpServletRequest 獲取

          通過(guò) HttpServletRequest 對(duì)象,再結(jié)合 Spring 自身提供的工具類(lèi) WebApplicationContextUtils 也可以獲取到 ApplicationContext 對(duì)象,而 HttpServletRequest 對(duì)象可以主動(dòng)獲?。ㄈ缦?getBean2 方法),也可以被動(dòng)獲?。ㄈ缦?getBean1 方法):

          @RestController
          @RequestMapping("/hello")
          @Qualifier
          public?class?HelloController?{

          ????@GetMapping("/bean1")
          ????public?Object?getBean1(HttpServletRequest?request){
          ????????//直接通過(guò)方法中的HttpServletRequest對(duì)象
          ????????ApplicationContext?applicationContext?=?WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
          ????????Wolf1Bean?wolf1Bean?=?(Wolf1Bean)applicationContext.getBean("wolf1Bean");

          ????????return?wolf1Bean.toString();
          ????}

          ????@GetMapping("/bean2")
          ????public?Object?getBean2(){
          ????????HttpServletRequest?request?=?((ServletRequestAttributes)?RequestContextHolder.getRequestAttributes()).getRequest();//手動(dòng)獲取request對(duì)象
          ????????ApplicationContext?applicationContext?=?WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());

          ????????Wolf2Bean?wolf2Bean?=?(Wolf2Bean)applicationContext.getBean("wolf2Bean");
          ????????return?wolf2Bean.toString();
          ????}
          }

          其他方式獲取

          當(dāng)然,除了上面提到的方法,我們也可以使用最開(kāi)始提到的 DL 中代碼示例去手動(dòng) new 一個(gè) ApplicationContext 對(duì)象,但是這樣就意味著重新初始化了一次,所以是不建議這么去做,但是在寫(xiě)單元測(cè)試的時(shí)候這種方式是比較適合的。

          談?wù)?@Autowrite 和 @Resource 以及 @Qualifier 注解的區(qū)別

          上面我們看到了,注入一個(gè) Bean 可以通過(guò) @Autowrite,也可以通過(guò) @Resource 注解來(lái)注入,這兩個(gè)注解有什么區(qū)別呢?

          • @Autowrite:通過(guò)類(lèi)型去注入,可以用于構(gòu)造器和參數(shù)注入。當(dāng)我們注入接口時(shí),其所有的實(shí)現(xiàn)類(lèi)都屬于同一個(gè)類(lèi)型,所以就沒(méi)辦法知道選擇哪一個(gè)實(shí)現(xiàn)類(lèi)來(lái)注入。
          • @Resource:默認(rèn)通過(guò)名字注入,不能用于構(gòu)造器和參數(shù)注入。如果通過(guò)名字找不到唯一的 Bean,則會(huì)通過(guò)類(lèi)型去查找。如下可以通過(guò)指定 name 或者 type 來(lái)確定唯一的實(shí)現(xiàn):
          @Resource(name?=?"wolf2Bean",type?=?Wolf2Bean.class)
          ?private?IWolf?iWolf;

          @Qualifier 注解是用來(lái)標(biāo)識(shí)合格者,當(dāng) @Autowrite @Qualifier 一起使用時(shí),就相當(dāng)于是通過(guò)名字來(lái)確定唯一:

          @Qualifier("wolf1Bean")
          @Autowired
          private?IWolf?iWolf;

          那可能有人就會(huì)說(shuō),我直接用 @Resource 就好了,何必用兩個(gè)注解結(jié)合那么麻煩,這么一說(shuō)似乎顯得 @Qualifier 注解有點(diǎn)多余?

          @Qualifier 注解是多余的嗎

          我們先看下面聲明 Bean 的場(chǎng)景,這里通過(guò)一個(gè)方法來(lái)聲明一個(gè) Bean (MyElement),而且方法中的參數(shù)又有 Wolf1Bean 對(duì)象,那么這時(shí)候 Spring 會(huì)幫我們自動(dòng)注入 Wolf1Bean:

          @Component
          public?class?InterfaceInject2?{
          ????@Bean
          ????public?MyElement?test(Wolf1Bean?wolf1Bean){
          ????????return?new?MyElement();
          ????}
          }

          然而如果說(shuō)我們把上面的代碼稍微改一下,把參數(shù)改成一個(gè)接口,而接口又有多個(gè)實(shí)現(xiàn)類(lèi),這時(shí)候就會(huì)報(bào)錯(cuò)了:

          @Component
          public?class?InterfaceInject2?{
          ????@Bean
          ????public?MyElement?test(IWolf?iWolf){//此時(shí)因?yàn)镮Wolf接口有多個(gè)實(shí)現(xiàn)類(lèi),會(huì)報(bào)錯(cuò)
          ????????return?new?MyElement();
          ????}
          }

          @Resource 注解又是不能用在參數(shù)中,所以這時(shí)候就需要使用 @Qualifier 注解來(lái)確認(rèn)唯一實(shí)現(xiàn)了(比如在配置多數(shù)據(jù)源的時(shí)候就經(jīng)常使用 @Qualifier 注解來(lái)實(shí)現(xiàn)):

          @Component
          public?class?InterfaceInject2?{
          ????@Bean
          ????public?MyElement?test(@Qualifier("wolf1Bean")?IWolf?iWolf){
          ????????return?new?MyElement();
          ????}
          }

          總結(jié)

          本文主要講述了如何在 Spring 中使用靈活的方式來(lái)實(shí)現(xiàn)各種場(chǎng)景的注入方式,并且著重介紹了當(dāng)一個(gè)接口有多個(gè)實(shí)現(xiàn)類(lèi)時(shí)應(yīng)該如何注入的問(wèn)題,最后也介紹了常用幾個(gè)注入注解的區(qū)別,通過(guò)本文,相信大家對(duì)如何使用 Spring 中的依賴(lài)注入會(huì)更加的熟悉。

          來(lái)源:blog.csdn.net/zwx900102/article/details/115023405

          1.?烏迪爾 ! 小哈花了一分鐘就給博客添加了頁(yè)面加載進(jìn)度條 !

          2.?SpringBoot服務(wù)監(jiān)控機(jī)制,總算整明白了!

          3.?俄羅斯、烏克蘭程序員薪資大曝光!

          4.?為什么 Redis 集群要使用反向代理? 看這篇就明白了!

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù)?Java?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          PS:因公眾號(hào)平臺(tái)更改了推送規(guī)則,如果不想錯(cuò)過(guò)內(nèi)容,記得讀完點(diǎn)一下在看,加個(gè)星標(biāo),這樣每次新文章推送才會(huì)第一時(shí)間出現(xiàn)在你的訂閱列表里。

          點(diǎn)“在看”支持小哈呀,謝謝啦??

          瀏覽 41
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  男人的天堂黄色 | 黄色a级三级毛片免费 | 色欲在线 | 亲子乱伦一区二区 | 日本親子亂子倫XXXX50路 |