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>
- class為bean的全限定名,指向classpath下類定義所在位置
- id為bean的唯一名稱標識,在整個IOC容器中必須是唯一的。它必須以字母開頭,後面可以是數字、連字元、下劃線、句號、冒號等符號。
name也是bean的名稱標識,但它可在多個bean命名中重複,幾乎可以使用任何符號如問號或以數字開頭等。
以下是幾點需要注意的:- id和name都可以指定多個名字,名字之間可用逗號、分號、或者空格間隔
- 如果配置了多個name相同的bean,通過getBean方法獲取Bean時,將返回最後宣告的那個Bean(前面的被後面覆蓋)
- 如果id和name屬性都未指定,使用者可通過getBean(“full_name”),即通過class值來獲取,如果定義了多個類相同的匿名Bean,如:
<bean class="full_name1"> <bean class
則可通過getBean(“full_name1”)獲取第一個,getBean(“full_name1#1”)獲取第二個,以此類推。
Bean的每一個屬性對應一個property標籤,name為屬性名,value為注入時為屬性構造的數值
- lazy-int指明bean的初始化時機
- scope:bean的作用域(後面兩種在web應用環境下呈現。
- singleton:整個IOC容器共享一個Bean
- prototype:每次對該bean請求(將其注入到另一個bean中,或者以程式的方式呼叫容器的getBean()方法)時都會建立一個新的bean例項.
- request:每次HTTP請求將會生成各自的bean例項
- 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> books
或String[] 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之間的關係
- 繼承
<bean id="parent" class="xxx1" abstract="true">
<!-- 如果設定了abstract="true"父bean不會被初始化 -->
<bean id="child" class="xxx2" parent="parent">
<!-- 通過parent建立了兩個bean的繼承關係 -->
- 依賴
<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介面 |