- Spring入門
Spring入門
使用Spring容器
Spring有兩個核心接口,BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。它們都可代表Spring容器,Spring容器是生成Bean實例的工廠,并管理容器中的Bean.
Spring容器
BeanFactory是Spring最基本的容器,包含如下幾個基本方法:
// 判斷Spring容器中是否包含id為name的Bean實例
boolean containsBean(String name);
// 獲取Spring容器中輸入requiredType類型的、唯一的Bean實例
<T> T getBean(Class<T> requiredType);
// 返回容器id為name的Bean實例
Object getBean(String name);
// 返回容器中id為name,并且類型類requiredType的Bean
<T> T getBean(String name,class<T> requiredType);
// 返回容器中id為name的Bean實例的類型
BeanFactory常用的實現類為DefaultListableBeanFactory
// 搜索類加載路徑下的beans.xml文件創建Resource對象
Resource isr = new ClassPathResource("beans.xml");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory;
new XmlBeanDefinitionReader(beanFactor).loadBeanDefinitions(isr);
如果應用需要加載多個配置文件來創建Spring容器,則應該采用BeanFactory的子接口ApplicationContext來創建BeanFactory的實例。ApplicationContext接口包含FileSystemXmlApplicationContext和ClassXmlApplicationContext兩個常用類,分別代表從類路徑加載配置文件和從文件系統加載配置文件。
ApplicationContext appContext = new ClassPathXmlApplicationContext("beans.xml", "server.xml");
使用ApplicationContext
ApplicationContext支持BeanFactory的全部功能,還支持如下額外功能:
- ApplicationContext默認會初始化所有的singleton Bean,也可以通過配置取消初始化
- ApplicationContext繼承MessageSource接口,提供國際化支持
- 同時加載多份配置文件
- 事件機制
- 以聲明方式啟動并創建Spring容器
當系統創建ApplicationContext容器時,默認會預初始化所有的singleton Bean,所以系統前期創建ApplicationContenxt將消耗較大的系統開銷,但一旦初始化完成,程序后面獲取singleton Bean實例會有較好的性能。
BeanFactory在創建時并不會初始化所有的Bean,只有在getBean方法顯式調用時才會初始化Bean。
ApplicationContext包括BeanFactory的全部功能,因此建議優先使用ApplicationContext。除非對于哪些內存非常關鍵的引用,才考慮使用BeanFactory.
無可阻止Spring容器預初始化容器中的singleton Bean,可以為<bean.../>
元素指定lazy-init="true",該屬性用于阻止預初始化該Bean
ApplicationContext的事件支持
ApplicationContext的事件機制是觀察者模式的實現。如果容器中有一個ApplicationListener Bean,每當ApplicationContext發布ApplicationEvent時,ApplicationListeners Bean將自動觸發
Spring的事件框架依賴如下兩個重要成員:
- ApplicationEvent:容器事件,必須由ApplicationContext發布
- ApplicationListener:監聽器,可由容器中的任何監聽器Bean擔任
只要一個Java類繼承了ApplicationEvent基類,那該對象就可以作為Spring容器的容器事件。
容器事件的監聽器類必須實現ApplicationListener接口,該接口必須實現如下方法:
- onApplicationEvent(ApplicationEvent event):每當容器內發生任何事件時,該方法自動被觸發
在配置文件配置監聽類Bean時,可省略id屬性,Spring容器會自動識別該監聽器
當系統創建Spring容器、加載Spring容器時會觸發容器事件,容器事件監聽器可以監聽到這些事件。程序也可調用ApplicaionContext的publicEvent()方法來主動觸發容器事件。
如果開發者需要在Spring容器初始化、銷毀時回調自定義方法,就可以通過事件監聽器來實現。
注:如果希望Bean發布容器事件,則必須讓該Bean實現ApplicationContextAware或BeanFactoryAware接口,以獲得對容器的引用。
讓Bean獲取Spring容器
為了讓Spring獲取它所在的Spring容器,可以讓Bean實現BeanFactoryAware接口,實現如下方法:
setBeanFactory(BeanFacotry beanFactory); //方法中的參數指向創建它的BeanFactory
類似的還有ApplicationContextAware接口,需實現的方法如下:
setApplicationContext(ApplicationContext context); //方法中的參數指向創建它的ApplicationContext
Spring容器會檢查所有的Bean,如果發現某個Bean實現了如上接口,將自己調用該Bean的setBeanFactory或setApplicaionContext方法將自身作為參數傳遞給該方法。
容器中的Bean
Bean的基本定義和Bean別名
<beans.../>
元素是Spring配置文件的根元素,可以指定如下屬性
- default-lazy-init:指定該<beans.../>元素下配置的所有Bean默認的延遲初始化行為
- default-merge:指定該<beans.../>元素下配置的所有Bean的merge行為
- default-autowire:指定該<beans.../>元素下配置的所有Bean默認的自動裝配行為
- default-autowire-candidates:指定該<beans.../>元素下配置的所有Bean默認是否作為自動自動裝配的候選Bean
- default-init-method:指定該<beans.../>元素下配置的所有Bean默認的初始化方法
- default-destory-method:指定該<beans.../>元素下配置的所有Bean默認的回收方法
在單個Bean中應用,只需將defalut去掉便可以應用于該Bean,<bean.../>下指定的屬性會覆蓋<beans.../>下指定的屬性。
在定義Bean時,通常需指定如下兩個屬性:
- id:確定該Bean在Spring容器中的唯一標識,容器對Bean的管理、訪問,以及該Bean的依賴關系都依賴于此屬性
- class:指定該Bean的具體實現類,不能是接口。
id屬性默認不能包含特殊字符,如*@/等。可通過name屬性指定別名使用
- name:該屬性指定一個Bean示例的標識名,表明將為該Bean示例指定別名
- alias:指定一個別名
<bean id="person" class="..." name="#abc,@123,abc*"/>
<alias name="person" alias="jack"/>
<alias name="jack" alias="jackee"/>
容器中Bean的作用域
Spring支持如下6種作用域:
- singleton:單例模式,在整個SpringIoC容器中,該實例只生成一次。默認屬性
- prototype:每次通過同期的getBean()方法獲取prototype作用域的Bean時,都會生成新的實例。
- request:同一次請求中獲取的Bean只生成一次
- session:同一個會話中獲取的Bean只生成一次
- application:對于整個Web應用中獲取的Bean為相同的實例
- websocket:在整個WebSocket的通信過程中,只生成一個實例
常用的為singleton和pototype作用域,其它四種作用域只應用于Web環境中。
對于singleton作用域實例,由容器負責跟蹤Bean實例的狀態,負責該Bean的整個生命行為。對于prototype作用域的的Bean,容器只負責生成,一旦創建成功便不再跟蹤
配置依賴
- 設值注入:通過
<property.../>
元素驅動Spring執行setter方法- 構造注入:通過
<constructor-arg.../>
元素驅動Spring執行帶參數的構造器
通常不建議使用配置文件管理Bean的基本類型的屬性值;通常只使用配置文件管理容器中Bean之間的依賴關系。
通過<constructor-arg.../>
配置依賴時時根據參數順序進行注入依賴,也可通過index屬性顯式指定參數順序:
<bean id="Person" class="com.learn.Person">
<constructor-arg index="1" value="35"/>
<constructor-arg index="0" value="張三"/>
</bean>
設置普通屬性值
<value.../>
元素用于指定基本類型及其包裝、字符串類型的參數值,Spring通過XML解析器來解析出這些數據,然后利用java.beans.PropertyEditor完成類型轉換:從String類型轉換為所需的參數類型
配置合作者Bean
<ref.../>
元素用于指定所依賴的其它Bean,指定該Bean的id屬性值
使用自動裝配注入合作者Bean
autowire、default-autowire接收如下參數值:
- no:不使用自動裝配。Bean依賴必須通過ref顯式配置。默認配置,不建議更改
- byName:根據setter方法名進行自動裝配。Spring容器查找容器中的全部Bean,找出其id與setter方法名去掉set前綴,并小寫首字母后同名的Bean來完成注入。如沒有找到,則不會進行任何注入。
- byType:根據setter方法的形參類型來自動匹配。Spring容器查找容器中的全部Bean,如果正好有一個Bean類型與setter方法的形參類型匹配,就自動注入這個Bean;如果找到多個這樣的Bean,則拋出異常;如果沒有找到,則什么都不會發生。
- constructor:與byType類型,區別是用于自動匹配構造器的參數
- autodetect:Spring容器根據Bean內部結構,自行決定使用constructor或byType策略。如果找到一個默認的構造參數,那么就會應用byType策略。
當一個Bean既使用自動裝配依賴,又使用ref顯式指定依賴時,則顯式指定的依賴覆蓋自動裝配的依賴。
如果希望將某些Bean排除在自動裝配之外,不作為Spring自動裝配策略的候選者,可<bean.../>
設置該Bean的autowire-candidate屬性為false。還可以通過在<beans.../>
中指定default-autowire-candidates屬性將一批Bean排除在自動裝配之外(該屬性值允許使用模式字符串,可指定多個模式字符串)。
自動裝配將Bean之間的耦合關系降低到代碼層次,不利于高層次解耦;降低了依賴關系的透明性和清晰性。故在大型項目中不推薦使用自動裝配。
注入嵌套Bean
如果某個Bean所依賴的Bean不想被Spring容器直接訪問,則可以使用嵌套Bean。
把<beans.../>
配置成<property.../>
或<constructor-ages.../>
的子元素,那么該<beans.../>
元素配置的Bean僅僅作為setter注入、構造注入的參數。由于容器不能獲取嵌套Bean,因此可不需指定id屬性。
<bean id="chinese" class="org.learn.impl.Chinese">
<property name="axe">
<bean class="org.learn.impl.SteelAxe"/>
</property>
</bean>
注入集合值
如果需要使用形參為集合的setter方法,或調用形參為集合的構造器,則可使用集合元素<list.../>、<set.../>、<map.../>、<props.../>分別來設置類型為List、Set、Map和Properties的集合參數值。
<bean id="chinese" class="org.learn.impl.Chinese">
<property name="schools">
<list>
<value>小學</value>
<value>中學</value>
<value>大學</value>
</list>
</property>
<property name="scores">
<map>
<entry key="數學" value="87"/>
<entry key="英語" value="90"/>
</map>
</property>
<property name="phaseAxes">
<map>
<entry key="原始社會" value-ref="stoneAxe"/>
<entry key="農業社會" value="steelAxe"/>
</map>
</property>
<property name="health">
<props>
<prop key="血壓"/>正常</prop>
<prop key="身高"/>175</prop>
</props>
</property>
<property name="axes">
<set>
<value>字符串</value>
<bean class="..." />
<ref bean="stoneAxe"/>
</set>
</property>
</bean>
<list.../>
、<set.../>
、<key../>
可接受如下子元素:
- value:指定集合元素是基本數據類型或字符串類型值
- ref:指定集合元素是容器中的另一個Bean示例
- bean:指定集合元素是一個嵌套Bean
- list、set、map及props:指定集合元素又是集合
<props.../>
元素用于配置Properties類型的參數值,其key和value只能是字符串
<map.../>
元素的<entry.../>
子元素配置一組鍵值對,其支持如下屬性:
- key:如果Map key是基本類型或字符串
- key-ref:如果Map key是容器中的另外一個Bean實例,使用該屬性指定引用Bean的id
- value:如果Map value是基本類型或字符串
- value-ref:如果Map value是容器中的另外一個Bean實例,使用該屬性指定引用Bean的id
SpringIoc容器支持集合的合并,子Bean中的集合屬性值可以從其父Bean中的集合屬性值繼承和覆蓋而來
<beans>
<bean id="parent" abstract="true" class="org.learn.impl.ComplexObject">
<property name="adminEmails">
<props>
<prop key="admin"/>admin@163.com</prop>
<prop key="support"/>support@163.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<props>
<prop key="sales"/>sales@163.com</prop>
<prop key="support"/>support@163.com</prop>
</props>
</property>
</bean>
</beans>
此時child Bean的adminEmails屬性值通過繼承和覆蓋變為三個
組合屬性
<bean id="chinese" class="org.learn.impl.Chinese">
<property name="foo.bar.x.y" value="xxxx"/>
</bean>
對于這種注入組合屬性值的形式,組合屬性只有最后一個屬性才調用setter方法,前面各屬性實際上對應于調用getter方法。故除最后一個屬性外,其他屬性均不能為null
Spring提供的Java配置管理
@Configuration
public class AppConfig
{
@Value("孫悟空") String personName;
@Bean("name=chinese")
public Person person()
{
Chinese p = new Person();
p.setName(personName);
p.setAxe(stoneAxe());
return p;
}
@Bean(name="stoneAxe")
public Axe stoneAxe()
{
return new StoneAxe();
}
@Bean(name="steelAxe")
public Axe steelAxe()
{
return new SteelAxe;
}
}
- @Configuration:用于修飾一個Java配置類
- @Bean:用于修飾一個方法,將該方法的返回值定義成容器的一個Bean,可通過那么屬性指定該 Bean 的 Id,name 屬性可省略Id默認為方法名
- @Value:用于修飾一個Field,用于為該Field配置一個值,相當于配置一個變量
一旦使用了Java配置類來管理Spring容器中的Bean及其依賴關系,此時就需要如下方式來創建Spring容器:
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
- @Import:修飾一個Java配置類,用于向當前Java配置類中導入其他Java配置類
- @Scope:用于修飾一個方法,指定該方法對應的Bean的作用域
- @Lazy:用于修飾一個方法,指定該方法對應的Bean是否需要延遲初始化
- @DependsOn:用于修飾一個方法,指定在初始化該方法對應的Bean之前先初始化指定的Bean
可混合使用XML配置文件和Java配置類
- 如果以XML配置為主,就需要讓XML配置文件能加載Java類配置。在XML中增加如下代碼:
<!-- 導入其他 Bean 配置 --> <import resource="beans.xml"/> <context:annotation-config/> <!-- 加載Java配置類--> <bean class="org.learn.app.config.AppConfig"/>
- 如果以Java配置為主,就需要讓Java配置類能加載XML配置。需要使用@ImportResource注解,導入指定的XML配置文件:
?@Configuration @ImportResource("classpath:/beans.xml") public class AppConfig { ..... }
創建Bean的3種方式
使用構造器創建Bean實例
使用靜態工廠創建Bean
使用靜態工廠方法創建Bean實例時,class屬性指向靜態工廠類,Spring通過該屬性知道是哪個工廠類來創建Bean實例。使用factory-method指定工廠方法,此方法必須是靜態的。如果靜態工廠需要參數,則使用<constructor-arg.../>元素傳入。
<bean id="dog" class="org.learn.app.BeingFactory" factory-method="getBeing">
<!-- 配置靜態工廠方法的參數 -->
<constructor-age value="dog"/>
<!-- 驅動Spring以“我是狗”為參數來執行dog的setMsg方法 -->
<property name="msg" value="我是狗"/>
</bean>
在這個過程中,Spring不再負責創建Bean實例,Bean實例時由用戶提供的靜態工廠類提供創建的。當靜態工廠方法創建了Bean實例后,Spring依然可以管理該Bean實例的的依賴關系,包括為其注入所需的依賴Beean、管理其生命周期等。
調用實例工廠方法創建Bean
使用實例工廠方法時,配置Bean實例的<bean.../>元素無須class屬性,因為Spring容器不再直接實例化該Bean,Spring容器僅僅調用實例工廠的工廠方法,工廠方法負責創建Bean實例。需指定如下參數:
- factory-bean:該屬性的值為工廠Bean的id
- factory-method:該屬性指定實例工廠的工廠方法
如果需要在調用實例方法時傳入參數,則使用<constructor-arg...>元素指定參數值
<bean id="personFactory" class="org.learn.factory.PersonFactory"/>
<bean id="chinese" factory-method="personFactory" factory-method="getPerson">
<constructor-arg value="chin"/>
</bean>
</bean>
深入理解容器中的Bean
抽象Bean與子Bean
將多個<bean.../>配置中相同的信息提取出來,指定<bean.../>中的abstract屬性為tru,集中成配置模板---這個模板不是真正的Bean,不能被實例化。抽象Bean的價值在于被繼承,抽象Bean通常作為父Bean被繼承。抽象Bean可不指定class屬性。
<beans>
<bean id="personTemp" abstract="true">
<property name="name" value="張三"/>
<property name="axe" ref="steelAxe"/>
</bean>
<bean id="chinese" class="org.learn.impl.Chinese" parent="personTemp"/>
<bean id="american" class="org.learn.impl.Chinese" parent="personTemp"/>
當子Bean指定的配置信息與父Bean模板所指定的配置信息不一致時,子Bean所指定的配置信息將覆蓋父Bean所指定的配置信息。子Bean無法從父Bean中繼承如下屬性:depends-on、autowire、singleton、scope、lazy-init。
如果父Bean指定了class屬性,那么子Bean連class屬性都可以省略,子Bean將采用與父Bean相同的實現類。
注:此處的抽象與繼承和Java中的抽象繼承無關,只是配置文件模板化、復用。
容器中的工廠Bean
FactoryBean接口是工廠Bean的標準接口,把工廠Bean(實現BeanFactory接口的Bean)部署在容器中之后,如果程序通過getBean()方法來獲取它時,容器返回的不是FactoryBean實現類的實例,而是返回FactoryBean的產品(即該工廠Bean的getObject()方法的返回值)。
FactoryBean接口提供如下三個方法:
T getObject(); //實現該方法負責返回該工廠Bean生成的Java實例
Class<?> getObjectType(); //實現該方法返回該工廠Bean生成的Java實例的實現類
boolean isSingleton(); //實現該方法表示該工廠Bean生成的Java實例是否為單例模式
配置FactoryBean與配置普通Bean的定義沒有區別,但當程序向Spring容器請求該Bean時,容器返回該BeanFactoryBean的產品,而不是返回該FactoryBean本身。
當程序需要獲得FactoryBean本身時,并不是直接請求Bean id,而是在Bean id前增加&符號,容器則返回FactoryBean本身,而不是它生產的Bean。
獲得Bean自身的id
如果開發Bean時需要預知該Bean的配置id,則可實現BeanNameAware接口,通過該接口即可提前預知該Bean的配置id。BeanNameAware提供如下方法:
setBeanName(String name); //該方法的name參數就是Bean的配置id
此接口的setter方法會由Spring容器自動調用,將部署該Bean的id屬性作為參數傳入。
強制初始化Bean
為了顯式指定被依賴Bean在目標Bean之前初始化,可以使用depends-on屬性,該屬性可以在初始化主調Bean之前,強制初始化一個或多個Bean
依賴關系注入之后的行為
Spring提供兩種方式在Bean全部屬性設置成功后執行特定行為:
- 使用init-method屬性
使用init-method屬性指定某個方法在Bean全部依賴關系設置結束后自動執行。使用這種方式不需要將代碼將Spring的接口耦合在一起,代碼污染小。推薦使用- 實現InitializingBean接口
達到同樣的效果,要求Bean必須繼承InitializingBean接口,該接口提供一個方法 ---void afterPropertiesSet() throws Excepion;>
如果同時使用兩種方式,則Spring容器先執行InitializingBean接口中定義的方法,然后執行init-method屬性指定的方法。
Bean銷毀之前的行為
Spring同樣提供兩張方式定制Bean實例銷毀之前的特定行為:
- 使用destory-method屬性
使用destory-method屬性指定某個方法在Bean銷毀之前自動執行。使用這種方式不需要將代碼將Spring的接口耦合在一起,代碼污染小。推薦使用- 實現DisposableBean接口
可以實現同樣的效果,但必須實現DisposableBean接口,該接口提供void destory() throw Exception;
如果同時使用兩種方式,則Spring容器先執行DisposableBean接口中定義的方法,然后執行destory-method屬性指定的方法。
如果處于一個非Web應用的環境下,為了讓Spring容器優雅地關閉,并調用singleton Bean相應的析構回調方法,則需要在JVM里注冊一個關閉鉤子。
public class BeanTest
{
public static void main(String[] args)
{
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Person p = ctx.getBean("chinese", Person.class);
p.useAxe();
//為Spring容器注冊關閉鉤子
ctx.registerShutdownHook();
}
}
如果在<beans.../>中指定了default-init-method="init",意味著只要Spring容器中的Bean實例具有init()方法,Spring就會在該Spring的所有依賴關系被設置之后,自動調用該Bena實例的init方法。
協調作用域不同的步的Bean
當prototype作用域的Bean依賴singleton作用域的Bean時,使用Spring提供的依賴注入進行管理即可。
當singleton作用域的Bean依賴prototype作用域的Bean時,一旦singleton Bean初始化完成,它就持有了一個prototype Bean,容器再也不會為singleton Bean執行注入了。解決此問題有如下方法:
- 放棄依賴注入:singleton作用域的Bean每次需要prototype作用域的Bean時,主動向容器請新的Bean實例,即可保證每次注入的prototype Bean實例都是最新的實例
- 利用方法注入
使用lookup方法注入可以讓Spring容器重寫容器中Bean的抽象或具體方法,返回查找容器中其他Bean的結果,被查找的Bean通常是一個non-singleton Bean(盡管可以是一個singleton的)。Spring通過使用JDK動態代理或cglib庫修改客戶端的二進制碼,從而實現上述要求。
為了實現lookup方法注入,大致需要如下兩步:
- 將調用者Bean的實現類定義為抽象類,并定義一個抽象方法來獲取被依賴的Bean
- 在<bean.../>元素中添加<lookup-method.../>子元素讓Spring為調用者Bean的實現類實現指定的抽象方法。
<lookup-method.../>元素需要如下兩個屬性:
- name:指定需要讓Spring實現的方法
- bean:指定Spring實現該方法的返回值
public abstract class Chinese
{
private Dog dog;
public abstract Dog getDog();
public void hunt()
{
getDog().run();
}
}
<bean id="personTemp" abstract="true">
<!-- Spring只要檢測到lookup-method元素,Spring會自動為該元素的name屬性指定的方法提供實現體 -->
<lookup-method name="getDog" bean="gunDog"/>
</bean>
<bean id="gunDog" class="org.leanrn.impl.GunDog" scope="prototype">
<property name="name" value="旺財"/>
</bean>
Spring會采用運行時動態增強的方式來實現<lookup-method.../>元素所指定的抽象方法,如果目標抽象類實現過接口,Spring會采用JDK動態代理來實現該抽象類,并為之實現抽象方法;如果目標抽象類沒有實現過接口,Spring會采用cglib實現該抽象類,并為之實現抽象方法。
要保證<lookup-method.../>方法注入每次都產生新的Bean實例,必須將目標Bean部署成prototype作用于;否則,如果容器中只有一個被依賴的Bean實例,即使采用lookup方法注入,每次也依然返回同一個Bean實例。
高級依賴關系配置
獲取其他Bean的屬性值
PropertyPathFactoryBean用來獲取目標Bean的屬性值(實際上就是它的getter方法的返回值),獲得的值可注入其他Bean,也可直接定義成新的Bean。使用PropertyPathFactoryBean調用其他Bean的getter方法需要指定如下信息:
調用哪個對象。由PropertyPathFactoryBean的setTargetObject(Object targetObject)方法指定;
調用哪個getter方法。由PropertyPathFactoryBean的setPropertyPath(String propertyPath)方法指定;
<!-- target bean to be referenced by name -->
<bean id="tb" class="org.springframework.beans.TestBean" singleton="false">
<property name="age" value="10"/>
<property name="spouse">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="11"/>
</bean>
</property>
</bean>
<!-- will result in 12, which is the value of property 'age' of the inner bean -->
<bean id="propertyPath1" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetObject">
<bean class="org.springframework.beans.TestBean">
<property name="age" value="12"/>
</bean>
</property>
<property name="propertyPath" value="age"/>
</bean>
<!-- will result in 11, which is the value of property 'spouse.age' of bean 'tb' -->
<bean id="propertyPath2" class="org.springframework.beans.factory.config.PropertyPathFactoryBean">
<property name="targetBeanName" value="tb"/>
<property name="propertyPath" value="spouse.age"/>
</bean>
<!-- will result in 10, which is the value of property 'age' of bean 'tb' -->
<bean id="tb.age" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
可以使用<util:property-path...>作為PropertyPathFactoryBean的簡化配置,需要如下兩個屬性:
- id:該屬性指定將getter方法的返回值定義成名為id的Bean的實例
- path:該屬性指定將哪個Bean實例、哪個屬性(支持復合屬性)暴露出來
<!-- will result in 10, which is the value of property 'age' of bean 'tb' -->
<util:property-path id="name" path="testBean.age"/>
PropertyPathFactoryBean就是工廠Bean,在這種配置方式下,配置PropertyPathFactoryBean工廠Bean時指定的id屬性,并不是該Bean的唯一標識,而是用于指定屬性表達式的值。
獲取Field值
通過FieldRetrievingFactoryBean類,可訪問類的靜態Field或對象的實例Field值。FieldRetrievingFactoryBean獲得指定Field的值之后,即可將獲取的值注入其他Bean,也可直接定義成新的Bean。使用FieldRetrievingFactoryBean分一下兩種情況:
- 如果要訪問的Field是靜態Field,則需要指定:
調用哪個類。由FieldRetrievingFactoryBean的setTargetClass(String targeClass)方法指定;
訪問哪個Field。由FieldRetrievingFactoryBean的setTargetField(String targetField)方法指定。
- 如果要訪問的Field是實例Field(要求實例Field以public修飾),則需要指定:
調用哪個對象。由FieldRetrievingFactoryBean的setTargetObject(Object targetObject)方法指定
訪問哪個Field。由FieldRetrievingFactoryBean的setTargetField(String targetField)方法指定。
FieldRetrievingFactoryBean還提供了一個setStaticField(String staticField)方法,該方法可同時指定獲取哪個類的哪個靜態Field的值。
// standard definition for exposing a static field, specifying the "staticField" property
<bean id="myField" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField" value="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
</bean>
// convenience version that specifies a static field pattern as bean name
<bean id="java.sql.Connection.TRANSACTION_SERIALIZABLE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>
<util:constant.../>
元素可作為FieldRetrievingFactoryBean訪問靜態Field的簡化配置,使用該元素時可指定如下兩個屬性:
- id:該屬性指定將靜態Field的值定義成名為id的Bean實例
- static-field:該屬性指定訪問哪個類的哪個靜態Field
<util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
獲取方法返回值
通過MethodInvokingFactoryBean工廠Bean可以調用任意類的類方法,也可調用任意對象的實例方法,如果調用的方法有返回值,既可將該指定方法的返回值定義成容器中的Bean,也可將指定方法的返回值注入給其他Bean。使用MethodInvokingFactoryBean分一下兩種情況:
- 如果希望調用的方法時靜態方法,則需要指定:
調用哪個類。通過MethodInvokingFactoryBean的setTargetClass(String targetClass)方法指定;
調用哪個方法。通過MethodInvokingFactoryBean的setTargetMethod(String targetMethod)方法指定;
調用方法的參數。通過MethodInvokingFactoryBean的 setArguments(Object[] arguments)方法指定。
- 如果希望調用的方法是實例方法,則需要指定:
調用哪個對象。通過MethodInvokingFactoryBean的 setTargetObject(Object targetObject)指定;
調用哪個方法。通過MethodInvokingFactoryBean的setTargetMethod(String targetMethod)方法指定;
調用方法的參數。通過MethodInvokingFactoryBean的 setArguments(Object[] arguments)方法指定。
bean定義的一個示例(在基于XML的bean工廠定義中),它使用此類來調用靜態工廠方法:
<bean id="myObject" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="com.whatever.MyClassFactory.getInstance"/>
</bean>
調用靜態方法然后使用實例方法獲取Java系統屬性的示例
<bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="java.lang.System"/>
<property name="targetMethod" value="getProperties"/>
</bean>
<bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="sysProps"/>
<property name="targetMethod" value="getProperty"/>
<property name="arguments" value="java.version"/>
</bean>
基于XML Schema 的簡化配置方式
使用 p:命名空間簡化配置(簡化設值注入)
需要導入 XML Schema 里的 p:命名空間xmlns:p="http://www.springframework.org/shema/p"
<bean id="chinese" class="org.learn.impl.Chinese" p:age="29" p:axe-ref="stoneAxe"/>
<bean id="stoneAxe" class="org.learn.impl.StoneAxe"/>
使用c:命名控件簡化配置(簡化構造注入)
需要導入 XML Schema 里的 c:命名空間xmlns:c="http://www.springframework.org/shema/c
<bean id="chinese" class="org.learn.impl.Chinese" c:age="29" c:axe-ref="stoneAxe"/>
<bean id="stoneAxe" class="org.learn.impl.StoneAxe"/>
可使用 c_N 的方式指定構造器參數位置。
<bean id="chinese" class="org.learn.impl.Chinese" c:_0="29" c:_1-ref="stoneAxe"/>
使用 util:命名空間簡化配置
需要導入 XML Schema 里的 util:命名空間 xmlns:util="http://www.springframework.org/shema/util
constant:該元素用于獲取指定類的靜態 Field
property-path:該元素用于獲取指定對象的 getter 方法的返回值
list:該元素用于定一個一個 List Bean,支持使用<value.../>、<ref.../>、<bean.../>等子元素來定義 List 集合元素。該標簽支持如下三個屬性:
- id :該屬性指定定一個名為 id 的 List Bean 實例
- list-class:該屬性指定 Spring 通過那個 List 實現類來創建 Bean 實例。默認使用 ArrayList
- scope:指定該 List Bean 的作用域
set:該元素用于定一個一個 Set Bean,支持使用<value.../>、<ref.../>、<bean.../>等子元素來定義 Set 集合元素。該標簽支持如下三個屬性:
- id :該屬性指定定一個名為 id 的 Set Bean 實例
- set-class:該屬性指定 Spring 通過那個 Set 實現類來創建 Bean 實例。默認使用 HashSet
- scope:指定該 Set Bean 的作用域
map:該元素用于定一個一個 Map Bean,支持使用<entry.../>元素來定義 Map 的鍵值對。該標簽支持如下三個屬性:
> + id :該屬性指定定一個名為 id 的 Map Bean 實例
- map-class:該屬性指定 Spring 通過那個 Map 實現類來創建 Bean 實例。默認使用 HashMap
- scope:指定該 Map Bean 的作用域
properties:該元素用于加載一份資源文件,并根據加載的資源文件創建一個 Properties Bean 實例。該標簽支持如下三個屬性:
- id :該屬性指定定一個名為 id 的 Properties Bean 實例
- location:指定資源文件的位置
- scope:指定該 Properties Bean 的作用域
SpEL 語法詳述
- 直接量表達式
{3.14159}
{'Hello'}
{false}
- 引用 bean、屬性和方法
SpEL 通過 ID 引用其它的 Bean{sgtPeppers}
引用 Bean 中的屬性{sgtPeppers.artist}
引用 Bean 中的方法{sgtPeppers.selectArtist()}
- 安全導航
{foo?.bar}
先判斷 foo是否為 null,如果 foo 為 null 就直接返回 null - 在表達式中使用類型
{T(java.lang.math).PI}
這里的 T()運算符的結果會是一個 Class 對象,表示訪問類作用域的方法和常量 - 運算符
{2 * T(java.lang.Math).PI * circle.radius}
{disc.title ?: 'Hello World'}
此處的?:表示判斷 disc.title 是否為 null,如果為 null 的話指定默認值為 Hello World - 計算集合
{jukebox.songs[4].title}
表示查詢 jukebox 中 songs 集合的第5個元素的 title,此處[]中表示第幾個元素
{jukebox.songs.?[artist eq 'Hello']}
此處的.?[]運算符表示對指定集合過濾,得到集合的一個子集
{jukebox.songs.^[artist eq 'Hello']}
和jukebox.songs.?[artist eq 'Hello']
此處中.^[]和.$[]分別表示查詢集合中的第一個匹配項和最后一個匹配項
jukebox.songs.![title]
此處的.![]是投影運算符,表示將集合的每個成員中選擇特定的屬性放到另外一個集合中。