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

          給學(xué)妹看的SpringIOC 面試題(下)

          共 12869字,需瀏覽 26分鐘

           ·

          2021-08-27 11:45

          之前上篇跟學(xué)弟學(xué)妹講了一下SpringIOC的啟動(dòng)流程,今天接著給學(xué)妹聊聊DI—Dependency Injection(依賴注入)

          給學(xué)妹看的SpringIOC 面試題(上)

          什么是依賴注入?

          依賴注入(DI)是一個(gè)過程,通過該過程,對(duì)象只能通過構(gòu)造函數(shù)參數(shù),工廠方法的參數(shù)或在構(gòu)造或創(chuàng)建對(duì)象實(shí)例后在對(duì)象實(shí)例上設(shè)置的屬性來定義其依賴關(guān)系(即,與它們一起工作的其他對(duì)象)。從工廠方法返回。

          然后,容器在創(chuàng)建 bean 時(shí)注入那些依賴項(xiàng)。從根本上講,此過程是通過使用類的直接構(gòu)造或服務(wù)定位器模式來自己控制其依賴關(guān)系的實(shí)例化或位置的 Bean 本身的逆過程(因此稱為 Control Inversion)。

          使用 DI 原理,代碼更簡(jiǎn)潔,當(dāng)為對(duì)象提供依賴項(xiàng)時(shí),去耦會(huì)更有效。該對(duì)象不查找其依賴項(xiàng),也不知道依賴項(xiàng)的位置或類。結(jié)果,您的類變得更易于測(cè)試,尤其是當(dāng)依賴項(xiàng)依賴于接口或抽象 Base Class 時(shí),它們?cè)试S在單元測(cè)試中使用存根或模擬實(shí)現(xiàn)。

          -----------以上解釋來源Spring官方文檔

          說白了依賴注入只是把bean添加到IOC容器的一種方式。

          從依賴注入的方式來說整體可以分為兩大類來處理,一種是手動(dòng)方式,一種是自動(dòng)方式。

          • 手動(dòng)方式:
            • XML 資源配置元信息(比較常見)
            • Java 注解配置元信息 (比較常見)
            • API 配置元信息(不太常用)
          • 自動(dòng)方式:
            • Autowiring

          依賴注入的方式有上面的兩種,但是也可按注入的類型來區(qū)分:

          • Setter注入
          • 構(gòu)造器注入
          • 接口注入
          • 方法注入

          聊到依賴注入那么首先需要先聊聊 Autowiring Modes自動(dòng)綁定模式

          Spring的官方文檔中對(duì)Autowiring Modes解釋是:

          Spring 容器可以自動(dòng)裝配協(xié)作 bean 之間的關(guān)系。通過檢查 ApplicationContext 的內(nèi)容,您可以讓 Spring 自動(dòng)為您的 bean 解析協(xié)作者(其他 bean)

          同時(shí)也提出了4種自動(dòng)裝配模式

          • no:(默認(rèn))無自動(dòng)裝配。Bean 引用必須由ref元素定義。對(duì)于大型部署,建議不要更改默認(rèn)設(shè)置,因?yàn)槊鞔_指定協(xié)作者可以提供更好的控制和清晰度。在某種程度上,它記錄了系統(tǒng)的結(jié)構(gòu)。
          • byName:按屬性名稱自動(dòng)布線。Spring 尋找與需要自動(dòng)裝配的屬性同名的 bean。例如,如果一個(gè) bean 定義被設(shè)置為按名稱自動(dòng)裝配,并且包含一個(gè)master屬性(即,它具有setMaster(..)方法),那么 Spring 將查找一個(gè)名為master的 bean 定義并使用它來設(shè)置屬性。
          • byType:如果容器中恰好存在一個(gè)該屬性類型的 bean,則使該屬性自動(dòng)裝配。如果存在多個(gè)錯(cuò)誤,則會(huì)引發(fā)致命異常,這表明您可能不對(duì)該 bean 使用byType自動(dòng)裝配。如果沒有匹配的 bean,則什么也不會(huì)發(fā)生(未設(shè)置該屬性)。
          • constructor:類似于byType,但適用于構(gòu)造函數(shù)參數(shù)。如果容器中不存在構(gòu)造函數(shù)參數(shù)類型的一個(gè) bean,則將引發(fā)致命錯(cuò)誤。

          雖然官方文檔提出了Autowiring自動(dòng)綁定方式,但是在我們的真實(shí)的業(yè)務(wù)場(chǎng)景中,相對(duì)來說是用的比較少的,因?yàn)樗幸欢ǖ木窒扌裕襍pring官方文檔中也列出了其中的不足點(diǎn)。

          自動(dòng)裝配的局限性和缺點(diǎn)(官方文檔鏈接)
          • propertyconstructor-arg設(shè)置中的顯式依賴項(xiàng)始終會(huì)覆蓋自動(dòng)裝配。您不能自動(dòng)連接簡(jiǎn)單屬性,例如基元,StringsClasses(以及此類簡(jiǎn)單屬性的數(shù)組)。此限制是設(shè)計(jì)使然  PS:針對(duì)這種情況可以通過另外的一種方式@value等進(jìn)行轉(zhuǎn)化來處理這個(gè)場(chǎng)景。
          • 自動(dòng)裝配不如顯式接線精確。盡管如前所述,Spring 還是小心避免在可能產(chǎn)生意外結(jié)果的模棱兩可的情況下進(jìn)行猜測(cè)。SpringManagement 的對(duì)象之間的關(guān)系不再明確記錄。
          • 容器內(nèi)的多個(gè) bean 定義可能與要自動(dòng)裝配的 setter 方法或構(gòu)造函數(shù)參數(shù)指定的類型匹配。對(duì)于數(shù)組,集合或Map實(shí)例,這不一定是問題。但是,對(duì)于需要單個(gè)值的依賴項(xiàng),不會(huì)任意解決此歧義。如果沒有唯一的 bean 定義可用,則引發(fā)異常。

          說完這么多文檔的基礎(chǔ)知識(shí),那么接下來就是開始demo測(cè)試環(huán)節(jié),來加深理解一下上面的說的那么多到底是個(gè)啥。

          Setter

          先從注入的類型先分析怎么樣的一種方式叫Setter方式注入

          /構(gòu)建一個(gè)測(cè)試Service
          public class SetterServiceInjection {
              public void testMethod(String param) {
                  System.out.println(param);
              }
          }

          public class SetterServiceInjectionTest {
              private SetterServiceInjection setterServiceInjection;

              // Setter方式注入
              public void setSetterServiceInjection(SetterServiceInjection setterServiceInjection) {
                  this.setterServiceInjection = setterServiceInjection;
              }

              public void testMethod(){
                  setterServiceInjection.testMethod("Setter方式注入");
              }

            
            // 測(cè)試啟動(dòng)demo
              public static void main(String[] args) {
                  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
                  //獲取IOC容器中的bean
                  SetterServiceInjectionTest serviceInjectionTest = (SetterServiceInjectionTest) applicationContext.getBean("setterServiceInjectionTest");
                  serviceInjectionTest.testMethod();
                 // 結(jié)果打?。?/span>
                 // Setter方式注入
                 
              }
          }

          xml文件配置

          <bean id="setterServiceInjection" class="com.ao.bing.demo.spring.ioc.SetterServiceInjection"/>

          <!--Setter方式-->
          <bean id="setterServiceInjectionTest" class="com.ao.bing.demo.spring.ioc.SetterServiceInjectionTest">
           <property name="setterServiceInjection" ref="setterServiceInjection"/>
          </bean>

          上面是很常見的一種注入方式,而且這種方式常見于去寫一些配置文件、插件二方包、或者注入數(shù)據(jù)源信息等。

          當(dāng)然Setter不是僅僅只是這一種使用方式,還可以注入對(duì)象,或者說注入一些集合信息等等。

          構(gòu)造器注入

          在代碼的實(shí)現(xiàn)上面構(gòu)造器和Setter方式是很相似的。還是按照上面的代碼改造一下如下所示

            private final SetterServiceInjection setterServiceInjection;

              // Setter方式注入
          //    public void setSetterServiceInjection(SetterServiceInjection setterServiceInjection) {
          //        this.setterServiceInjection = setterServiceInjection;
          //    }

              public void testMethod(){
                  setterServiceInjection.testMethod("構(gòu)造器方式注入");
              }

              //構(gòu)造器注入
              public SetterServiceInjectionTest(SetterServiceInjection setterServiceInjection){
                  this.setterServiceInjection = setterServiceInjection;
              }
              <context:component-scan base-package="com.ao.bing.demo"/>

              <bean id="setterServiceInjection" class="com.ao.bing.demo.spring.ioc.SetterServiceInjection"/>

              <!--Setter方式-->
          <!--    <bean id="setterServiceInjectionTest" class="com.ao.bing.demo.spring.ioc.SetterServiceInjectionTest">-->
          <!--        <property name="setterServiceInjection" ref="setterServiceInjection"/>-->
          <!--    </bean>-->

              <bean id="setterServiceInjectionTest" class="com.ao.bing.demo.spring.ioc.SetterServiceInjectionTest">
                  <constructor-arg index="0" ref="setterServiceInjection"/>
              </bean>

          既然兩個(gè)代碼這么相似,為什么Spring官方還需要推薦使用這種方式呢?和Setter方式區(qū)別又是啥?

          • 推薦原因:從定義的屬性來說添加了final修飾說明我們注入的依賴不能再變動(dòng)。其次從XML的配置bean的屬性來說,當(dāng)需要實(shí)例化setterServiceInjectionTest這個(gè)類的時(shí)候已經(jīng)實(shí)現(xiàn)了有參構(gòu)造函數(shù),那么就不會(huì)再使用默認(rèn)的構(gòu)造函數(shù),同時(shí)針對(duì)傳入的參數(shù)需要確保有這種類型的值,否則就會(huì)報(bào)錯(cuò),所以這樣就保證了依賴不會(huì)為空最后因?yàn)闃?gòu)造器傳入的參數(shù)是確定有值的,那就意味著構(gòu)造屬性是已經(jīng)完全初始化的狀態(tài),所以這也就避免了后面需要分析的循環(huán)依賴的問題。
          • 區(qū)別
            • 在Setter注入,可以將依賴項(xiàng)部分注入,構(gòu)造方法注入不能部分注入
            • 使用setter注入不能保證類的所有的屬性都注入進(jìn)來。
            • 在類對(duì)象相互依賴的時(shí)候可以通過Setter方式解決循環(huán)依賴問題。

          接口回調(diào)注入

          提供Spring中獲取容器本身的一些功能資源,就是通過實(shí)現(xiàn)一系列Spring Aware接口來實(shí)現(xiàn)具體的功能。

          • BeanFactoryAware:獲取 IoC 容器 - BeanFactory
          • ApplicationContextAware:獲取 Spring 應(yīng)用上下文 - ApplicationContext 對(duì)象
          • EnvironmentAware:獲取 Environment 對(duì)象
          • ResourceLoaderAware:獲取資源加載器 對(duì)象 - ResourceLoader
          • BeanClassLoaderAware:獲取加載當(dāng)前 Bean Class 的 ClassLoader
          • BeanNameAware:獲取當(dāng)前 Bean 的名稱
          • MessageSourceAware:獲取 MessageSource 對(duì)象,用于 Spring 國(guó)際化
          • ApplicationEventPublisherAware:獲取 ApplicationEventPublishAware 對(duì)象,用于 Spring 事件
          • EmbeddedValueResolverAware:獲取 StringValueResolver 對(duì)象,用于占位符處理

          上面的接口回調(diào)實(shí)現(xiàn)方式也比較簡(jiǎn)單,基本所有的bean都能實(shí)現(xiàn)Aware接口,但是實(shí)現(xiàn)Aware接口也有一定的局限性,不能進(jìn)行擴(kuò)展只能是進(jìn)行內(nèi)嵌,所以理解這就是一種內(nèi)建的回調(diào)方式。

          ApplicationContextAware實(shí)現(xiàn)代碼為例如下圖所示

          @Component
          public class SetterServiceInjectionTest implements ApplicationContextAware {

          //    @Autowired
          //    private SetterServiceInjection setterServiceInjection;

              private ApplicationContext applicationContext;

              public void testMethod() {
                  SetterServiceInjection setterServiceInjection = (SetterServiceInjection) applicationContext.getBean("setterServiceInjection");
                  setterServiceInjection.testMethod("接口回調(diào)");
              }

              public static void main(String[] args) {
                  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
                  //獲取IOC容器中的bean
                  SetterServiceInjectionTest serviceInjectionTest = (SetterServiceInjectionTest) applicationContext.getBean("setterServiceInjectionTest");
                  serviceInjectionTest.testMethod();
              }

              // 獲取上下文
              @Override
              public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
                  this.applicationContext = applicationContext;
              }
          }

          方法注入

          方法注入實(shí)現(xiàn)方式可以分為四種:

          • @Autowired:是Spring自帶的注解,依照類型進(jìn)行裝配。
          • @Bean:產(chǎn)生一個(gè)Bean對(duì)象,然后這個(gè)Bean對(duì)象交給Spring管理。
          • @Resource:@Resource`是JavaEE的標(biāo)準(zhǔn),Spring對(duì)它是兼容性的支持,依照名稱進(jìn)行裝配。
          • @Inject(不常見):jsr330中的規(guī)范。

          以常見的Autowired為例

              @Autowired 
              private SetterServiceInjection setterServiceInjection;

              public void testMethod(){
                  setterServiceInjection.testMethod("方法注入");
              }
              public static void main(String[] args) {
                  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
                  //獲取IOC容器中的bean
                  SetterServiceInjectionTest serviceInjectionTest = (SetterServiceInjectionTest) applicationContext.getBean("setterServiceInjectionTest");
                  serviceInjectionTest.testMethod();
              }

          從上面的代碼中并不需要再寫一些構(gòu)造方法,也不用配置相關(guān)XML文件只要簡(jiǎn)單的加上@Autowired一個(gè)注解就能完成bean的相互關(guān)聯(lián)。

          所以方法注入可以理解不用關(guān)心方法名稱也不用關(guān)心方法類型,只要方法上面在參數(shù)里面有相關(guān)的依賴類型同時(shí)加上@Autowired或者  @Resource 就能相關(guān)聯(lián)上。

          類型選擇

          上面介紹了這么多類型,那么應(yīng)該怎么合理的選擇哪個(gè)依賴的注入類型呢?

          • 構(gòu)造器注入:強(qiáng)制依賴類型,低依賴。
          • Setter 方法注入:非很強(qiáng)的強(qiáng)制依賴類型(無依賴順序),多依賴。
          • 方法注入:常用于聲明類
          • 接口回調(diào)注入:業(yè)務(wù)中常用于寫一些主鍵啥的。

          合理的選擇注入類型能減少業(yè)務(wù)開發(fā)環(huán)境中的很多的問題。

          在真實(shí)的業(yè)務(wù)場(chǎng)景中還會(huì)遇到另外的一個(gè)問題,就是多個(gè)類型相同的bean注冊(cè)到Spring容器中,那么僅僅使用上面的幾種方式Spring框架則會(huì)拋出NoUniqueBeanDefinitionException異常,所以為了解決上述的問題Spring提出了一個(gè)新的注解**@Qualifier**來指定哪一個(gè)bean或者實(shí)現(xiàn)bean的邏輯分組,其用法也相對(duì)來說比較加單

          public class QualifierDemo {

              @Autowired
              private List<Demo> demos; // 1 ,2,3,4 全部都有

              @Autowired
              @Qualifier 
              private List<Demo> demosQualifier; // 只有 3,4

              @Autowired
              @Qualifier("demo2")
              private Demo demo1; // 只有2

              @Bean
              public Demo demo1() {
                  return new Demo(1);
              }
              @Bean
              public Demo demo2() {
                  return new Demo(2);
              }
              @Bean
              @Qualifier // 進(jìn)行邏輯分組
              public Demo demo3() {
                  return new Demo(3);
              }
              @Bean
              @Qualifier // 進(jìn)行邏輯分組
              public Demo demo4() {
                  return new Demo(4);
              }
              @Data
              public class Demo {
                  private Integer id;
                  public Demo (Integer id){
                      this.id =id;
                  }
              }
          }

          通過上面的代碼就能很明確的知道沒有使用Qualifier注解的默認(rèn)就是加載了所有的,使用了Qualifier注解的demosQualifier的里面只有 demo3 和 demo4兩個(gè),同樣也可以指定使用那么bean如demo1所示。

          當(dāng)然這里只介紹了Qualifier的簡(jiǎn)單實(shí)用,在Spring的官方文檔中還有一種用法就是實(shí)現(xiàn)Qualifier擴(kuò)展用法,自定義注解,了解Spring Cloud 的同學(xué)可以去看看@LoadBalanced這個(gè)注解。用法如下

          @Target({ElementType.FIELD, ElementType.METHOD})
          @Retention(RetentionPolicy.RUNTIME)
          @Inherited
          @Documented
          @Qualifier
          public @interface DemoGroup {
          }

          Spring依賴注入差不多就跟大家聊完了,當(dāng)然后一些其他的一些比較少見的就不跟大家細(xì)聊了,比如說延遲依賴注入感興趣的可以小伙伴可以再去看下,推薦是使用ObjectProvider方式來處理。

          總結(jié)

          Spring的依賴注入用一句話來說解耦對(duì)象之間的依賴關(guān)系,通過xml方式或者注解的方式來靈活管理依賴。

          看這中框架性的東西推薦大家可以去看看官方文檔,如果看不懂的英文的可以去找找中文翻譯過的,來加深自己的理解。(中文官方文檔鏈接)。

          接下來剖析一下Spring中的3層緩存怎么去解決的循環(huán)依賴。

          為了加深理解還給大家整理了一下幾個(gè)面試題。

          構(gòu)造器注入和 Setter 注入有啥區(qū)別?更推薦什么方式?

          答案已經(jīng)在文中構(gòu)造器的解釋中給說出來了

          怎么解決多個(gè)類型相同的bean注冊(cè)到Spring容器的使用問題?

          可以使用Qualifier注解來實(shí)現(xiàn)

          參考文檔:中文官方文檔、《小馬哥核心編程》。

          最近在搞的面試版PDF真的覺得還挺有意思的,等搞出來了,應(yīng)該可以讓大家面試前突擊突擊,對(duì)了面試視頻籌劃中了,這次準(zhǔn)備用不同的風(fēng)格演繹,下個(gè)月肯定能出來。

          我是敖丙,你知道的越多,你不知道的越多,我們下期見。

          瀏覽 38
          點(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>
                  啪啪视频免费网址 | 哪个网站可以看毛片 | 美女色综合网 | 青青草原网址 | 欧美日韩国产中文字幕 |