《Spring學習筆記》
第一章 裝配Bean
一、 聲明bean
1. 聲明一個簡單的bean
從Spring3.0開始,提供xml和註解兩種配置Bean的方式。
以下爲xml配置文件聲明一個Bean:
<bean id=”bean_id” class=”bean_class”/>
id屬性定義了bean的名字,class屬性定義了bean的類。當Spring容器加載該bean時,Spring將使用默認的構造方法來實例化該bean。
2. 通過構造方法聲明一個Bean
假設Bean中有一個帶參數的構造方法,可以通過xml配置該bean使用帶參數的構造方法實例化。
Bean代碼:
package com.model
public class MyBean{
private int age;
public MyBean(int age)
{this.age=age;}
public void setAge(int age)
{this.age=age;}
public int getAge()
{return age;}
}
Xml文件:
<bean id=”mybean” class=”com.model.MyBean” >
<constructor-arg value=”18” />
</bean>
3. 通過構造方法引用對象
如果bean的構造方法參數是對象,則可以通過xml文件配置。假設上例中構造方法參數爲對象,示例配置如下:
<bean id=”mybean” class=”com.model.MyBean”>
<constructor-arg ref=”object”/>
</bean>
4. 通過工廠方法創建Bean
有時候靜態工廠方法是實例化對象的唯一方法。Spring支持通過<bean>元素的factory-method屬性來裝配工廠創建的Bean。
假設工廠方法爲
public static Instance getInstance()
{return new Instance();}
xml配置爲:
<bean id=”mybean” class=”com.model.MyBean” >
<factory-method =”getInstance” />
5. Bean的作用域
所有的Spring Bean默認都是單例。當容器分配一個Bean時,它總是返回Bean的同一個實例。
在Spring中配置<bean>元素時,我們可以爲bean聲明一個作用域,爲了讓Spring在每次請求時都獲得一個新的Bean實例,只需要配置bean的scope屬性爲prototype即可。
<bean id=”mybean” class=”com.model.MyBean” scope=”prototype” />
Spring還提供了其他的作用域選項,如下所示
作用域 | 定義 |
singleton | 在每一個Spring容器中,一個bean定義只有一個實例對象(默認) |
prototype | 每次調用都創建一個實例 |
request | 在一次http請求中,每個bean定義對於一個實例,該作用域僅在基於web的上下文中才有效 |
session | 在一個http session中,每個bean定義對應一個實例,該作用域僅在基於web的上下文中才有效 |
global-session | 在一個全局http session中,每個bean對應一個實例,該作用域僅在portlet上下文中才有效 |
6. 初始化和銷燬Bean
爲bean定義初始化和銷燬操作,只需要使用init-method和destory-method參數來配置<bean>元素。init-method屬性指定了在初始化bean時要調用的方法。destory-method屬性指定了bean從容器移除之前要調用的方法。
示例:
<bean id=”mybean” class=”com.model.MyBean”
init-method=”initMethod” destory-method=”destoryMethod” />
使用這種配置,bean在實例化之後會立即調用initMethod方法,在該bean從容器移除和銷燬前,會調用destoryMethod方法。
如果上下文中定義的很多bean都擁有相同名字的初始化方法和摧毀方法,可以使用<beans>元素的default-init-method和default-destory-method屬性:
<beans>
……………………
default-init-method=”default-init-method”
default-destory-method=”default-destory-method”</beans>
7. lazy-init
在bean元素中可以設定lazy-init="true",這樣這個bean可以不跟隨容器啓動而初始化,而是在需要用到這個bean的時候再初始化。
二、 注入bean屬性
通常JavaBean的屬性是私有的,同時擁有一組get、set方法。Spring可以藉助屬性的set方法來配置屬性的值,以實現setter方式的注入。
1. 注入簡單值
在spring中可以使用<property>元素配置bean的屬性。<property>在許多方面都與<constructor-arg>類似,只不過一個是通過構造參數來注入值,另一個是通過調用屬性的setter方法來注入值。
例如:
<bean id=”mybean” class=”com.model.MyBean” >
<property name=”arg” value=”arg_value”>
</bean>
一旦bean被實例化,spring就調用<property>元素所指定屬性的setter方法爲該屬性注入值。value屬性可以指定數值型(int、float、Double等)、boolean、String型的值。
2. 引用其他bean
假設mybean1被mybean引用,
<bean id=”mybean1” class=”com.model.MyBean1”/>
<bean id=”mybean” class=”com.model.MyBean” >
<property name=”arg” ref=”mybean1”>
</bean>
3. 注入內部bean
內部bean是定義在其他bean內部的bean。
例如:
<bean id=”mybean” class=”com.model.MyBean”>
<property name=”arg”>
<bean class=”com.model.MyBean1”/>
</property>
</bean>
內部bean並不僅限於setter注入,還可以把內部bean裝配到構造方法的入參中,如下所示:
<bean id=”mybean” class=”com.model.MyBean”>
<constructor-arg>
<bean class=”com.model.MyBean1”/>
</constructor-arg>
</bean>
注意內部bean沒有id屬性,雖然爲內部bean配置一個id屬性是完全合法的,但是並沒有太大必要,因爲我們永遠不會通過名字來引用內部bean。內部bean最大的缺點是他們不能被複用。內部bean僅適用於一次注入,而且不能被其他bean引用。
4. 使用Spring命名空間P裝配屬性
命名空間p的schema URI 爲http://www.springframework.org/schema/p 使用命名空間p,需要在xml文件中增加如下一段聲明:
xmlns:p=” http://www.springframework.org/schema/p”
命名空間p使用示例:
<bean id=”mybean” class=”com.model.MyBean”
p:arg1=”value1”
p:arg2=”value2”
p:arg3-ref=”object” />
5. 裝配集合
value和ref僅在bean的屬性值是單個值的情況下才有用。當bean的屬性值是複數時,如集合時,可以使用集合配置。
Spring提供了相應的集合配置元素
集合元素 | 用途 |
<list> | 裝配list類型的值,允許重複 |
<set> | 裝配set類型的值,不允許重複 |
<map> | 裝配map類型的值,名稱和值可以是任意類型 |
<props> | 裝配properties類型的值,名稱和值必須是String類型 |
裝配List、Set、Array
<bean id=”mybean” class=”com.model.MyBean”>
<property name=”objectList”>
<list>
<ref bean=”object1”/>
<ref bean=”object2”/>
</list>
</property>
</bean>
list元素包含一個或多個值。這裏的<ref>元素用來定義spring上下文中的其他bean引用,當然還可以使用其他的元素作爲<list>成員,包括<bean>、<value>、<null/>。實際上,<list>可以包含另一個<list>作爲其成員,形成多維列表。
裝配map:
<bean id=”mybean” class=”com.model.MyBean”>
<property name=”objectMap”>
<map>
<entry key =”key1” value-ref=”value1 />
<entry key=”key2” value-ref =”value2”/>
</map>
</property>
</bean>
<map>元素聲明瞭一個java.util.Map類型的值。每個<entry>元素定義了一個map成員。key屬性指定了entry的鍵,value-ref屬性定義了entry的值,並引用了spring上下文中的其他bean。
屬性 | 用途 |
key | 指定map中entry的鍵爲String |
key-ref | 指定map中entry的鍵爲spring上下文其他bean的引用 |
value | 指定map中entry的值爲String |
value-ref | 指定map中entry的值爲spring上下文其他bean的引用 |
6. 裝配空值
爲屬性設置null值,只需要使用<null/>元素
三、 使用表達式裝配 2015-07-21
Spring3引入了Spring表達式語言 SpEL。它通過運行期間執行的表達式將值裝配到bean的屬性或構造函數參數中。
SpEL特性:
l 使用bean的ID來引用bean
l 調用方法和訪問對象的屬性
l 對值進行算術、關係和邏輯運算
l 正則表達式匹配
l 集合操作
一、 字面值
最簡單的SpEL表達式僅包含一個字面值。
<property name=”count” value=”#{5}”/>
#{ }標記會提示Spring這個標記裏的內容是SpEL表達式,它們還可以與非SpEL表達式的值混用。
<property name=”message” value=”the value is #{5}” />
String類型的字面值可以使用單引號或雙引號作爲字符串的界定符。例如
<property name=”name” value=”#{ ‘ kom ’ }”>
二、 引用bean、properties方法
SpEL表達式能做的另一個基本失去是通過id引用其他bean。
<property name=”object” value=”#{mybean}” />
注意,是使用的value而不是ref。
SpEL可以引用其他對象中的屬性。例如
<bean id=”bean1” class=”com.model.MyBean”>
<property name=”anotherbean”>
<bean class=”com.model.AnotherBean”/>
</property>
</bean>
<bean id=”bean2” class=”com.model.MyBean”>
<property name=”anotherbean” value=”#{bean1.anotherbean}”/>
</bean>
其中bean2中的屬性anotherbean引用了bean1中的anotherbean。
SpEL不僅能調用bean的屬性,還可以調用它的方法。例如
<property name=”anotherbean” value=”bean1.getAnotherBean()” />
如果bean1爲null,則SpEL則會拋出一個空指針異常。可以使用null-safe存取器,使用 ?.運算符代替 . 來訪問方法。在放分右邊方法之前,該運算符會確保左邊項的值不會爲null。如果左邊爲null,則SpEL不會嘗試調用右邊的方法。
三、 操作類
在SpEL中,使用T()運算符會調用類作用域的方法和常量。例如在SpEL中使用java的Math類,可以這樣寫:
T(java.lang.Math)
在上面的例子中,T()運算符會返回一個java.lang.Math的類對象。通過該運算符可以訪問指定類的靜態方法和常量。
<property name=”randomNumber” value=”#{T(java.lang.Math).random()}” />
上例中調用了Math.random();
四、 在SpEl值上執行操作
SpEL提供了幾種運算符,這些運算符可以用在SpEL表達式中的值上。
運算符類型 | 運算符 |
算術運算 | +、-、*、/、%、^ |
關係運算 | <、>、==、<=、>=、lt、gt、eq、le、ge |
邏輯運算 | and、not、or、! |
條件運算 | ?:(ternary)、?:(Elvis) |
正則表達式 | mathes |
SpEL提供了所有java支持的基礎算術運算符,它還增加了^運算符執行乘方運算。
<property name=”age” value=”#{Tom.age+20}” />
比較值
例如,比較倆個值是否相同返回boolean類型的值
<property name=”equal ” value=”#{bean1.age==23} /”>
邏輯表達式
<property name=”sex” value=”#{! true}” />
五、 篩選集合
可以使用SpEL引用集合中的某個成員,SpEL同樣具有基於屬性值來過濾集合成員的能力。SpEL可以從集合的成員中提取某些屬性放到一個新的集合中。
爲了展示用戶,定義一個City類。
package com.model
public class City{
private String name;
private int population;
public void setName(String name)
.
.
}
在xml文件中,配置信息如下
<util : list id=”cities” >
<bean class=”com.model.City” p:name=”BeiJing” p:population=”20000000”/>
<bean class=”com.model.City” p:name=”ShangHai” p:population=”30000000”/>
<bean class=”com.model.City” p:name=”HangZhou” p:population=”10000000” />
</util:list>
訪問集合成員
從集合中提取一個成員,並將它裝配到某個屬性中
<property name=”city” value=”# { cities [ 2 ] }” />
查詢集合成員
如果我們想從cities中查詢人口大於1500w的城市,在SpEL中,只需要使用查詢運算符 . ? [ ] 就可以做到。
<property name=”bigCities” value=”#{cities.?[population gt 15000000]}” />
查詢運算符會創建一個新集合,新集合中只存放符合括號內的表達式的成員。
SpEL提供兩種其他查詢運算符: . ^[ ]和 . $ [ ] ,從集合中查詢出第一個匹配項和最後一個匹配項。
投影集合
集合投影是從集合的每一個成員中選擇特定的屬性放入一個新的集合中。SpEL的投影運算符 . ! [ ] 可以做到。
假設僅需要包含城市名字的集合,
<property name=”cityName” value=”#{cities.![name]}” />
可以對集合進行查詢和投影運算,這裏把符合條件的大城市名字注入cityNames
<property name=”cityNames” value=”#{cities.?[population gt 15000000].![name] }” />
第二章 最小化Spring XML配置
Spring提供了幾種技巧,可以幫助減少XML的配置數量。
l 自動裝配(autowiring),有助於減少甚至消除配置<property >元素和<constructor-arg>元素,讓spring自動識別如何裝配bean的依賴關係。
l 自動檢測(autodiscovery)比自動裝配更進了一步,讓spring自動識別哪些類需要被配置成spring bean,從而減少對<bean> 元素的使用。
一、 自動裝配Bean屬性
一、 4種類型的自動裝配
l byName 把與bean的屬性具有相同名字(或者id)的其他bean自動裝配到bean的對應屬性中。如果沒有跟屬性的名字匹配的bean,則該屬性不進行裝配。
l byType 把與Bean的屬性具有相同類型的其他bean自動裝配到bean的對應屬性中。如果沒有跟屬性的類型匹配的bean,則該屬性不進行裝配。
l constructor 把與bean的構造方法入參具有相同類型的其他bean自動裝配到bean的構造方法對應的入參中。
l autodetect 首先嚐試使用constructor進行自動裝配,如果失敗,再嘗試使用byType進行自動裝配。
byName自動裝配
手動裝配:
<bean id=”bean” class=”com.model.Bean” />
<bean id=”bean2” class=”com.model.Bean” >
<property name=”name” value=” Tom” / >
<property name=”bean” ref=”bean” />
</bean>
自動裝配:
<bean id=”bean” class=”com.model.Bean” />
<bean id=”bean2” class=”com.model.Bean” autowire=”byName”>
<property name=”name” value=” Tom” / >
</bean>
這樣,bean2中的bean屬性就被自動裝配了。
byName自動裝配遵循一項約定:爲屬性自動裝配ID與該屬性的名字相同的Bean。使用byName自動裝配的缺點是需要假設bean的名字與其他bean的屬性的名字一樣。
byType自動裝配
當使用byType自動裝配時,spring會尋找哪一個bean的類型與屬性的類型相匹配。但byType自動裝配存在一個侷限性,如果spring尋到到多個bean,它們的類型與需要自動裝配的屬性的類型都匹配,則spring會拋出異常。所有,應用只允許存在一個bean與需要自動裝配的屬性類型相匹配。
爲了避免使用byType自動裝配帶來的歧義,spring提供2種選擇:可以爲自動裝配標識一個首選bean,或者可以取消某個bean自動裝配的候選資格。
爲自動裝配標識一個首選bean,可以使用<bean>元素的primary屬性。如果只有一個候選bean的primary屬性爲true,那麼該bean比其他候選bean優先被選擇。但是primary 默認設置爲true。
如果在自動裝配時,我們希望排除某些bean,則可以設置這些bean的autowire-candidate屬性爲false,這裏我們要求spring在自動裝配時忽略bean作爲候選bean。
constructor自動裝配
如果要通過構造方法注入來配置bean,可以移除<constructor-arg>元素,由spring在應用上下文自動選擇bean注入到構造方法入參中。
<bean id=”bean1” class=”com.model.Bean” autowire=”constructor” />
由於constructor自動裝配和byType自動裝配都是通過bean的類型自動裝配的,當發現多個匹配bean時,spring會拋出異常。
最佳自動裝配
我們可以設置autowire屬性爲autodetect,由spring決定。spring首先嚐試使用constructor自動裝配,如果沒有發現與構造方法匹配的bean時,spring將嘗試使用byType自動裝配。
二、 默認自動裝配
如果需要爲srping應用上下文中的每一個(或者其中大多數)bean配置相同的autowire屬性,可以在根元素<beans> 上增加一個default-autowire屬性。默認情況下,defau-autowire屬性被設置爲none。
三、 混合使用自動裝配和顯示裝配
對某個bean選擇了自動裝配策略,不代表不能對該bean的某些屬性進行顯示裝配。
注意,使用constructor自動裝配時,必須讓spring自動裝配構造方法的所有入參,不能混合使用constructor自動裝配策略。
二、 使用註解裝配
從spring2.25開始,spring支持使用註解自動裝配bean的屬性。
spring容器默認禁止註解裝配。使用前需要啓用:
在<beans>元素中配置<context:annotation-config />,如下
<beans>
……………….
<context:annotation-config />
</beans>
1. 使用@Autowired
使用@Autowired讓spring自動裝配Bean的city屬性,則可以對setCity()方法進行標註,如下
@Autowired
public void setCity(City city){
this.city=city;
}
現在我們可以移除city屬性對應的<property>元素了,Spring會嘗試對該方法執行byType自動裝配。
我們不僅可以使用它標註setter方法,還可以標註需要自動 裝配bean引用的任意方法。@Autowired註解可以標註構造方法。
另外當用@Autowired直接標註屬性,並刪除setter方法,
@Autowired
private City city;
@Autowored甚至不會受限於private關鍵字。
如果沒有匹配的bean或者存在多個匹配的bean,@Autowired註解就會遇到一些麻煩。但是這兩種情況都有相應的解決辦法。如下
2. 可選的自動裝配
屬性不一定非要裝配,null也是可以接受的。在這種情況下,可以設置@Autowired的required屬性爲false來配置自動裝配是可選的。這樣,spring將先嚐試裝配,如果找不到與之匹配的bean,應用不會發生任何問題,而屬性值會被設置爲null。
3. 限定歧義性的依賴
當與之匹配的bean有多個的時候,可以使用@Qualifier註解來指明使用id來匹配bean,例如
@Autowired
@Qualifier(“ShangHai”)
private City city;
如上,@Qualifier註解將嘗試注入id爲”ShangHai” 的bean。
4. 在註解注入中使用表達式
spring3.0引入了@Value,可以讓我們使用註解裝配String類型的值和基本類型的值,例如int、boolean。例如
@Value(“Tom Clone”)
private String name;
在這裏,爲String類型的屬性name裝配了一個String類型的值。
藉助於SpEL表達式,使得@Value成爲強大的裝配可選方案。
@Value(#{city[0].name})
private String name;
5. @Scope
相當於<bean>元素中的scope屬性
6. @postconstruct
相當於init-method
7. @predestor
相當於destory-method
三、 自動檢測bean
<context:component-scan>元素除了完成與<context:annotation-config>一樣的工作,還允許spring自動檢測bean和定義bean。這意味着不用<bean>元素,spring中大多數bean能偶實現定義和裝配。
爲了完成spring自動檢測,需要使用 <context:component-scan>代替<context:annotation-config>。
<beans>
…………………………………………….
<context:component-scan base-package=”com.model”>
</ context:component-scan >
</beans>
<context:component-scan>元素會自動掃描指定的包及其所有子包,並查找出能自動註冊爲bean的類。base-package屬性標識了所掃描的包。
1. 爲自動檢測標註bean
默認情況下,<context:component-scan>查找使用構造型(stereotype)註解所標註的類,這些特殊的註解如下
n @Component 通用的構造型註解,標識該類爲spring組件
n @Controller 標識將該類定義爲spring mvc controller
n @Repository 標識將該類定義爲數據倉庫
n @Service 標識將該類定義爲服務
n 使用@Component 標註的任意自定義註解
可以使用 @Component(“ id ”)在括號裏顯式的指定bean的id
2. 過濾組件掃描
在如何掃描來獲得候選bean方面,<context:component-scan>非常靈活。通過<context:component-scan>配置<context:include-filter>或者<context:exclude-filter>子元素,可以隨意調整掃描行爲。
<context:include-filter>的type和expression屬性一起寫作來定義組件掃描策略。
過濾器類型 | 描述 |
annotation | 過濾器掃描使用指定註解標註的那些類,通過expression屬性指定要掃描的註解 |
assignable | 過濾器掃描派生於expression屬性所指定類型的那些類
|
aspectj | 過濾器掃描與expression屬性所指定的AspectJ表達式所匹配的那些類 |
custom | 使用自定義的org.springframework.core.type.TypeFilter實現類,該類由expression屬性指定 |
regex | 過濾器掃描類的名稱與expression屬性所指定的正則表達式所匹配的類 |
除了使用<context:include-filter>告知哪些類需要註冊爲bean以外,還可以使用<context:exclude-filter>告知哪些類不需要註冊爲bean。
四、 使用基於Spring基於java的配置
第三章 面向切面的Spring
一、 什麼是面向切面編程(AOP)
1. 定義AOP術語
描述切面常用的術語有通知(advice)、切點(pointcut)和連接點(join point)。
通知(advice)
在AOP術語中,切面的工作被稱之爲通知。通知定義了切面是什麼以及何時使用。除了描述切面要完成的工作,通知還解決了何時執行這個工作的問題。它應該應用於某個方法被調用之前?之後?之前和之後?還是隻在方法拋出異常時?
spring切面可以應用5種類型的通知
n Before 在方法被調用之前調用通知
n After 在方法完成之後調用通知,無論方法執行是否成功
n After-returning 在方法成功執行之後調用通知
n After-throwing 在方法拋出異常後調用通知
n Around 通知包裹了被通知的方法,被通知的方法調用之前和調用之後執行自定義的行爲。
連接點(Join point)
連接點是在應用執行過程中能夠插入切面的一個點。這個店可以是調用方法時、拋出異常時、甚至修改一個字段時。切面代碼可以利用這些點插入到應用的正常流程之中,並添加新的行爲。
切點(pointcut)
一個切面並不需要通知應用的所有連接點。切點有助於縮小切面所通知連接點的範圍。
切面(Aspect)
切面是通知和切點的結合。通知和切點共同定義了關於切面的全部內容-它是什麼,在何時和何處完成其功能。
引用(Introduction)
引用允許我們向現有的類添加新的方法或屬性。
織入(Weaving)
織入是將切面應用到目標對象來創建新的代理對象的過程。切面在指定的連接點被織入到目標對象中。
2. 五種類型的通知
l Before 在方法被調用之前調用通知
l After 在方法完成之後調用通知,無論方法是否執行成功
l After-returnning 在方法成功執行之後調用通知
l After-throwing在方法拋出異常後調用通知
l Around 通知包裹了被通知的方法,被通知的方法調用之前和調用之後執行自定義的行爲。
3. 編寫切點(annotation方式)
首先,需要在xml文件中打開aop,在xml文件中添加以下代碼
<aop:aspectj-autoproxy/>
添加aspectJ的依賴包aspectjweaver.jar。
在切面類上使用註解@Aspect,並且使用註解@Component或者xml文件配置方式把切面類配置成bean。
使用execution()指示器選擇Instrument的play()方法。方法表達式以*號開始,標識了不需要關心方法返回值的類型,然後我們置頂了全限定類名和方法名。對於方法參數列表,使用( . . )標識切點選擇任意的play()方法,無論該方法的入參是什麼。
現在假設我們需要配置切點僅匹配com.springinaction.springidol包。此時可以使用withdin()指示器來限制匹配。如下
execution(*com.springincation.springidol.Instrument.play(..))&&within(com.springincation.springidol.*)
注意,使用&&把execution()和within()指示器連接在一起形成and關係。類似,也可以使用||、!操作符,在xml文件中,&符號具有特殊意義,所以使用and代替&&。同樣可以使用or、not。
注意,bean()指示器允許我們在切點表達式中使用bean 的id來標識bean。bean()指示器使用bean id或bean名稱作爲參數來限制切點只匹配特定的bean。
4. 在xml中聲明切面
AOP配置元素 | 描述 |
<aop:advisor> | 定義AOP通知 |
<aop:after> | 定義AOP後置通知(不管通知的方法是否執行成功) |
<aop:after-returning> | 定義AOP after-returning通知 |
<aop:around> | 定義AOP around 通知 |
<aop:aspect> | 定義切面 |
<aop:aspectj-autoproxy> | 啓用@AspectJ註解 |
<aop:before> | 定義AOP after通知 |
<aop:declare-parents> | 爲被通知的對象引入額外的接口,並透明地實現 |
<aop:pointcut> | 定義切點 |
<aop:config> | 頂層的AOP元素,大多數<aop:*>元素必須包含在此元素下 |