- 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常用的實現(xiàn)類為DefaultListableBeanFactory
// 搜索類加載路徑下的beans.xml文件創(chuàng)建Resource對象
Resource isr = new ClassPathResource("beans.xml");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory;
new XmlBeanDefinitionReader(beanFactor).loadBeanDefinitions(isr);
如果應(yīng)用需要加載多個配置文件來創(chuàng)建Spring容器纤壁,則應(yīng)該采用BeanFactory的子接口ApplicationContext來創(chuàng)建BeanFactory的實例雅镊。ApplicationContext接口包含F(xiàn)ileSystemXmlApplicationContext和ClassXmlApplicationContext兩個常用類襟雷,分別代表從類路徑加載配置文件和從文件系統(tǒng)加載配置文件。
ApplicationContext appContext = new ClassPathXmlApplicationContext("beans.xml", "server.xml");
使用ApplicationContext
ApplicationContext支持BeanFactory的全部功能仁烹,還支持如下額外功能:
- ApplicationContext默認(rèn)會初始化所有的singleton Bean,也可以通過配置取消初始化
- ApplicationContext繼承MessageSource接口耸弄,提供國際化支持
- 同時加載多份配置文件
- 事件機(jī)制
- 以聲明方式啟動并創(chuàng)建Spring容器
當(dāng)系統(tǒng)創(chuàng)建ApplicationContext容器時,默認(rèn)會預(yù)初始化所有的singleton Bean卓缰,所以系統(tǒng)前期創(chuàng)建ApplicationContenxt將消耗較大的系統(tǒng)開銷计呈,但一旦初始化完成,程序后面獲取singleton Bean實例會有較好的性能征唬。
BeanFactory在創(chuàng)建時并不會初始化所有的Bean捌显,只有在getBean方法顯式調(diào)用時才會初始化Bean。
ApplicationContext包括BeanFactory的全部功能总寒,因此建議優(yōu)先使用ApplicationContext扶歪。除非對于哪些內(nèi)存非常關(guān)鍵的引用,才考慮使用BeanFactory.
無可阻止Spring容器預(yù)初始化容器中的singleton Bean摄闸,可以為<bean.../>
元素指定lazy-init="true",該屬性用于阻止預(yù)初始化該Bean
ApplicationContext的事件支持
ApplicationContext的事件機(jī)制是觀察者模式的實現(xiàn)善镰。如果容器中有一個ApplicationListener Bean,每當(dāng)ApplicationContext發(fā)布ApplicationEvent時,ApplicationListeners Bean將自動觸發(fā)
Spring的事件框架依賴如下兩個重要成員:
- ApplicationEvent:容器事件年枕,必須由ApplicationContext發(fā)布
- ApplicationListener:監(jiān)聽器炫欺,可由容器中的任何監(jiān)聽器Bean擔(dān)任
只要一個Java類繼承了ApplicationEvent基類,那該對象就可以作為Spring容器的容器事件熏兄。
容器事件的監(jiān)聽器類必須實現(xiàn)ApplicationListener接口,該接口必須實現(xiàn)如下方法:
- onApplicationEvent(ApplicationEvent event):每當(dāng)容器內(nèi)發(fā)生任何事件時摩桶,該方法自動被觸發(fā)
在配置文件配置監(jiān)聽類Bean時桥状,可省略id屬性,Spring容器會自動識別該監(jiān)聽器
當(dāng)系統(tǒng)創(chuàng)建Spring容器硝清、加載Spring容器時會觸發(fā)容器事件岛宦,容器事件監(jiān)聽器可以監(jiān)聽到這些事件。程序也可調(diào)用ApplicaionContext的publicEvent()方法來主動觸發(fā)容器事件耍缴。
如果開發(fā)者需要在Spring容器初始化砾肺、銷毀時回調(diào)自定義方法挽霉,就可以通過事件監(jiān)聽器來實現(xiàn)。
注:如果希望Bean發(fā)布容器事件变汪,則必須讓該Bean實現(xiàn)ApplicationContextAware或BeanFactoryAware接口侠坎,以獲得對容器的引用。
讓Bean獲取Spring容器
為了讓Spring獲取它所在的Spring容器裙盾,可以讓Bean實現(xiàn)BeanFactoryAware接口实胸,實現(xiàn)如下方法:
setBeanFactory(BeanFacotry beanFactory); //方法中的參數(shù)指向創(chuàng)建它的BeanFactory
類似的還有ApplicationContextAware接口,需實現(xiàn)的方法如下:
setApplicationContext(ApplicationContext context); //方法中的參數(shù)指向創(chuàng)建它的ApplicationContext
Spring容器會檢查所有的Bean番官,如果發(fā)現(xiàn)某個Bean實現(xiàn)了如上接口庐完,將自己調(diào)用該Bean的setBeanFactory或setApplicaionContext方法將自身作為參數(shù)傳遞給該方法。
容器中的Bean
Bean的基本定義和Bean別名
<beans.../>
元素是Spring配置文件的根元素徘熔,可以指定如下屬性
- default-lazy-init:指定該<beans.../>元素下配置的所有Bean默認(rèn)的延遲初始化行為
- default-merge:指定該<beans.../>元素下配置的所有Bean的merge行為
- default-autowire:指定該<beans.../>元素下配置的所有Bean默認(rèn)的自動裝配行為
- default-autowire-candidates:指定該<beans.../>元素下配置的所有Bean默認(rèn)是否作為自動自動裝配的候選Bean
- default-init-method:指定該<beans.../>元素下配置的所有Bean默認(rèn)的初始化方法
- default-destory-method:指定該<beans.../>元素下配置的所有Bean默認(rèn)的回收方法
在單個Bean中應(yīng)用门躯,只需將defalut去掉便可以應(yīng)用于該Bean,<bean.../>下指定的屬性會覆蓋<beans.../>下指定的屬性酷师。
在定義Bean時讶凉,通常需指定如下兩個屬性:
- id:確定該Bean在Spring容器中的唯一標(biāo)識,容器對Bean的管理山孔、訪問懂讯,以及該Bean的依賴關(guān)系都依賴于此屬性
- class:指定該Bean的具體實現(xiàn)類,不能是接口台颠。
id屬性默認(rèn)不能包含特殊字符褐望,如*@/等〈埃可通過name屬性指定別名使用
- name:該屬性指定一個Bean示例的標(biāo)識名譬挚,表明將為該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容器中酪呻,該實例只生成一次。默認(rèn)屬性
- prototype:每次通過同期的getBean()方法獲取prototype作用域的Bean時盐须,都會生成新的實例玩荠。
- request:同一次請求中獲取的Bean只生成一次
- session:同一個會話中獲取的Bean只生成一次
- application:對于整個Web應(yīng)用中獲取的Bean為相同的實例
- websocket:在整個WebSocket的通信過程中,只生成一個實例
常用的為singleton和pototype作用域贼邓,其它四種作用域只應(yīng)用于Web環(huán)境中阶冈。
對于singleton作用域?qū)嵗扇萜髫?fù)責(zé)跟蹤Bean實例的狀態(tài)塑径,負(fù)責(zé)該Bean的整個生命行為女坑。對于prototype作用域的的Bean,容器只負(fù)責(zé)生成,一旦創(chuàng)建成功便不再跟蹤
配置依賴
- 設(shè)值注入:通過
<property.../>
元素驅(qū)動Spring執(zhí)行setter方法- 構(gòu)造注入:通過
<constructor-arg.../>
元素驅(qū)動Spring執(zhí)行帶參數(shù)的構(gòu)造器
通常不建議使用配置文件管理Bean的基本類型的屬性值统舀;通常只使用配置文件管理容器中Bean之間的依賴關(guān)系匆骗。
通過<constructor-arg.../>
配置依賴時時根據(jù)參數(shù)順序進(jìn)行注入依賴劳景,也可通過index屬性顯式指定參數(shù)順序:
<bean id="Person" class="com.learn.Person">
<constructor-arg index="1" value="35"/>
<constructor-arg index="0" value="張三"/>
</bean>
設(shè)置普通屬性值
<value.../>
元素用于指定基本類型及其包裝、字符串類型的參數(shù)值碉就,Spring通過XML解析器來解析出這些數(shù)據(jù)盟广,然后利用java.beans.PropertyEditor完成類型轉(zhuǎn)換:從String類型轉(zhuǎn)換為所需的參數(shù)類型
配置合作者Bean
<ref.../>
元素用于指定所依賴的其它Bean,指定該Bean的id屬性值
使用自動裝配注入合作者Bean
autowire瓮钥、default-autowire接收如下參數(shù)值:
- no:不使用自動裝配筋量。Bean依賴必須通過ref顯式配置。默認(rèn)配置碉熄,不建議更改
- byName:根據(jù)setter方法名進(jìn)行自動裝配桨武。Spring容器查找容器中的全部Bean,找出其id與setter方法名去掉set前綴锈津,并小寫首字母后同名的Bean來完成注入呀酸。如沒有找到,則不會進(jìn)行任何注入一姿。
- byType:根據(jù)setter方法的形參類型來自動匹配七咧。Spring容器查找容器中的全部Bean,如果正好有一個Bean類型與setter方法的形參類型匹配叮叹,就自動注入這個Bean艾栋;如果找到多個這樣的Bean,則拋出異常蛉顽;如果沒有找到蝗砾,則什么都不會發(fā)生。
- constructor:與byType類型携冤,區(qū)別是用于自動匹配構(gòu)造器的參數(shù)
- autodetect:Spring容器根據(jù)Bean內(nèi)部結(jié)構(gòu)悼粮,自行決定使用constructor或byType策略。如果找到一個默認(rèn)的構(gòu)造參數(shù)曾棕,那么就會應(yīng)用byType策略扣猫。
當(dāng)一個Bean既使用自動裝配依賴,又使用ref顯式指定依賴時翘地,則顯式指定的依賴覆蓋自動裝配的依賴申尤。
如果希望將某些Bean排除在自動裝配之外,不作為Spring自動裝配策略的候選者衙耕,可<bean.../>
設(shè)置該Bean的autowire-candidate屬性為false昧穿。還可以通過在<beans.../>
中指定default-autowire-candidates屬性將一批Bean排除在自動裝配之外(該屬性值允許使用模式字符串,可指定多個模式字符串)橙喘。
自動裝配將Bean之間的耦合關(guān)系降低到代碼層次时鸵,不利于高層次解耦;降低了依賴關(guān)系的透明性和清晰性厅瞎。故在大型項目中不推薦使用自動裝配饰潜。
注入嵌套Bean
如果某個Bean所依賴的Bean不想被Spring容器直接訪問初坠,則可以使用嵌套Bean。
把<beans.../>
配置成<property.../>
或<constructor-ages.../>
的子元素囊拜,那么該<beans.../>
元素配置的Bean僅僅作為setter注入某筐、構(gòu)造注入的參數(shù)。由于容器不能獲取嵌套Bean冠跷,因此可不需指定id屬性南誊。
<bean id="chinese" class="org.learn.impl.Chinese">
<property name="axe">
<bean class="org.learn.impl.SteelAxe"/>
</property>
</bean>
注入集合值
如果需要使用形參為集合的setter方法,或調(diào)用形參為集合的構(gòu)造器蜜托,則可使用集合元素<list.../>抄囚、<set.../>、<map.../>橄务、<props.../>分別來設(shè)置類型為List幔托、Set、Map和Properties的集合參數(shù)值蜂挪。
<bean id="chinese" class="org.learn.impl.Chinese">
<property name="schools">
<list>
<value>小學(xué)</value>
<value>中學(xué)</value>
<value>大學(xué)</value>
</list>
</property>
<property name="scores">
<map>
<entry key="數(shù)學(xué)" value="87"/>
<entry key="英語" value="90"/>
</map>
</property>
<property name="phaseAxes">
<map>
<entry key="原始社會" value-ref="stoneAxe"/>
<entry key="農(nóng)業(yè)社會" 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:指定集合元素是基本數(shù)據(jù)類型或字符串類型值
- ref:指定集合元素是容器中的另一個Bean示例
- bean:指定集合元素是一個嵌套Bean
- list棠涮、set谬哀、map及props:指定集合元素又是集合
<props.../>
元素用于配置Properties類型的參數(shù)值,其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屬性值通過繼承和覆蓋變?yōu)槿齻€
組合屬性
<bean id="chinese" class="org.learn.impl.Chinese">
<property name="foo.bar.x.y" value="xxxx"/>
</bean>
對于這種注入組合屬性值的形式篇梭,組合屬性只有最后一個屬性才調(diào)用setter方法,前面各屬性實際上對應(yīng)于調(diào)用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默認(rèn)為方法名
- @Value:用于修飾一個Field,用于為該Field配置一個值官脓,相當(dāng)于配置一個變量
一旦使用了Java配置類來管理Spring容器中的Bean及其依賴關(guān)系,此時就需要如下方式來創(chuàng)建Spring容器:
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
- @Import:修飾一個Java配置類涝焙,用于向當(dāng)前Java配置類中導(dǎo)入其他Java配置類
- @Scope:用于修飾一個方法卑笨,指定該方法對應(yīng)的Bean的作用域
- @Lazy:用于修飾一個方法,指定該方法對應(yīng)的Bean是否需要延遲初始化
- @DependsOn:用于修飾一個方法仑撞,指定在初始化該方法對應(yīng)的Bean之前先初始化指定的Bean
可混合使用XML配置文件和Java配置類
- 如果以XML配置為主赤兴,就需要讓XML配置文件能加載Java類配置妖滔。在XML中增加如下代碼:
<!-- 導(dǎo)入其他 Bean 配置 --> <import resource="beans.xml"/> <context:annotation-config/> <!-- 加載Java配置類--> <bean class="org.learn.app.config.AppConfig"/>
- 如果以Java配置為主,就需要讓Java配置類能加載XML配置桶良。需要使用@ImportResource注解座舍,導(dǎo)入指定的XML配置文件:
?@Configuration @ImportResource("classpath:/beans.xml") public class AppConfig { ..... }
創(chuàng)建Bean的3種方式
使用構(gòu)造器創(chuàng)建Bean實例
使用靜態(tài)工廠創(chuàng)建Bean
使用靜態(tài)工廠方法創(chuàng)建Bean實例時,class屬性指向靜態(tài)工廠類陨帆,Spring通過該屬性知道是哪個工廠類來創(chuàng)建Bean實例曲秉。使用factory-method指定工廠方法,此方法必須是靜態(tài)的疲牵。如果靜態(tài)工廠需要參數(shù)承二,則使用<constructor-arg.../>元素傳入。
<bean id="dog" class="org.learn.app.BeingFactory" factory-method="getBeing">
<!-- 配置靜態(tài)工廠方法的參數(shù) -->
<constructor-age value="dog"/>
<!-- 驅(qū)動Spring以“我是狗”為參數(shù)來執(zhí)行dog的setMsg方法 -->
<property name="msg" value="我是狗"/>
</bean>
在這個過程中纲爸,Spring不再負(fù)責(zé)創(chuàng)建Bean實例亥鸠,Bean實例時由用戶提供的靜態(tài)工廠類提供創(chuàng)建的。當(dāng)靜態(tài)工廠方法創(chuàng)建了Bean實例后识啦,Spring依然可以管理該Bean實例的的依賴關(guān)系负蚊,包括為其注入所需的依賴Beean、管理其生命周期等颓哮。
調(diào)用實例工廠方法創(chuàng)建Bean
使用實例工廠方法時家妆,配置Bean實例的<bean.../>元素?zé)o須class屬性,因為Spring容器不再直接實例化該Bean,Spring容器僅僅調(diào)用實例工廠的工廠方法题翻,工廠方法負(fù)責(zé)創(chuàng)建Bean實例揩徊。需指定如下參數(shù):
- factory-bean:該屬性的值為工廠Bean的id
- factory-method:該屬性指定實例工廠的工廠方法
如果需要在調(diào)用實例方法時傳入?yún)?shù),則使用<constructor-arg...>元素指定參數(shù)值
<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"/>
當(dāng)子Bean指定的配置信息與父Bean模板所指定的配置信息不一致時炊豪,子Bean所指定的配置信息將覆蓋父Bean所指定的配置信息凌箕。子Bean無法從父Bean中繼承如下屬性:depends-on、autowire词渤、singleton牵舱、scope、lazy-init缺虐。
如果父Bean指定了class屬性芜壁,那么子Bean連class屬性都可以省略,子Bean將采用與父Bean相同的實現(xiàn)類。
注:此處的抽象與繼承和Java中的抽象繼承無關(guān)慧妄,只是配置文件模板化顷牌、復(fù)用。
容器中的工廠Bean
FactoryBean接口是工廠Bean的標(biāo)準(zhǔn)接口塞淹,把工廠Bean(實現(xiàn)BeanFactory接口的Bean)部署在容器中之后窟蓝,如果程序通過getBean()方法來獲取它時,容器返回的不是FactoryBean實現(xiàn)類的實例饱普,而是返回FactoryBean的產(chǎn)品(即該工廠Bean的getObject()方法的返回值)运挫。
FactoryBean接口提供如下三個方法:
T getObject(); //實現(xiàn)該方法負(fù)責(zé)返回該工廠Bean生成的Java實例
Class<?> getObjectType(); //實現(xiàn)該方法返回該工廠Bean生成的Java實例的實現(xiàn)類
boolean isSingleton(); //實現(xiàn)該方法表示該工廠Bean生成的Java實例是否為單例模式
配置FactoryBean與配置普通Bean的定義沒有區(qū)別,但當(dāng)程序向Spring容器請求該Bean時费彼,容器返回該BeanFactoryBean的產(chǎn)品滑臊,而不是返回該FactoryBean本身。
當(dāng)程序需要獲得FactoryBean本身時箍铲,并不是直接請求Bean id雇卷,而是在Bean id前增加&符號,容器則返回FactoryBean本身颠猴,而不是它生產(chǎn)的Bean关划。
獲得Bean自身的id
如果開發(fā)Bean時需要預(yù)知該Bean的配置id,則可實現(xiàn)BeanNameAware接口,通過該接口即可提前預(yù)知該Bean的配置id翘瓮。BeanNameAware提供如下方法:
setBeanName(String name); //該方法的name參數(shù)就是Bean的配置id
此接口的setter方法會由Spring容器自動調(diào)用贮折,將部署該Bean的id屬性作為參數(shù)傳入。
強制初始化Bean
為了顯式指定被依賴Bean在目標(biāo)Bean之前初始化资盅,可以使用depends-on屬性调榄,該屬性可以在初始化主調(diào)Bean之前,強制初始化一個或多個Bean
依賴關(guān)系注入之后的行為
Spring提供兩種方式在Bean全部屬性設(shè)置成功后執(zhí)行特定行為:
- 使用init-method屬性
使用init-method屬性指定某個方法在Bean全部依賴關(guān)系設(shè)置結(jié)束后自動執(zhí)行呵扛。使用這種方式不需要將代碼將Spring的接口耦合在一起每庆,代碼污染小。推薦使用- 實現(xiàn)InitializingBean接口
達(dá)到同樣的效果今穿,要求Bean必須繼承InitializingBean接口缤灵,該接口提供一個方法 ---void afterPropertiesSet() throws Excepion;>
如果同時使用兩種方式,則Spring容器先執(zhí)行InitializingBean接口中定義的方法蓝晒,然后執(zhí)行init-method屬性指定的方法腮出。
Bean銷毀之前的行為
Spring同樣提供兩張方式定制Bean實例銷毀之前的特定行為:
- 使用destory-method屬性
使用destory-method屬性指定某個方法在Bean銷毀之前自動執(zhí)行。使用這種方式不需要將代碼將Spring的接口耦合在一起芝薇,代碼污染小胚嘲。推薦使用- 實現(xiàn)DisposableBean接口
可以實現(xiàn)同樣的效果,但必須實現(xiàn)DisposableBean接口洛二,該接口提供void destory() throw Exception;
如果同時使用兩種方式馋劈,則Spring容器先執(zhí)行DisposableBean接口中定義的方法立倍,然后執(zhí)行destory-method屬性指定的方法。
如果處于一個非Web應(yīng)用的環(huán)境下侣滩,為了讓Spring容器優(yōu)雅地關(guān)閉,并調(diào)用singleton Bean相應(yīng)的析構(gòu)回調(diào)方法变擒,則需要在JVM里注冊一個關(guān)閉鉤子君珠。
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容器注冊關(guān)閉鉤子
ctx.registerShutdownHook();
}
}
如果在<beans.../>中指定了default-init-method="init",意味著只要Spring容器中的Bean實例具有init()方法娇斑,Spring就會在該Spring的所有依賴關(guān)系被設(shè)置之后策添,自動調(diào)用該Bena實例的init方法。
協(xié)調(diào)作用域不同的步的Bean
當(dāng)prototype作用域的Bean依賴singleton作用域的Bean時毫缆,使用Spring提供的依賴注入進(jìn)行管理即可唯竹。
當(dāng)singleton作用域的Bean依賴prototype作用域的Bean時,一旦singleton Bean初始化完成苦丁,它就持有了一個prototype Bean浸颓,容器再也不會為singleton Bean執(zhí)行注入了。解決此問題有如下方法:
- 放棄依賴注入:singleton作用域的Bean每次需要prototype作用域的Bean時旺拉,主動向容器請新的Bean實例产上,即可保證每次注入的prototype Bean實例都是最新的實例
- 利用方法注入
使用lookup方法注入可以讓Spring容器重寫容器中Bean的抽象或具體方法,返回查找容器中其他Bean的結(jié)果蛾狗,被查找的Bean通常是一個non-singleton Bean(盡管可以是一個singleton的)晋涣。Spring通過使用JDK動態(tài)代理或cglib庫修改客戶端的二進(jìn)制碼,從而實現(xiàn)上述要求沉桌。
為了實現(xiàn)lookup方法注入谢鹊,大致需要如下兩步:
- 將調(diào)用者Bean的實現(xiàn)類定義為抽象類,并定義一個抽象方法來獲取被依賴的Bean
- 在<bean.../>元素中添加<lookup-method.../>子元素讓Spring為調(diào)用者Bean的實現(xiàn)類實現(xiàn)指定的抽象方法留凭。
<lookup-method.../>元素需要如下兩個屬性:
- name:指定需要讓Spring實現(xiàn)的方法
- bean:指定Spring實現(xiàn)該方法的返回值
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屬性指定的方法提供實現(xiàn)體 -->
<lookup-method name="getDog" bean="gunDog"/>
</bean>
<bean id="gunDog" class="org.leanrn.impl.GunDog" scope="prototype">
<property name="name" value="旺財"/>
</bean>
Spring會采用運行時動態(tài)增強的方式來實現(xiàn)<lookup-method.../>元素所指定的抽象方法,如果目標(biāo)抽象類實現(xiàn)過接口冰抢,Spring會采用JDK動態(tài)代理來實現(xiàn)該抽象類松嘶,并為之實現(xiàn)抽象方法;如果目標(biāo)抽象類沒有實現(xiàn)過接口挎扰,Spring會采用cglib實現(xiàn)該抽象類翠订,并為之實現(xiàn)抽象方法。
要保證<lookup-method.../>方法注入每次都產(chǎn)生新的Bean實例遵倦,必須將目標(biāo)Bean部署成prototype作用于尽超;否則,如果容器中只有一個被依賴的Bean實例梧躺,即使采用lookup方法注入似谁,每次也依然返回同一個Bean實例傲绣。
高級依賴關(guān)系配置
獲取其他Bean的屬性值
PropertyPathFactoryBean用來獲取目標(biāo)Bean的屬性值(實際上就是它的getter方法的返回值),獲得的值可注入其他Bean巩踏,也可直接定義成新的Bean秃诵。使用PropertyPathFactoryBean調(diào)用其他Bean的getter方法需要指定如下信息:
調(diào)用哪個對象。由PropertyPathFactoryBean的setTargetObject(Object targetObject)方法指定塞琼;
調(diào)用哪個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實例毅往、哪個屬性(支持復(fù)合屬性)暴露出來
<!-- 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的唯一標(biāo)識委粉,而是用于指定屬性表達(dá)式的值熙揍。
獲取Field值
通過FieldRetrievingFactoryBean類,可訪問類的靜態(tài)Field或?qū)ο蟮膶嵗鼺ield值。FieldRetrievingFactoryBean獲得指定Field的值之后,即可將獲取的值注入其他Bean,也可直接定義成新的Bean残拐。使用FieldRetrievingFactoryBean分一下兩種情況:
- 如果要訪問的Field是靜態(tài)Field,則需要指定:
調(diào)用哪個類。由FieldRetrievingFactoryBean的setTargetClass(String targeClass)方法指定碟嘴;
訪問哪個Field溪食。由FieldRetrievingFactoryBean的setTargetField(String targetField)方法指定。
- 如果要訪問的Field是實例Field(要求實例Field以public修飾)娜扇,則需要指定:
調(diào)用哪個對象错沃。由FieldRetrievingFactoryBean的setTargetObject(Object targetObject)方法指定
訪問哪個Field。由FieldRetrievingFactoryBean的setTargetField(String targetField)方法指定雀瓢。
FieldRetrievingFactoryBean還提供了一個setStaticField(String staticField)方法枢析,該方法可同時指定獲取哪個類的哪個靜態(tài)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訪問靜態(tài)Field的簡化配置刃麸,使用該元素時可指定如下兩個屬性:
- id:該屬性指定將靜態(tài)Field的值定義成名為id的Bean實例
- static-field:該屬性指定訪問哪個類的哪個靜態(tài)Field
<util:constant static-field="java.sql.Connection.TRANSACTION_SERIALIZABLE"/>
獲取方法返回值
通過MethodInvokingFactoryBean工廠Bean可以調(diào)用任意類的類方法醒叁,也可調(diào)用任意對象的實例方法,如果調(diào)用的方法有返回值泊业,既可將該指定方法的返回值定義成容器中的Bean把沼,也可將指定方法的返回值注入給其他Bean。使用MethodInvokingFactoryBean分一下兩種情況:
- 如果希望調(diào)用的方法時靜態(tài)方法吁伺,則需要指定:
調(diào)用哪個類饮睬。通過MethodInvokingFactoryBean的setTargetClass(String targetClass)方法指定;
調(diào)用哪個方法篮奄。通過MethodInvokingFactoryBean的setTargetMethod(String targetMethod)方法指定捆愁;
調(diào)用方法的參數(shù)割去。通過MethodInvokingFactoryBean的 setArguments(Object[] arguments)方法指定。
- 如果希望調(diào)用的方法是實例方法昼丑,則需要指定:
調(diào)用哪個對象呻逆。通過MethodInvokingFactoryBean的 setTargetObject(Object targetObject)指定;
調(diào)用哪個方法菩帝。通過MethodInvokingFactoryBean的setTargetMethod(String targetMethod)方法指定页慷;
調(diào)用方法的參數(shù)。通過MethodInvokingFactoryBean的 setArguments(Object[] arguments)方法指定胁附。
bean定義的一個示例(在基于XML的bean工廠定義中),它使用此類來調(diào)用靜態(tài)工廠方法:
<bean id="myObject" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="com.whatever.MyClassFactory.getInstance"/>
</bean>
調(diào)用靜態(tài)方法然后使用實例方法獲取Java系統(tǒng)屬性的示例
<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:命名空間簡化配置(簡化設(shè)值注入)
需要導(dǎo)入 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:命名控件簡化配置(簡化構(gòu)造注入)
需要導(dǎo)入 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 的方式指定構(gòu)造器參數(shù)位置滓彰。
<bean id="chinese" class="org.learn.impl.Chinese" c:_0="29" c:_1-ref="stoneAxe"/>
使用 util:命名空間簡化配置
需要導(dǎo)入 XML Schema 里的 util:命名空間 xmlns:util="http://www.springframework.org/shema/util
constant:該元素用于獲取指定類的靜態(tài) Field
property-path:該元素用于獲取指定對象的 getter 方法的返回值
list:該元素用于定一個一個 List Bean控妻,支持使用<value.../>、<ref.../>揭绑、<bean.../>等子元素來定義 List 集合元素弓候。該標(biāo)簽支持如下三個屬性:
- id :該屬性指定定一個名為 id 的 List Bean 實例
- list-class:該屬性指定 Spring 通過那個 List 實現(xiàn)類來創(chuàng)建 Bean 實例。默認(rèn)使用 ArrayList
- scope:指定該 List Bean 的作用域
set:該元素用于定一個一個 Set Bean他匪,支持使用<value.../>菇存、<ref.../>、<bean.../>等子元素來定義 Set 集合元素邦蜜。該標(biāo)簽支持如下三個屬性:
- id :該屬性指定定一個名為 id 的 Set Bean 實例
- set-class:該屬性指定 Spring 通過那個 Set 實現(xiàn)類來創(chuàng)建 Bean 實例依鸥。默認(rèn)使用 HashSet
- scope:指定該 Set Bean 的作用域
map:該元素用于定一個一個 Map Bean,支持使用<entry.../>元素來定義 Map 的鍵值對悼沈。該標(biāo)簽支持如下三個屬性:
> + id :該屬性指定定一個名為 id 的 Map Bean 實例
- map-class:該屬性指定 Spring 通過那個 Map 實現(xiàn)類來創(chuàng)建 Bean 實例贱迟。默認(rèn)使用 HashMap
- scope:指定該 Map Bean 的作用域
properties:該元素用于加載一份資源文件,并根據(jù)加載的資源文件創(chuàng)建一個 Properties Bean 實例絮供。該標(biāo)簽支持如下三個屬性:
- id :該屬性指定定一個名為 id 的 Properties Bean 實例
- location:指定資源文件的位置
- scope:指定該 Properties Bean 的作用域
SpEL 語法詳述
- 直接量表達(dá)式
{3.14159}
{'Hello'}
{false}
- 引用 bean衣吠、屬性和方法
SpEL 通過 ID 引用其它的 Bean{sgtPeppers}
引用 Bean 中的屬性{sgtPeppers.artist}
引用 Bean 中的方法{sgtPeppers.selectArtist()}
- 安全導(dǎo)航
{foo?.bar}
先判斷 foo是否為 null,如果 foo 為 null 就直接返回 null - 在表達(dá)式中使用類型
{T(java.lang.math).PI}
這里的 T()運算符的結(jié)果會是一個 Class 對象壤靶,表示訪問類作用域的方法和常量 - 運算符
{2 * T(java.lang.Math).PI * circle.radius}
{disc.title ?: 'Hello World'}
此處的?:表示判斷 disc.title 是否為 null缚俏,如果為 null 的話指定默認(rèn)值為 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]
此處的.![]是投影運算符贮乳,表示將集合的每個成員中選擇特定的屬性放到另外一個集合中忧换。