1. 程式人生 > 實用技巧 >[模組二]Spring IOC應用

[模組二]Spring IOC應用

【1】Spring IOC 基礎

檔名 說明
beans.xml 定義需要例項化物件的類的全限定類名以及類之間依賴關係描述。
BeanFactory.java 通過反射技術來例項化物件並維護物件之間的依賴關係。

Spring框架的IOC實現(3種):

  1. 純xml(bean資訊定義全部配置在xml中):

    // JavaSE應用
    // 方式一
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    // 方式二
    new FileSystemXmlApplicationContext("c:/beans.xml");
    
    // JavaWeb應用
    ContextLoaderListener(監聽器去載入xml)
    
  2. xml+註解(部分bean使用xml定義,bean使用註解定義)

    // JavaSE應用
    // 方式一
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    // 方式二
    new FileSystemXmlApplicationContext("c:/beans.xml");
    
    // JavaWeb應用
    ContextLoaderListener(監聽器去載入xml)
    
  3. 純註解(所有的bean都是用註解定義)

    // JavaSE應用
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    
    // JavaWeb應用
    ContextLoaderListener(監聽器去載入註解配置類)
    

1.1 BeanFactory與ApplicationContext區別

名稱 說明
BeanFactory Spring框架中IOC容器的頂層介面,只是用來定義一些基礎功能和基礎規範。
ApplicationContext 是BeanFactory的一個子介面,具備BeanFactory提供的全部功能。
  1. 兩個實現類基於xml配置:

    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext
  2. 基於註解配置的實現類:

    • AnnotationConfigApplicationContext
  3. 啟動IOC容器的方式

    • Java環境下啟動IOC容器

      • ClassPathXmlApplicationContext:從類的根路徑下載入配置檔案(推薦使用)
      • FileSystemXmlApplicationContext:從磁碟路徑上載入配置檔案
      • AnnotationConfigApplicationContext:純註解模式下啟動Spring容器
    • Web環境下啟動IOC容器

      • 從xml啟動容器

        <!DOCTYPE web-app PUBLIC
             "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
             "http://java.sun.com/dtd/web-app_2_3.dtd" >
        <web-app>
            <display-name>Archetype Created Web Application</display-name>
            <!--配置Spring ioc容器的配置⽂件-->
            <context-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:applicationContext.xml</param-value>
            </context-param>
            <!--使⽤監聽器啟動Spring的IOC容器-->
            <listener>
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            </listener>
        </web-app>
        
      • 從配置類啟動容器

        <!DOCTYPE web-app PUBLIC
                      "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
                 "http://java.sun.com/dtd/web-app_2_3.dtd" >
        <web-app>
            <display-name>Archetype Created Web Application</display-name>
            <!--告訴ContextloaderListener知道我們使⽤註解的⽅式啟動ioc容器-->
            <context-param>
                <param-name>contextClass</param-name>
                <param-value>org.springframework.web.context.support.AnnotationConfigWebAppli
                    cationContext</param-value>
            </context-param>
            <!--配置啟動類的全限定類名-->
            <context-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>com.lagou.edu.SpringConfig</param-value>
            </context-param>
            <!--使⽤監聽器啟動Spring的IOC容器-->
            <listener>
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            </listener>
        </web-app>
        

1.2 純XML模式

1.xml檔案頭

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 https://www.springframework.org/schema/beans/spring-beans.xsd">

2.例項化Bean的三種方式

  • 方式一:使用無參建構函式

    在預設情況下,它會通過反射調⽤⽆參建構函式來建立物件。如果類中沒有⽆參建構函式,將建立失敗。

    <!--配置service物件-->
    <bean id="userService" class="com.lagou.service.impl.TransferServiceImpl"></bean>
    
  • 方式二:使用靜態方法建立

    在實際開發中,我們使⽤的物件有些時候並不是直接通過建構函式就可以創建出來的,它可能在建立的過程 中會做很多額外的操作此時會提供⼀個建立物件的⽅法,恰好這個⽅法是static修飾的⽅法,即是此種情況。

    <!--使⽤靜態⽅法建立物件的配置⽅式-->
    <bean id="userService" class="com.lagou.factory.BeanFactory"factory-method="getTransferService"></bean>
    
  • 方式三:使用例項化方法建立

    此種⽅式和上⾯靜態⽅法建立其實類似,區別是⽤於獲取物件的⽅法不再是static修飾的了,⽽是類中的⼀個普通⽅法。此種⽅式⽐靜態⽅法建立的使⽤⼏率要⾼⼀些。

    <!--使⽤例項⽅法建立物件的配置⽅式-->
    <bean id="beanFactory" class="com.lagou.factory.instancemethod.BeanFactory"></bean>
    <bean id="transferService" factory-bean="beanFactory" factory-method="getTransferService"></bean>
    

3.Bean的X及生命週期

  • 作用範圍的改變
    • 單例模式[singleton](預設):生命週期與容器相同。
    • 多例模式[prototype]:spring框架只負責建立,不負責銷燬。

4.Bean的標籤屬性

屬性名稱 說明
id ⽤於給bean提供⼀個唯⼀標識。在⼀個標籤內部,標識必須唯⼀。
class ⽤於指定建立Bean物件的全限定類名。
name ⽤於給bean提供⼀個或多個名稱。多個名稱⽤空格分隔。
factory-bean ⽤於指定建立當前bean物件的⼯⼚bean的唯⼀標識。當指定了此屬性之後,class屬性失效。
factory-method ⽤於指定建立當前bean物件的⼯⼚⽅法,如配合factory-bean屬性使⽤,則class屬性失效。如配合class屬性使⽤,則⽅法必須是static的。
scope ⽤於指定bean物件的作⽤範圍。通常情況下就是singleton。當要⽤到多例模式時,可以配置為prototype。
init-method ⽤於指定bean物件的初始化⽅法,此⽅法會在bean物件裝配後調⽤。必須是⼀個⽆參⽅法。
destroy-method ⽤於指定bean物件的銷燬⽅法,此⽅法會在bean物件銷燬前執⾏。它只能為scope是singleton時起作⽤。

5.DI依賴注入的xml配置

  • 依賴注入分類

    • 按照注入的方式分類

      類別 說明
      建構函式注入 顧名思義,就是利⽤帶參建構函式實現對類成員的資料賦值。
      set方法注入 它是通過類成員的set⽅法實現資料的注⼊。(使⽤最多的)
    • 按照注入的資料型別分類

      類別 說明
      基本型別和String 注⼊的資料型別是基本型別或者是字串型別的資料。
      其他Bean型別 注⼊的資料型別是物件型別,稱為其他Bean的原因是,這個物件是要求出現在IoC容器中的。那麼針對當前Bean來說,就是其他Bean了。
      複雜型別(集合型別) 注⼊的資料型別是Aarry,List,Set,Map,Properties中的⼀種類型。
  • 依賴注入的配置實現之建構函式注入

    • 顧名思義,就是利⽤建構函式實現對類成員的賦值。它的使⽤要求是,類中提供的建構函式引數個數必須和配置的引數個數⼀致,且資料型別匹配。同時需要注意的是,當沒有⽆參構造時,則必須提供建構函式引數的注⼊,否則Spring框架會報錯。

    • 在使用建構函式注入的時候,涉及的標籤是constructor-arg,該標籤有如下屬性:

      屬性名稱 說明
      name ⽤於給建構函式中指定名稱的引數賦值。
      index ⽤於給建構函式中指定索引位置的引數賦值。
      value ⽤於指定基本型別或者String型別的資料。
      ref ⽤於指定其他Bean型別的資料。寫的是其他bean的唯⼀標識。
  • 依賴注入的配置實現之set方法注入

    • 顧名思義,就是利⽤欄位的set⽅法實現賦值的注⼊⽅式。此種⽅式在實際開發中是使⽤最多的注⼊⽅式。

    • 在使用set方法注入時,需要使用property標籤,該標籤屬性如下:

      屬性名稱 說明
      name 指定注⼊時調⽤的set⽅法名稱。(注:不包含set這三個字⺟,druid連線池指定屬性名稱)
      value 指定注⼊的資料。它⽀持基本型別和String型別。
      ref 指定注⼊的資料。它⽀持其他bean型別。寫的是其他bean的唯⼀標識。複雜型別資料,它指的是集合型別資料。集合分為兩類,⼀類是List結構(陣列結構),⼀類是Map接⼝(鍵值對)。

1.3 XML與註解相結合模式

注意點:

  1. 實際企業開發中,純XML模式使用很少了;
  2. 引入註解功能不需要引入額外的jar;
  3. xml+註解結合模式,xml檔案依然存在,所以,spring IOC容器的啟動仍然從載入xml開始;
  4. 第三方jar中的bean定義在xml中,比如德魯伊資料庫連線池;自己開發的bean的定義使用註解。
  • xml中標籤和註解的對應(IOC)

    xml形式 對應的註解形式
    標籤 @Component("accountDao"),註解加在類上。bean的id屬性內容直接配置在註解後⾯如果不配置,預設定義個這個bean的id為類的類名⾸字⺟⼩寫;另外,針對分層程式碼開發提供了@Componenet的三種別名@Controller、@Service、@Repository分別⽤於控制層類、服務層類、dao層類的bean定義,這四個註解的⽤法完全⼀樣,只是為了更清晰的區分⽽已。
    標籤的scope屬性 @Scope("prototype"),預設單例,註解加在類上。
    標籤的init-method屬性 @PostConstruct,註解加在⽅法上,該⽅法就是初始化後調⽤的⽅法。
    標籤的destroy-method屬性 @PreDestory,註解加在⽅法上,該⽅法就是銷燬前調⽤的⽅法。
  • DI依賴注入的註解實現方式

    • @Autowired(推薦使用)

      1. @Autowired為Spring提供的註解,需要導包;
      2. @Autowired採取的策略是按型別注入。當一個型別有多個bean值的時候,會造成無法選擇具體注入哪一個的情況,這個時候就需要配合@Qualifier使用。@Qualifier告訴Spring具體裝配哪個物件。這個時候我們就可以通過型別和名稱定位到我們想注⼊的物件。
    • @Resource

      @Resource在jdk11中已經移除,如果使用要單獨匯入jar包。

      1. @Resource註解由J2EE提供,需要匯入包;
      2. @Resource預設按照ByName自動注入。
        • 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到就丟擲異常;
        • 如果指定了name,則從上下文中找到名稱匹配的bean進行裝配,找不到則丟擲異常;
        • 如果指定了type,則從上下文找到類似匹配的唯一bean進行裝配,找不到或是找到多個,都會丟擲異常。
        • 如果既沒有指定name,又沒有指定type,則自動按照ByName方式進行裝配。

1.4 純註解模式

註解名 說明
@Confifiguration 表名當前類是⼀個配置類
@ComponentScan 替代 context:component-scan
@PropertySource 引⼊外部屬性配置⽂件
@Import 引⼊其他配置類
@Value 對變數賦值,可以直接賦值,也可以使⽤ ${} 讀取資源配置⽂件中的資訊
@Bean 將⽅法返回物件加⼊ SpringIOC 容器

【2】Spring IOC高階特性

2.1 lazy-Init延遲載入

應用場景

  1. 開啟延遲載入一定程度提高容器啟動和運轉效能;
  2. 對於不常使用的Bean設定延遲載入,這樣偶爾使用的時候再載入,不必要從一開始該Bean就佔用資源。

Bean的延遲載入(延遲建立)

原因:ApplicationContext容器的預設行為是在啟動伺服器時將所有singleton bean提前例項化。預設lazy-init=“false”。

2.2 FactoryBean和BeanFactory

名稱 說明
FactoryBean BeanFactory接⼝是容器的頂級接⼝,定義了容器的⼀些基礎⾏為,負責⽣產和管理Bean的⼀個⼯⼚,具體使⽤它下⾯的⼦接⼝型別,⽐如ApplicationContext。
BeanFactory Spring中Bean有兩種,⼀種是普通Bean,⼀種是⼯⼚Bean(FactoryBean),FactoryBean可以⽣成某⼀個型別的Bean例項(返回給我們),也就是說我們可以藉助於它⾃定義Bean的建立過程。

2.3 後置處理器

Spring提供了兩種後處理bean的擴充套件接⼝,分別為 BeanPostProcessor 和BeanFactoryPostProcessor,兩者在使⽤上是有所區別的。

介面名稱 說明
BeanPostProcessor BeanPostProcessor是針對Bean級別的處理,可以針對某個具體的Bean.該接⼝提供了兩個⽅法,分別在Bean的初始化⽅法前和初始化⽅法後執⾏,具體這個初始化⽅法指的是什麼⽅法,類似我們在定義bean時,定義了init-method所指定的⽅法。定義⼀個類實現了BeanPostProcessor,預設是會對整個Spring容器中所有的bean進⾏處理。如果要對具體的某個bean處理,可以通過⽅法引數判斷,兩個型別引數分別為Object和String,第⼀個引數是每個bean的例項,第⼆個引數是每個bean的name或者id屬性的值。所以我們可以通過第⼆個引數,來判斷我們將要處理的具體的bean。注意:處理是發⽣在Spring容器的例項化和依賴注⼊之後。
BeanFactoryPostProcessor BeanFactory級別的處理,是針對整個Bean的⼯⼚進⾏處理,典型應⽤:PropertyPlaceholderConfifigurer,此接⼝只提供了⼀個⽅法,⽅法引數為ConfifigurableListableBeanFactory,該引數型別定義了⼀些⽅法。其中有個⽅法名為getBeanDefifinition的⽅法,我們可以根據此⽅法,找到我們定義bean的BeanDefifinition物件。然後我們可以對定義的屬性進⾏修改,⽅法名字類似我們bean標籤的屬性,setBeanClassName對應bean標籤中的class屬性,所以當我們拿到BeanDefifinition物件時,我們可以⼿動修改bean標籤中所定義的屬性值。BeanDefifinition物件:我們在 XML 中定義的 bean標籤,Spring 解析 bean 標籤成為⼀個 JavaBean,這個JavaBean 就是 BeanDefifinition。注意:調⽤ BeanFactoryPostProcessor ⽅法時,這時候bean還沒有例項化,此時 bean 剛被解析成BeanDefifinition物件。