<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>

          長文干貨丨一文搞懂IoC的依賴注入

          共 8308字,需瀏覽 17分鐘

           ·

          2021-01-26 14:49

          一、注解驅(qū)動IoC

          xml驅(qū)動的IoC容器使用的是ClassPathXmlApplicationContext讀取xml內(nèi)bean信息

          注解驅(qū)動的IoC容器使用的是AnnotationConfigApplicationContext讀取Java類中的bean信息

          1. AnnotationConfigApplicationContext 的注冊使用

          相比于xml文件作為驅(qū)動, 注解驅(qū)動需要指明配置類 一個配置類可以理解為"相當(dāng)于"一個xml ?配置類只需要在類上標(biāo)注注解 @Configuration

          @Configuration
          public?class?DemoConfiguration?{
          }

          在xml中聲明bean的方式

          <bean?id="person"?class="com.huodd.bean.Person">bean>

          在配置類中使用的是@Bean注解

          @Bean
          public?Person?person()?{
          ????return?new?Person();
          }

          說明: ?向IoC容器注冊一個類型為Persion,id為Person的Bean

          方法名表示的是bean的id 返回值表示的是注冊的bean的類型

          @Bean注解也可以顯示的聲明bean的id ?如 @Bean("person1")

          2. 注解IoC容器的初始化

          public?class?AnnotationConfigApplication?{
          ????public?static?void?main(String[]?args)?{
          ????????ApplicationContext?ctx?=?new?AnnotationConfigApplicationContext(DemoConfiguration.class);
          ????????Person?person?=?ctx.getBean(Person.class);
          ????????System.out.println(person);
          ????}
          }

          運(yùn)行后Person控制臺打印結(jié)果

          com.huodd.bean.Person@55536d9e

          3. 組件的注冊和掃描

          上述初始化時 ?我們在使用AnnotationConfigApplicationContext時傳遞了參數(shù) Class... componentClasses

          翻看AnnotationConfigApplicationContext的構(gòu)造方法可以發(fā)現(xiàn)還可以傳遞參數(shù)的參數(shù)類型還有 String... basePackages

          這里就涉及到組件的注冊和掃描

          這里可以思考一個問題, 如果我們要注冊的組件特別多, 那進(jìn)行編寫這些@Bean的時候代碼工作量也會特別多,這時候該如何解決呢?

          Spring 給我們提供了幾個注解,可以幫助我們快速注冊需要的組件, 這些注解被稱為模式注解(stereotype annotations)

          @Component

          @Component可以說是所有組件注冊的根源 在類上標(biāo)注 @Component 代表該類被注冊到IoC容器中作為一個Bean

          @Component
          public?class?Person?{
          }

          如果未指定 Bean 的名稱 默認(rèn)規(guī)則是 "類名稱首字母小寫" ?上面的bean名稱默認(rèn)會是 person

          如果要自定義bean的名稱 可以在@Component聲明value的值即可 ?如

          @Component("person1")
          public?class?Person?{
          }

          在xml中相當(dāng)于

          <bean?id="person1"?class="com.huodd.bean.Person"/>

          @ComponentScan

          這個時候 如果我們直接運(yùn)行啟動類 獲取Person的bean對象,會報錯NoSuchBeanDefinitionException 這是為什么呢?

          因為我們只是聲明了組件,而后直接啟動了IoC容器,這樣容器是感知不到有@Component存在的,

          解決方案1:

          我們需要在寫配置類時再額外標(biāo)注一個新的注解@ComponentScan

          目的是告訴IoC容器 我要掃描哪個包下面的帶有@Component注解的類

          @Configuration
          @ComponentScan("com.huodd.bean")
          public?class?DemoComponentScanConfiguration?{
          }

          注: 如果不指定掃描路徑, 則默認(rèn)掃描本類所在包及所有子包下帶有@Component的組件

          啟動類代碼如下:

          public?class?AnnotationConfigApplication?{
          ????public?static?void?main(String[]?args)?{
          ????????ApplicationContext?ctx?=?new?AnnotationConfigApplicationContext(DemoComponentScanConfiguration.class);
          ????????Person?person?=?ctx.getBean(Person.class);
          ????????System.out.println(person);
          ????}
          }

          解決方案2:

          這里也可以不寫@ComponentScan 而直接在AnnotationConfigApplicationContext方法參數(shù)內(nèi)傳入String類型的包掃描路徑 代碼如下

          public?class?AnnotationConfigApplication?{
          ????public?static?void?main(String[]?args)?{
          ????????ApplicationContext?ctx?=?new?AnnotationConfigApplicationContext("com.huodd.bean");
          ????????Person?person?=?ctx.getBean(Person.class);
          ????????System.out.println(person);
          ????}
          }

          PS: 組件掃描并非是注解驅(qū)動IoC所特有的, 其實(shí)在xml驅(qū)動的IoC模式下 同樣可以啟用組件掃描, 只需要在xml中聲明一個標(biāo)簽即可

          <context:component-scan?base-package="com.huodd.bean"/>

          這里需要注意下: 如需要掃描多個路徑,需要寫多個標(biāo)簽 也就是 一個標(biāo)簽只能聲明一個根包

          組件注冊的補(bǔ)充

          SpringFramework 提供了在進(jìn)行Web開發(fā)三層架構(gòu)時的擴(kuò)展注解: 分別為 @Controller@Service@Repository 小伙伴有沒有很熟悉?

          分別代表 表現(xiàn)層業(yè)務(wù)層持久層 ?這三個注解的作用與 @Component完全一樣 ?扒開源碼我們可以看到 ?底層在這三個注解類上又添加了 @Component

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Component
          public?@interface?Service?{
          }

          這樣 我們在進(jìn)行符合三層架構(gòu)的開發(fā)時 對于相應(yīng)的如 ServiceImpl等 就可以直接標(biāo)注 @Service 等注解了

          @Configuration

          @Configuration 底層也有標(biāo)注@Component

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Component
          public?@interface?Configuration?{?...?}

          由此可以說明,配置類不是向我們所想的那樣,只是單純的做一個配置而已, 它也會被視為 bean,也被注冊到IoC容器里面

          4. 注解驅(qū)動與xml驅(qū)動互相引用

          4.1 xml引用注解

          需開啟注解配置 再注冊相應(yīng)配置類


          <beans?xmlns="http://www.springframework.org/schema/beans"
          ???????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          ???????xmlns:context="http://www.springframework.org/schema/context"
          ???????xsi:schemaLocation="http://www.springframework.org/schema/beans
          ????????https://www.springframework.org/schema/beans/spring-beans.xsd?
          ????????http://www.springframework.org/schema/context?
          ????????https://www.springframework.org/schema/context/spring-context.xsd"
          >


          ????
          ????<context:annotation-config?/>
          ????
          ????<bean?class="com.huodd.config.AnnotationConfigConfiguration"/>
          beans>

          4.2 注解引用XMl

          需在配置類上標(biāo)注 @ImportResource 并聲明配置文件的路徑

          @Configuration
          @ImportResource("classpath:annotation/demo-beans.xml")
          public?class?ImportXmlAnnotationConfiguration?{?
          }

          二、IoC的依賴注入

          1.Setter屬性注入

          創(chuàng)建對象 將屬性值set進(jìn)去 之后返回對象

          @Bean
          public?Person?person()?{
          ????Person?person?=?new?Person();
          ????person.setId(1);
          ????person.setName("PoXing");
          ????person.setAge(18);
          ????return?person;
          }

          xml中的setter注入

          <bean?id="person"?class="com.huodd.bean.Person">
          ????<property?name="id"?value="1"/>
          ????<property?name="name"?value="PoXing"/>
          ????<property?name="age"?value="18"/>
          bean>

          2. 構(gòu)造器注入

          使用構(gòu)造器注入,需要在bean本身添加有參構(gòu)造方法, 如在Person中添加有參構(gòu)造方法如下

          public?Person(Integer?id,?String?name,?Integer?age)?{
          ????????this.id?=?id;
          ????????this.name?=?name;
          ????????this.age?=?age;
          }

          注解驅(qū)動中,我們創(chuàng)建bean的時候注入屬性時 ?就需要同時指定參數(shù)值

          @Bean
          public?Person?person()?{
          ????return?new?Person(1,?"PoXing",?18);
          }

          xml驅(qū)動中如下

          <bean?id="person"?class="com.huodd.bean.Person">
          ????
          ????<constructor-arg?index="0"?value="1"/>
          ????<constructor-arg?index="1"?value="PoXing"/>
          ????<constructor-arg?index="2"?value="18"/>
          bean>

          3. 注解式屬性注入

          這里先說明一下,為何會有注解式屬性值注入. 細(xì)心的小伙伴可能會發(fā)現(xiàn) 上面我們談到的 Setter屬性注入構(gòu)造器注入 好像在只能是在使用 @Bean注解的時候時候使用, 但是 如果是通過標(biāo)注 @Component注解的組件呢(像前面我們的Person類中標(biāo)注了@Component注解),怎么給它設(shè)定屬性值, 該節(jié)主要就是說一下這部分

          @Component 下的屬性注入

          這里我們使用Dog類做為演示(這里我悄悄的添加了@Component注解 自己嘗試的小伙伴要注意哦 否則會報錯的)

          @Component
          public?class?Dog?{
          ????private?Integer?id;
          ????private?String?name;
          ????private?Integer?age;

          ???...?省略?Getter、Setter
          ???...?省略?toString

          }

          這里要實(shí)現(xiàn)注解式屬性注入,可以直接在要注入的字段上標(biāo)注 @Value注解 如

          @Value("1")
          private?Integer?id;

          @Value("wangcai")
          private?String?name;

          @Value("3")
          private?Integer?age;

          啟動類代碼如下

          public?class?DiApplication?{
          ????public?static?void?main(String[]?args)?{
          ????????ApplicationContext?ctx?=?new?AnnotationConfigApplicationContext("com.huodd.bean");
          ????????Dog?dog?=?ctx.getBean(Dog.class);
          ????????System.out.println(dog);
          ????}
          }

          控制臺打印結(jié)果

          Dog{id=1,?name='wangcai',?age=3}

          外部配置文件(@PropertySource)

          這里主要是解決上面的@Value中注入 我們把屬性值直接固定寫死了,如果要修改 還要去Java代碼中去修改,很不符合開發(fā)規(guī)范,

          SpringFramework為我們擴(kuò)展了新的注解@PropertySource 主要用來導(dǎo)入外部配置文件

          1. 這里我們創(chuàng)建一個 dog.properties
          dog.id=1
          dog.name=wangcai
          dog.age=3
          1. 引入配置文件
          @PropertySource("classpath:di/dog.properties")
          @ComponentScan("com.huodd.bean")
          @Configuration
          public?class?DemoComponentScanConfiguration?{
          }
          1. Dog類中屬性注入 這里@Value需要配合占位符 來獲取properties配置文件中的內(nèi)容
          @Value("${dog.id}")
          private?Integer?id;

          @Value("${dog.name}")
          private?String?name;

          @Value("${dog.age}")
          private?Integer?age;
          1. 修改一下啟動類
          public?class?DiApplication?{
          ????public?static?void?main(String[]?args)?{
          ????????ApplicationContext?ctx?=?new?AnnotationConfigApplicationContext(DemoComponentScanConfiguration.class);
          ????????Dog?dog?=?ctx.getBean(Dog.class);
          ????????System.out.println(dog);
          ????}
          }

          控制臺打印結(jié)果如下

          Dog{id=1,?name='wangcai',?age=3}

          此時配置文件的屬性已經(jīng)注入成功

          4.自動注入

          在xml模式中有ref屬性 可以將一個bean注入到另外一個bean中, 注解模式中也同樣可以

          @Autowired

          給Dog的bean中注入 Person的Bean (即 ?給dog指定它的主人)

          方法1 → 在屬性上標(biāo)注

          @Component
          public?class?Dog?{
          ????//?......
          ????@Autowired
          ????private?Person?person;
          }

          方法2 → 使用構(gòu)造器注入方式

          @Component
          public?class?Dog?{
          ????//?......
          ????private?Person?person;

          ????@Autowired
          ????public?Dog(Person?person)?{
          ?????this.person?=?person;
          ????}
          }

          方法3 → 使用setter方法注入

          @Component
          public?class?Dog?{
          ????//?......
          ????private?Person?person;
          ????
          ????@Autowired
          ????public?void?setPerson(Person?person)?{
          ????????this.person?=?person;
          ????}
          }

          JSR250規(guī)范下的@Resource

          @Resource也是用來屬性注入的注解

          它與@Autowired的區(qū)別是:

          • @Autowired是按照類型注入
          • @Resource是按照屬性名(也就是bean的名稱)注入

          @Resource 注解相當(dāng)于標(biāo)注 @Autowired@Qualifier

          @Qualifier這里簡要說明下,為指定bean的名稱而存在,如果存在多個相同的bean,而bean的名稱不同,我們可以使用@Autowired 配置 @Qualifier注解

          如: 下面表示該Dog類注入的主人Bean是名稱為 xiaowang的, ?而當(dāng)前容器內(nèi)可能存在多個 主人bean對象 ?比如 xiaoli、xiaoming ....

          @Component
          public?class?Dog?{
          ????//?......
          ????@Autowired
          ????@Qualifier("xiaowang")
          ????private?Person?person;
          }

          下面如果使用@Resource 可以更方便些 代碼如下

          @Component
          public?class?Dog?{
          ????//?......
          ????@Resource(name="xiaowang")
          ????private?Person?person;
          }

          JSR330規(guī)范下的@Inject

          @Inject注解也是按照類型注入,與@Autowire的策略一樣, 不過如要使用@Inject 需要額外的導(dǎo)入依賴


          <dependency>
          ????<groupId>javax.injectgroupId>
          ????<artifactId>javax.injectartifactId>
          ????<version>1version>
          dependency>

          后面的使用方法就與SpringFramework 原生的 @Autowire + @Qualifier 相同了

          @Component
          public?class?Dog?{
          ????
          ????@Inject?//?等同于@Autowired
          ????@Named("xiaowang")?//?等同于@Qualifier
          ????private?Person?person;

          它與@Autowired的區(qū)別是:

          • @Autowired所在的包為 org.springframework.beans.factory.annotation.Autowired 即為 SpringFramework 提供的
          • @Inject所在的包為 javax.inject.Inject 屬于JSR的規(guī)范 也就是說如果不使用SpringFramework時可以使用該注解

          5. 復(fù)雜類型注入

          Array注入

          <property?name="names">
          ????<array>
          ????????<value>PoXingvalue>
          ????????<value>LaoWangvalue>
          ????array>
          property>

          List注入

          <property?name="tels">
          ????<list>
          ????????<value>13000000000value>
          ????????<value>13000000001value>
          ????list>
          property>

          Set注入-


          <bean?id="wangcai"?class="com.huodd.bean.ext.Dog"/>
          ---

          <property?name="dogs">
          ????<set>
          ????????<bean?class="com.huodd.bean.Dog"/>
          ????????<ref?bean="wangcai"/>
          ????set>
          property>

          Map注入

          <property?name="homesMap">
          ????<map>
          ????????<entry?key="1"?value="main">
          ?????????<ref?bean="myHome1"?/>
          ????????entry>
          ????????<entry?key="2"?value="other">
          ?????????????<ref?bean="myHome2"?/>
          ????????entry>
          ????map>
          property>

          Properties注入

          <property?name="props">
          ????<props>
          ????????<prop?key="sex">prop>
          ????????<prop?key="age">18prop>
          ????props>
          property>

          面試題

          1.@Autowired注入原理是什么?

          1. 先拿屬性對應(yīng)的類型,去IoC容器中找相應(yīng)的Bean
          2. 如果沒有找到 直接拋出NoUniqueBeanDefinitionException異常
          3. 如果找到一個 直接返回
          4. 如果找到多個相同類型的bean 再拿屬性名去與這多個bean的id進(jìn)行對比
          5. 如果有多個或者沒有 則會拋出NoUniqueBeanDefinitionException異常
          6. 如果只有一個 直接返回

          2.依賴注入的方式有哪些,都有什么區(qū)別

          注入方式被注入對象是否可改變是否依賴IOC框架的API使用場景
          構(gòu)造器注入不可變不可變的固定注入
          參數(shù)注入不可變注解配置類中 @Bean方法注冊 bean
          屬性注入(注解式屬性注入)不可變是(只能通過標(biāo)注注解來侵入式注入)通常用于不可變的固定注入
          setter注入可變可選屬性的注入

          3.自動注入的注解對比

          注解注入方式是否支持@Primary來源Bean不存在時處理
          @Autowired根據(jù)類型注入SpringFramework原生注解可指定required=false來避免注入失敗
          @Resource根據(jù)名稱注入JSR250規(guī)范容器中不存在指定Bean會拋出異常
          @Inject根據(jù)類型注入JSR330規(guī)范 ( 需要導(dǎo)jar包 )容器中不存在指定Bean會拋出異常

          @Qualifier :如果被標(biāo)注的成員/方法在根據(jù)類型注入時發(fā)現(xiàn)有多個相同類型的 Bean ,則會根據(jù)該注解聲明的 name 尋找特定的 bean

          @Primary :如果有多個相同類型的 Bean 同時注冊到 IOC 容器中,使用 “根據(jù)類型注入” 的注解時會注入標(biāo)注 @Primary 注解的 bean 即默認(rèn)策略

          4.使用依賴注入有什么優(yōu)缺點(diǎn)

          • 依賴注入作為 IOC 的實(shí)現(xiàn)方式之一,目的就是解耦,我們不需要直接去 new 那些依賴的類對象就可以直接從容器中去取來使用, 如果組件存在多級依賴,依賴注入可以將這些依賴的關(guān)系簡化。
          • 依賴對象的可配置:通過 xml 或者注解聲明,可以指定和調(diào)整組件注入的對象,借助 Java 的多態(tài)特性,可以不需要大批量的修改就完成依賴注入的對象替換



          一碼不掃,

          何以掃天下?



          瀏覽 35
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  免费又色又爽又黄的成人用品 | 免费观看黄色网 | 骚货操逼视频porn | h文在线播放 | 夜夜噜噜噜 |