1. 程式人生 > >spring學習筆記(3)Bean命名、定義與配置

spring學習筆記(3)Bean命名、定義與配置

基於xml的配置

基礎配置

<bean id="id" name="name" class="full_name">
  <property name="pname" value="pvalue" lazy-init="defalut/true/false" scope="singleton/prototype/request/session" />
</bean>
  1. class為bean的全限定名,指向classpath下類定義所在位置
  2. id為bean的唯一名稱標識,在整個IOC容器中必須是唯一的。它必須以字母開頭,後面可以是數字、連字元、下劃線、句號、冒號等符號。
  3. name也是bean的名稱標識,但它可在多個bean命名中重複,幾乎可以使用任何符號如問號或以數字開頭等。
    以下是幾點需要注意的:

    1. id和name都可以指定多個名字,名字之間可用逗號、分號、或者空格間隔
    2. 如果配置了多個name相同的bean,通過getBean方法獲取Bean時,將返回最後宣告的那個Bean(前面的被後面覆蓋)
    3. 如果id和name屬性都未指定,使用者可通過getBean(“full_name”),即通過class值來獲取,如果定義了多個類相同的匿名Bean,如:
      <bean class="full_name1">
      <bean class
      ="full_name1">
      <bean class="full_name1">

    則可通過getBean(“full_name1”)獲取第一個,getBean(“full_name1#1”)獲取第二個,以此類推。

  4. Bean的每一個屬性對應一個property標籤,name為屬性名,value為注入時為屬性構造的數值

  5. lazy-int指明bean的初始化時機
  6. scope:bean的作用域(後面兩種在web應用環境下呈現。
    1. singleton:整個IOC容器共享一個Bean
    2. prototype:每次對該bean請求(將其注入到另一個bean中,或者以程式的方式呼叫容器的getBean()方法)時都會建立一個新的bean例項.
    3. request:每次HTTP請求將會生成各自的bean例項
    4. session:每次會話請求對應一個bean例項

拓展配置

1. 引用外部Bean

<bean id="idCard" class="test.IdCard"></bean>
   <bean id="user" class="test.User" >
       <property name="idCard" ref="idCard" />
   </bean>

在這裡以物件組合的方式將idCard定義為了user中的一個屬性。上面程式碼使用了簡化形式配置,<property name="idCard" ref="idCard" />相當於

    <property name="idCard">
        <ref bean="idCard"/>
    </property>

在標籤中,除了bean屬性,還有local、parent等,它們定義了引用Bean的可見域:
1. bean:可以引用同一容器或父容器中定義的Bean
2. local:只能引用同一配置檔案中的Bean
3. parent:只能引用父容器的Bean

關於父子容器(上下文)的有關定義:
1. 父子容器可以通過ConfigurableApplicationContext或ConfigurableBeanFactory來實現,這兩個介面中分別有setParent及setParentBeanFactory方法,可以與當前的子容器進行父子容器關聯。也可以在動態載入資原始檔時設定,如方法:ClassPathXmlApplicationContext(String configLocation,ApplicationContext parent)
2. 子容器可以訪問父容器中的Bean,但父容器不能訪問子容器的Bean。在容器內,Bean的id必須是唯一的,但子容器可以擁有一個和父容器id相同的Bean。父子容器層級體系增強了Spring容器架構的擴充套件性和靈活性,因為第三方可以通過程式設計的方式,為一個已經存在的容器新增一個或多個特殊用途的子容器,以提供一些額外的功能

2. 內部Bean

在Bean1內部定義一個Bean2,Bean2僅能被Bean1呼叫,其他Bean不可見,形式如:

<bean id="user" class="test.User" >
   <property name="idCard">
       <bean class="test.IdCard">
           <property name="number" value="123"><null /></property>
       </bean>
   </property>
</bean>

值得注意的是,我們以標籤將idCard的number屬性設定成了null值

3. 級聯屬性

<bean id="user" class="test.User" >
   <property name="idCard.number" ><null />
   </property>
</bean>

此時,在User類中必須有定義IdCard idCard屬性

4. 集合型別屬性

1. List集合或陣列

<bean id="user" class="test.User" >
   <property name="books">
       <list>
           <value>book1</value>
           <value>book2</value>
           <value>book3</value>
       </list>
   </property>
</bean>

此時,在User類中要有定義List<String> booksString[] books屬性

2. Set集合

<bean id="user" class="test.User" >
   <property name="books">
       <set>
           <value>book1</value>
           <value>book2</value>
           <value>book3</value>
       </set>
   </property>
</bean>

對應User類中Set<String> books,當然,books的型別還可以為Set的實現類。

3. Map集合

1. 使用<map>配合<entry>標籤進行組合
<bean id="user" class="test.User" >
   <property name="books">
       <map>
           <entry>
               <key><value>book1</value></key>
               <value>100</value>
           </entry>
           <entry>
               <key><value>book2</value></key>
               <value>200</value>
           </entry>
       </map>
   </property>
</bean>

如果map的鍵為bean,可使用<key><ref bean="beanName"></key>
如果map的值為bean,可使用<key>xxx<key><ref bean="beanName"></key>

2. 使用<props>標籤進行組合
<bean id="user" class="test.User" >
       <property name="books">
           <props>
               <prop key="book1">100</prop>
               <prop key="book2">200</prop>
           </props>
       </property>
   </bean>

對應以上兩種情況,User類中要定義Map books或其實現類

4. 集合合併

<bean id="user" class="test.User"><!-- 父bean -->
       <property name="books" >
           <set>
               <value>book1</value>
               <value>book2</value>
               <value>book3</value>
           </set>
       </property>
   </bean>
   <bean id="subUser" parent="user"><!-- 指定父bean為user -->
       <property name="books" ><!-- 子bean -->
           <set merge="true"><!--和父bean中同名元素集合合併  -->
               <value>book1</value>
               <value>book4</value>
               <value>book5</value>
           </set>
       </property>
   </bean>

呼叫測試函式:

public static void main(String args[]){
   ApplicationContext atc = new ClassPathXmlApplicationContext("spring.xml");
   User user = (User) atc.getBean("subUser");
   System.out.println(user.getBooks().size());
   //print 5 即合併時相同元素會消除
}

如果將merge=”true”去掉,則測試列印結果為3,即父bean中的屬性會被子bean中的覆蓋

5. 自動裝配

格式如:<bean autowire="no/byName/byType/constructor/audodetect/default">

裝配型別 說明
no 預設值,不進行自動裝配
byName 根據屬性名自動裝配。此選項將檢查容器並根據名字查詢與屬性完全一致的bean,並將其與屬性自動裝配
byType 如果容器中存在一個與指定屬性型別相同的bean,那麼將與該屬性自動裝配;如果存在多個該型別bean,那麼丟擲異常,並指出不能使用byType方式進行自動裝配;如果沒有找到相匹配的bean,則什麼事都不發生,也可以通過設定dependency-check=”objects”讓Spring丟擲異常。
constructor 與byType方式類似,不同之處在於它應用於構造器引數。如果容器中沒有找到與構造器引數型別一致的bean, 那麼丟擲異常
autodetect 通過bean類的內省機制(introspection)來決定是使用constructor還是byType方式進行自動裝配。如果發現預設的構造器,那麼將使用byType方式,否則採用 constructor。
default 由上級標籤的default-autowire屬性確定。

注意:在配置bean時,標籤中Autowire屬性的優先順序比其上級標籤高,即是說,如果在上級標籤中定義default-autowire屬性為byName,而在中定義為byType時,Spring IoC容器會優先使用標籤的配置。

小結:
1. 使用自動裝配,配置檔案簡潔了許多。但是,我們不論是使用byName還是byType的方法,Spring不一定就能很準確的為我們找到JavaBean依賴的物件。
2. 如果使用自動裝配,Spring配置檔案的可讀性也大大降低,我們不能很容易的看出個bean之間的依賴關係,這也在一定程度上降低了程式可維護性;也容易造成潛在的錯誤,比如說通過byName來裝配,如果將屬性名字改了後,Spring就不會將其自動裝配給Bean的屬性了。

6. bean之間的關係

  1. 繼承
<bean id="parent" class="xxx1" abstract="true">
<!-- 如果設定了abstract="true"父bean不會被初始化 -->
<bean id="child" class="xxx2" parent="parent">
<!-- 通過parent建立了兩個bean的繼承關係 -->
  1. 依賴
<bean id="bean" class="xxx" depends-on="dobean">

指定bean後於dobean建立和銷燬(銷燬只對作用域為singleton的bean才有效,因為只有“singleton”Bean能被Spring管理銷燬)

關於“depends-on”有什麼好處呢?主要是給出明確的初始化及銷燬順序,比如要初始化當前Bean時要確保指定Bean的資源準備好了,否則使用當前Bean時會看不到準備的資源;而在銷燬時要先在當前Bean的把對指定Bean資源的引用釋放掉才能銷燬指定Bean,否則可能銷燬指定Bean時而當前Bean還保持著資源訪問,造成資源不能釋放或釋放錯誤。

基於java類註解的配置

spring提供了專門的application實現類來管理基於註解的Bean方式配置AnnotationConfigApplicationContext。我們通過@configuration來註解一個java類來標識一個容器,用@Bean來定義一個類,它有與xml檔案配置中一樣的屬性如name、autowire、initMethod、destroyMethods等。如下面例項:

@Configuration//從地位上相當於是一個xml檔案,用於定義Bean
@Import(Beans1.class)//引入另一個java類配置檔案
public class Beans {
   //定義一個Bean
   @Scope("singleton")//設定Bean作用範圍為單例
   @Bean(name = "person",)
   public User userMaker(){
       User user = new User();
       user.setName("user");
       user.setPassword("password");
       return user;
   }
   public static void main(String args[]){
       ApplicationContext atc = new AnnotationConfigApplicationContext(Beans.class);
       //還可以通過註冊的方式來獲取
       /*
       AnnotationConfigApplicationContext atc = new AnnotationConfigApplicationContext();
       atc.register(Beans.class);
       //atc.register(otherBeans.class);//還可以註冊多個
       atc.refresh();//重新整理容器使註冊生效
       */
       User user = atc.getBean("person",User.class);
       System.out.println(user.getName() + "——" + user.getPassword());
       //執行main方法控制檯會打印出: user——password
   }
}

上面的userMaker方法相當於xml檔案中的:

<bean name="person">
   <property name="name" value="user"/>
   <property name="password" value="password"/>
</bean>

基於普通註解的配置

使用形如@Component,@Repository,@Service,@Controller等註解。
- 後面三個註解的功能與第一個基本一致,但我們常將它們用於:
1. Repository : 對DAO實現類註解
2. Service : 對Service實現類註解
3. Controller : 對控制層實現類註解
註解例項如:

    package test;

    @Component("user")
    public class User{}
  • 則與<bean id="user" class="test.User">等價。
  • 要使註解配置資訊生效,需在容器中配置:<context:component-scan base-package="test">它會自動掃描test包下所有的類。
  • 如果希望掃描特定的類,可以使用resource-pattern屬性:`這樣,只會掃描以User為字首的包。
  • 如果需要配置包含特定的類和去除特定的類,可使用<context:include-filter /><context:exclude-filter />標籤。上述兩個標籤有”type”和”expression”兩個屬性,它們的引數如下所示
type expression例項 命中過濾條件的類
annotation test.Service test包下所有標識了@Service註解
assginable test.BaseService test包下所有繼承或實現了BaseService的類
aspectj 包名.aspectj表示式 對應包下滿足aspectj表示式的類
regex test.user* test包下以user開頭的類
custom test.myTypeFilter 通過程式碼定義過濾規則,其中myTypeFiler必須實現org.springframework.core.type.TypeFilter介面