Spring裝配bean

Spring中,對象無需自己查找或創建與其所關聯的其他對象。相反,容器負責把需要相互協作的對象引用賦予各個對象。例如一個訂單管理組件需要信用卡認證組件,但它無需自己創建信用卡認證組件,訂單管理組件秩序只需表明自己兩手空空,容器就會主動賦予它一個in用卡認證組件。
創建應用對象之間協作關係的行爲通常稱爲裝配(wiring),這也是依賴注入(DI)的本質。

一、Spring配置bean可選方案

在Spring中裝配bean有多種方式,最常見的有三種:

  • 在XML中進行顯式配置
  • 在java中進行顯式配置
  • 隱式的bean發現機制和自動裝配

二、自動化裝配bean

Spring從兩個角度來實現自動化裝配:

  • 組件掃描(component scanning):spring會自動發現應用上下文中所創建的bean
  • 自動裝配(autowiring):Spring自動滿足bean之間的依賴
    組件掃描和自動裝配組合在一起能將顯示配置降低到最少。

1.創建可被發現的bean

@Component標記需要自動裝載的類,@ComponentScan自動掃描bean

2.爲組建掃描的bean命名

Spring應用上下文中所有的bean都會給定一個ID。沒有明確設置ID,Spring會根據類名爲其制定一個ID,即將類名的第一個字母變爲小寫作爲ID。

  • 使用@Component 時:如需爲這個bean設置不同的ID,則將期望的ID作爲值傳給@Component註解,例如@Component(“XXX”)
  • @Named方式:不用@Component註解,而是使用java依賴注入規範中提供的@Named註解來爲bean設置ID

3.設置組件掃描的基礎包

@ComponentScan可配置基礎掃規則:

  • 默認規則:當未爲@ComponentScan配置屬性時,按照默認規則會以配置類所在的包作爲掃描的基礎包。
  • 配置單個包規則:@ComponentScan(“com.XXX”)。
  • 配置多個掃描包規則:@ComponentScan(basePackages={“com.XXX1”,”com.XXX2”})。
  • 配置規則類所在的包:以上配置的basePackages的值是以String類型表示的,但這種方式不安全,如果重構代碼所指定的基礎包可能就會出現錯誤(String無法直接在編譯或idea中直接識別,改了包名後容易忽視配置的規則包),所以提供了另一種方式,將規則配置爲目標規則包中所包含的類或者接口,basePackages屬性被basePackageClasses,便於重構時引用非法,@ComponentScan(basePackageClasses={XXXCls.class,XXX2Cls.class})。

如果所有的bean都是獨立的,彼此間沒有依賴,使用Component和ComponentScan即可完成注入

4.通過爲bean添加註解實現自動裝配

  • @Autowired
    可以使用到構造器上,還能用在屬性的Setter方法。
    如有且只有一個bean匹配依賴需求的話,那麼這個bean將會被裝配進來。
    如果沒有匹配的bean,那麼在應用上下文創建的時候,Spring會拋出一個異常爲了避免異常的出現,你可以將@Autowired的required屬性設置爲false:
@Autowired(required=false)

將required屬性設置爲false時,Spring會嘗試執行自動裝配,但是如果沒有匹配的bean的話,Spring將會讓這個bean處於未裝配的狀態。但是,把required屬性設置爲false時,你需要謹慎對待。如果在你的代碼中沒有進行null檢查的話,這個處於未裝配狀態的屬性有可能會出現NullPointerException。

  • @Inject
    Inject註解來源於Java依賴注入規範,該規範同時還爲我們定義了@Named註解。在自動裝配中,Spring同時支持@Inject和@Autowired。儘管@Inject和@Autowired之間有着一些細微的差別,但是在大多數場景下,它們都是可以互相替換的。

三、通過java代碼配置bean

在很多場景下通過組件掃描和自動裝配實現Spring的自動化配置是更爲推薦的方式,但有時候自動化配置的方案行不通,因此需要明確配置Spring。比如說,你想要將第三方庫中的組件裝配到你的應用中,在這種情況下,是沒有辦法在它的類上添加@Component和@Autowired註解的,因此就不能使用自動化裝配的方案了。必須要採用顯式裝配的方式。
顯示配置相關注解:

四、通過XML裝配bean

在裝配bean的時候,有另一種可選方案,即XML配置。
在Spring剛剛出現的時候,XML是描述配置的主要方式。在Spring的名義下,我們創建了無數行XML代碼。在一定程度上,Spring成爲了XML配置的同義詞。
儘管Spring長期以來確實與XML有着關聯,但現在需要明確的是,XML不再是配置Spring的唯一可選方案。Spring現在有了強大的自動化配置和基於Java的配置,XML不應該再是第一選擇了。鑑於已經存在那麼多基於XML的Spring配置,所以理解如何在Spring中使用XML還是很重要的。
XML裝配bean步驟如下:

1.配置規範 :

在使用XML爲Spring裝配前,需要創建一個新的配置規範。在使用javaConfig的時候這意味着要創建一個帶有@Configuration註解的類,而在ML配置中,就意味着要創建一個ML文件,並且要以元素爲跟。

<?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
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context">
        <!-- do something -->
</beans>

基本XML配置比同等功能的JavaConfig類複雜,在JavaConfig中秩序@Configuration,而在XML中需要在配置文件的等不聲明多個XML模式的XSD文件來定義配置Spring的XML元素。
用來裝配bean的最基本的XML元素包含在spring-beans模式之中,在上面這個XML文件中,它被定義爲根命名空間。是該模式中的一個元素,它是所有Spring配置文件的根元素。
由於中沒有聲明任何bean,因此這個合法的基礎SpringXML配置暫無用處,需要配置bean來實現。

2.聲明bean:

  • 聲明註解
    要在基於XML的Spring配置中聲明一個bean,要使用spring-beans模式中的另一個元素,元素類似於JavaConfig中的@Bean註解:
<bean class="sound.sgtPeppers" />
  • bean在xml中命名設置
    因爲沒有明確給定ID,bean將會根據全限定類名來進行命名,上例中bean的ID是“sound.sgtPeppers#0”,#0是計數的形式,用來區分相同類型的其他bean,如果聲明瞭另一個sgtPeppers,並且沒有明確進行表示,那自動得到的ID將會是“sound.sgtPeppers#1”。
    儘管自動化的bean命名方式非常方便,但如果稍後要引用,那自動產生的名字就無用了,因此最好是藉助id屬性,爲每個bean設置一個唯一名字:
<bean class="sound.sgtPeppers" id="compactDis" />

減少繁瑣爲了減少XML中繁瑣的配置,只對那些需要按名字引用的bean(比如,需要將對它的引用注入到另一個bean中)進行明確命名。

  • xml中bean的特徵(與JavaConfig比的缺點)
    • 不再需要直接在代碼中負責創建bean對應的實例,由容器被動創建
      當spring發現xml中bean元素時會調用默認構造器來創建bean。xml配置bean創建非常被動,而JavaConfig常見bean則強大的多,可以以任何方式創建。
    • xml配置bean的類型如class等屬性以string的形式設置,無法保證配置有效
      以配置class爲例,當修改了類名後,xml中的string屬性並不能在編譯期間檢查到是否合法,可能需要手動檢查後修改(藉助IDE檢查XML的合法性使用能夠感知Spring功能的IDE,如Spring Tool Suite,能夠在很大程度上幫助你確保Spring XML配置的合法性,但任然無法保證能完全檢查到)。

3.藉助構造器注入初始化bean:

  • 配置DI方式:
    在spring XML配置中只有一個聲明bean的方式:使用bean標籤。
    但是在聲明DI時,會有多種可選的配置方案:

  • constructor-arg標籤元素

  • 使用Spring3.0引入的c-命名空間
    兩者區別很大程度在於是否冗長繁瑣。使用constructor-arg元素比使用c-命名空間更加冗長導致XML更難讀,但constructor-args可以配置或設置更多c-命名空間無法實現的功能。

  • constructor-arg初始化beanDI:
    XML中若一個bean初始化時與需要同時注入另一個bean,需要使用bean元素分別聲明這些bean,在需要映入其他bean來初始化的bean中通過ID來引用:

<bean id="b1Id" class="xx.xx.B1"/>

<bean id="b2Id" class="x.xx.B2">
    <constructor-arg ref="b1Id" />
</bean>

當spring發現這個<bean id="b2Id">元素時會創建一個B2實例,<constructor-arg>元素會告知spring要將一個id爲b1Id的bean引用傳遞到b2的構造器中。

  • 命名空間c-初始化beanDI:
    作爲初始化的替代方案,可以使用Spring的c-命名空間。c-命名空間是在Spring3.0中引入的,他在XML中更爲簡潔的描述構造器參數的方式:
    xml頂部聲明其模式:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:c="https://www.springframework.org/schema/c"
 xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 ...
 </beans>

聲明構造器參數:

<bean id="bean1" class="com.xx.bean1" c:cd-ref="bean2"/>
<bean id="bean1" class="com.xx.bean1" c:_0-ref="bean2"/>
<bean id="bean1" class="com.xx.bean1" c:_-ref="bean2"/>

將字面量注入到構造器中:

(只有一個構造器參數)
<bean id="bean1" class="com.xx.bean1" c:_="hahaha"/>
(多個屬性構造參數)
<bean id ="bean1" class="com.xx.bean1" c:_title="hahaha" c:_name="hahah"/>
<bean id="bean1" class="com.xx.bean1" c:_0="hahah"
c:_1="hahaha"/>

構造器注入字面量:

<bean id="bean1" class="com.xx.bean1">
    <constructor-arg value="hahah"/>
    <constructor-arg value="hahaha"/>
    <constructor-arg><null/></constructor-arg>
</bean>
<bean id="bean1" class="com.xx.bean1">
    <constructor-arg value="hahah"/>
    <constructor-arg value="hahaha"/>
     <list>
         <value>11111</value>
         <value>22222</value>
         ...
     <list>
     </constructor-arg>
</bean>
<bean id="bean1" class="com.xx.bean1">
    <constructor-arg value="hahah"/>
    <constructor-arg value="hahaha"/>
     <list>
         <ref bean="bean111"/>
         <ref bean="bean111"/>
         ...
     <list>
     </constructor-arg>
</bean>
<bean id="bean1" class="com.xx.bean1">
    <constructor-arg value="hahah"/>
    <constructor-arg value="hahaha"/>
     <set>
         <value>11111</value>
         <value>22222</value>
         ...
     <set>
     </constructor-arg>
</bean>

通過對比,constructor-arg比c-命名空間更具優勢,c-目前無法實現裝配集合。

4.設置屬性:

  • <property>
<bean id="bean1" class="com.xx.bean1">
  <property name="bean2" ref="bean2Id" />
  <property name="title" value="hahaha"/>
  <property name="tracks">
    <list>
        <value>hahahah</value>
        <value>deeedep</value>
        ...
    </list>
  </property>
  <property>
  </property>
</bean>

元素爲屬性的setter方法所提供的功能與元素構造器提供的功能是一樣的。

  • p-命名空間:
    spring提供了p-命名空間作爲元素的替代方案:
    xml頂部聲明:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
  ...
>
</beans>

裝配屬性:

<bean id="bean1" class="com.xx.bean1" p:bean2-ref="bean2" />
  • -util命名空間:
    由於p-命名空間無法裝配集合,因此可以使用Spring util-命名空間中的一些功能來簡化BlankDiscbean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:util="http://www.springframework.org/schema/util"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.xsd">
    ...
<beans>

util-命名空間所提供的功能之一就是元素,它會創建一個列表的bean

...
<util:list id="trackingList">
  <value>11111eee</value>
  <value>22222kkk</value>
  ....
</util:list>
<bean id="bean1" class="com.xx.bean1" p:title="vvvv" p:tracks-ref="trackList"/>

導入和混合配置

1.JavaConfig中引用XML配置

  • 僅使用JavaConfig實現兩個bean的引用關係:
@Configuration
public class Bean2Config{
  @Bean
  public Bean1 bean1(){
    return new Bean1();
  }
}

bean2構造需要引用到bean1,除了在構造器中外可以使用@import導入引用:

@Configuration
@Import(CDConfig.class)
public class Bean2Config{
  @Bean
  public Bean2 bean2(Bean1 bean1){
    return new Bean2(bean1);
  }
}

或者不在Bean2的config中Import而是使用另一個更高級的類,同事Import兩個類來使用另一種講兩個類關聯在一起:

@Configuration
@Import({Bean2Config.class,Bean1Config.class})
public class SystemBeanConfig{
}
  • JavaConfig中引用XML中的配置:
    當由於某些原因Bean1配置在XML中(cd-bean-config.xml):
<bean id="bean1" class="com.xx.Bean1" c:_0="hahah" c:_1="lll">
  <contructor-arg>
    <list>
      <value>hahha</value>
      <value>lllk</value>
      ...
    </list>
  </constructor-arg>
</beans>

在bean2中需要引用這個聲明好的bean1時,需使用@ImportResource來引入XML中配置的bean:

@Configuration
@Import(Bean2Config.class)
@ImportResource("classpath:cd-bean-config.xml")
public class SystemBeanConfig{
}

通過以上配置,bean1與bean2都會被加載到Spring容器之中。因爲Bean2中帶有@Bean註解的方法接受一個Bean1作爲參數,因此bean1將會裝配進來,與它是通過XML配置還是java代碼配置的沒有任何關係。

2.XML中引用JavaConfig配置

當正在使用Spring基於XML配置並已經意識到XML主鍵變得無法控制(配置太繁瑣)時,可以將XML配合指紋鍵進行拆分:
使用XML提供的bean元素的Class:

<bean class="com.xx.Bean2Config"/>

如果需要將一個在另一個XML中bean和JavaConfig配置的bean關聯在同一個XML中可以使用和元素:

<bean class="com.xx.Bean2Config/>
<import resource="cd-bean-config.xml"/>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章