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配置類

  1. 如果以XML配置為主赤兴,就需要讓XML配置文件能加載Java類配置妖滔。在XML中增加如下代碼:
<!-- 導(dǎo)入其他 Bean 配置 -->
<import resource="beans.xml"/>
<context:annotation-config/>
<!-- 加載Java配置類-->
<bean class="org.learn.app.config.AppConfig"/>
  1. 如果以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方法注入谢鹊,大致需要如下兩步:

  1. 將調(diào)用者Bean的實現(xiàn)類定義為抽象類,并定義一個抽象方法來獲取被依賴的Bean
  2. 在<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 語法詳述

  1. 直接量表達(dá)式
    {3.14159} {'Hello'} {false}
  2. 引用 bean衣吠、屬性和方法
    SpEL 通過 ID 引用其它的 Bean{sgtPeppers}

    引用 Bean 中的屬性{sgtPeppers.artist}

    引用 Bean 中的方法{sgtPeppers.selectArtist()}
  3. 安全導(dǎo)航
    {foo?.bar} 先判斷 foo是否為 null,如果 foo 為 null 就直接返回 null
  4. 在表達(dá)式中使用類型
    {T(java.lang.math).PI}這里的 T()運算符的結(jié)果會是一個 Class 對象壤靶,表示訪問類作用域的方法和常量
  5. 運算符
    {2 * T(java.lang.Math).PI * circle.radius}
    {disc.title ?: 'Hello World'}此處的?:表示判斷 disc.title 是否為 null缚俏,如果為 null 的話指定默認(rèn)值為 Hello World
  6. 計算集合
    {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]此處的.![]是投影運算符贮乳,表示將集合的每個成員中選擇特定的屬性放到另外一個集合中忧换。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市向拆,隨后出現(xiàn)的幾起案子包雀,更是在濱河造成了極大的恐慌,老刑警劉巖亲铡,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件才写,死亡現(xiàn)場離奇詭異葡兑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)赞草,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門讹堤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人厨疙,你說我怎么就攤上這事洲守。” “怎么了沾凄?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵梗醇,是天一觀的道長。 經(jīng)常有香客問我撒蟀,道長叙谨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任保屯,我火速辦了婚禮手负,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘姑尺。我一直安慰自己竟终,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布切蟋。 她就那樣靜靜地躺著统捶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪柄粹。 梳的紋絲不亂的頭發(fā)上瘾境,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音镰惦,去河邊找鬼迷守。 笑死,一個胖子當(dāng)著我的面吹牛旺入,可吹牛的內(nèi)容都是我干的兑凿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼茵瘾,長吁一口氣:“原來是場噩夢啊……” “哼礼华!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拗秘,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤圣絮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后雕旨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扮匠,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡捧请,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了棒搜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疹蛉。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖力麸,靈堂內(nèi)的尸體忽然破棺而出可款,到底是詐尸還是另有隱情,我是刑警寧澤克蚂,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布闺鲸,位于F島的核電站,受9級特大地震影響埃叭,放射性物質(zhì)發(fā)生泄漏摸恍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一游盲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛮粮,春花似錦益缎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至变泄,卻和暖如春令哟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妨蛹。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工屏富, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛙卤。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓狠半,卻偏偏與公主長得像,于是被迫代替她去往敵國和親颤难。 傳聞我的和親對象是個殘疾皇子神年,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容