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

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

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

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

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

    引用 Bean 中的方法{sgtPeppers.selectArtist()}
  3. 安全導航
    {foo?.bar} 先判斷 foo是否為 null,如果 foo 為 null 就直接返回 null
  4. 在表達式中使用類型
    {T(java.lang.math).PI}這里的 T()運算符的結果會是一個 Class 對象,表示訪問類作用域的方法和常量
  5. 運算符
    {2 * T(java.lang.Math).PI * circle.radius}
    {disc.title ?: 'Hello World'}此處的?:表示判斷 disc.title 是否為 null,如果為 null 的話指定默認值為 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]此處的.![]是投影運算符,表示將集合的每個成員中選擇特定的屬性放到另外一個集合中。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容