Spring bean加載之1:BeanFactory和FactoryBean

BeanFactory

BeanFactory:以Factory結尾,表示它是一個工廠類(接口),用於管理Bean的一個工廠。在Spring中,BeanFactory是IOC容器的核心接口,它的職責包括:實例化、定位、配置應用程序中的對象及創建這些對象間的依賴。git

Spring爲咱們提供了許多易用的BeanFactory實現,XmlBeanFactory就是經常使用的一個,該實現將以XML方式描述組成應用的對象及對象間的依賴關係。XmlBeanFactory類將持有此XML配置元數據,並用它來構建一個徹底可配置的系統或應用。github

FactoryBean的使用

  通常狀況下,Spring經過反射機制利用bean的class屬性指定實現類來實例化bean。在某些狀況下,實例化bean過程比較複雜,若是按照傳統的方式,則須要在<bean>中提供大量的配置信息,配置方式的靈活性是受限的,這時採用編碼的方式可能會獲得一個簡單的方案。Spring爲此提供了一個org.Springframework.bean.factory.FactoryBean的工程類接口,用戶能夠經過實現接口定製實例化bean的邏輯。spring

FactoryBean接口對於Spring框架來講佔有重要的地位,Spring自身就提供了70多個FactoryBean的實現。它們隱藏了實例化一些複雜bean的細節,給上層應用帶來了便利。從Spring3.0開始,FactoryBean開始支持泛型,即接口聲明改成FactoryBean<T>的形式:服務器

Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);

ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);

ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
BeanFactory factory = (BeanFactory) context;

基本就是這些了,接着使用getBean(String beanName)方法就能夠取得bean的實例;BeanFactory提供的方法及其簡單,僅提供了六種方法供客戶調用:mybatis

  •   boolean containsBean(String beanName) 判斷工廠中是否包含給定名稱的bean定義,如有則返回true
  •   Object getBean(String) 返回給定名稱註冊的bean實例。根據bean的配置狀況,若是是singleton模式將返回一個共享實例,不然將返回一個新建的實例,若是沒有找到指定bean,該方法可能會拋出異常
  •   Object getBean(String, Class) 返回以給定名稱註冊的bean實例,並轉換爲給定class類型
  •   Class getType(String name) 返回給定名稱的bean的Class,若是沒有找到指定的bean實例,則排除NoSuchBeanDefinitionException異常
  •   boolean isSingleton(String) 判斷給定名稱的bean定義是否爲單例模式。對於Singleton屬性,能夠在BeanDefinition中指定。
  •   String[] getAliases(String name) 返回給定bean名稱的全部別名,這些別名都是用戶在BeanDefinition中指定的。
  •        isPrototype(String)查詢指定名字的Bean是不是prototype類型的。與Singleton屬性同樣,這個屬性也能夠在BeanDefinition中指定。
  •        isTypeMatch()查詢指定名字的Bean的Class類型是不是特定的Class類型。這個Class類型能夠由用戶來指定。

  這些定義的接口方法勾畫了IoC容器的基本特性,由於BeanFactory接口定義了IoC容器。app

        
    

FactoryBean

FactoryBean:以Bean結尾,表示它是一個Bean,不一樣於普通Bean的是:它是實現了FactoryBean<T>接口的Bean,根據該Bean的ID從BeanFactory中獲取的其實是FactoryBean的getObject()返回的對象,而不是FactoryBean自己,若是要獲取FactoryBean對象,請在id前面加一個&符號來獲取。框架

FactoryBeandom

下面展現了FactoryBean提供的接口方法,須要注意的是,在Spring中爲咱們實現了大量的FactoryBean,因此能夠看出FactoryBean是很是重要的。this

FactoryBean的使用

通常狀況下,Spring經過反射機制利用bean的class屬性指定實現類來實例化bean。在某些狀況下,實例化bean過程比較複雜,若是按照傳統的方式,則須要在<bean>中提供大量的配置信息,配置方式的靈活性是受限的,這時採用編碼的方式可能會獲得一個簡單的方案。Spring爲此提供了一個org.Springframework.bean.factory.FactoryBean的工程類接口,用戶能夠經過實現接口定製實例化bean的邏輯。編碼

FactoryBean接口對於Spring框架來講佔有重要的地位,Spring自身就提供了70多個FactoryBean的實現。它們隱藏了實例化一些複雜bean的細節,給上層應用帶來了便利。從Spring3.0開始,FactoryBean開始支持泛型,即接口聲明改成FactoryBean<T>的形式:

FactoryBean的源碼:
package org.springframework.beans.factory;

public interface FactoryBean<T> {
    T getObject() throws Exception;

    Class<?> getObjectType();

    boolean isSingleton();
}
  • Object getObject():返回本工廠建立的對象實例。此實例也許是共享的,依賴於該工廠返回的是單例或者是原型。
  • boolean isSingleton():若是FactoryBean返回的是單例,該方法返回值爲true,不然爲false。
  • Class getObjectType():返回對象類型。對象類型是getObject()方法返回的對象的類型,若是不知道的類型則返回null。

FactoryBean概念和接口在Spring框架中大量使用。Spring內置的有超過70個實現。

當配置文件中<bean>的class屬性配置的實現類是FactoryBean時,經過getBean()方法返回的不是FactoryBean自己,而是FactoryBean#getBean()方法所返回的對象,至關於FactoryBean#getObject()代理了getBean()方法。

例如:若是使用傳統方式配置下面Car的<bean>時,Car的每一個屬性分別對應一個<property>元素標籤。

public class Car{
    private int maxSpeed;
    private String brand;
    private double price;
    //get set方法
}

若是用FactoryBean的方式實現就會靈活一些,下例經過逗號分割符的方式一次性地爲Car的全部屬性指定配置值:

public class CarFactoryBean implements FactoryBean<Car>{
    private String carInfo;
    public Car getObject() throws Exception{
        Car car = new Car();
        String[] infos = carInfo.split(",");
        car.setBrand(infos[0]);
        car.setMaxSpeed(Integer.valueOf(infos[1]));
        car.setPrice(Double.valueOf(infos[2]));
        return car;
    }
 
    public Class<Car> getObjectType(){
        return Car.class;
    }
 
    public boolean isSingleton(){
        return false;
    }
 
    public String getCarInfo(){
        return this.carInfo;
    }
 
    public void setCarInfo(Stirng carInfo){
        this.carInfo = carInfo;
    }
}

有了這個CarFactoryBean後,就能夠在配置文件中使用下面這種自定義的配置方式配置CarBean了:

<bean id="car" class="com.test.factorybean.CarFactoryBean" carInfo="超級跑車,400,100000" />

當調用getBean("car")時,Spring經過反射機制發現CarBeanFactory實現了FactoryBean的接口,這時Spring容器就調用接口方法CarFactoryBean#getObject()方法返回。若是但願獲取CarFactoryBean的實例,則須要在使用getBean(beanName)方法時在beanName前顯示的加上"&"前綴,例如getBean("&car")。

 

應用場景

FactoryBean 一般是用來建立比較複雜的bean,通常的bean 直接用xml配置便可,但若是一個bean的建立過程當中涉及到不少其餘的bean 和複雜的邏輯,用xml配置比較困難,這時能夠考慮用FactoryBean

應用案例

若是你們有看過Mybatis的SessionFactoryBean和Activiti初始化引擎的ProcessEngineFactoryBean兩個類就應該瞭解FactoryBean的具體使用。

不少開源項目在集成Spring 時都使用到FactoryBean,好比 MyBatis3 提供 mybatis-spring項目中的 org.mybatis.spring.SqlSessionFactoryBean

    <bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="trade" />
        <property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" />
    </bean>

org.mybatis.spring.SqlSessionFactoryBean 以下:

package org.mybatis.spring;

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
        ......
}

FactoryBean的功能貌似更像是一種代理,有一種場景是,咱們使用一個通用的類來在xml文件中註冊bean,咱們但願經過該通用bean產生一個咱們但願的bean,而這個需求FactoryBean就能夠辦到,你只須要攔截你須要代理的bean,而後轉換成你但願的bean再註冊。一個應用場景就是Rpc服務器端的bean註冊,以及Rpc客戶端的服務調用,均可以經過一個第三方bean來產生咱們真正須要的bean。

當xml的配置文件中<bean>的class屬性配置的實現類是FactoryBean時(以下面配置文件"bean id="car"),經過 getBean()方法返回的不是FactoryBean自己,而是FactoryBean.getObject()方法所返回的對象,至關於FactoryBean.getObject()代理了getBean()方法。例如:若是使用傳統方式配置下面Car的<bean>時,Car的每一個屬性分別對應一個<property>元素標籤。

public   class  Car  {    
       private   int maxSpeed ;    
       private  String brand ;    
       private   double price ;    
      //get/set方法  
}

對應Car在xml中的配置:

<bean id="car" class="com.dxz.demo.Car">
    <property name="maxSpeed">
        <value>100</value>
    </property>
    <property name="brand">
        <value>紅旗</value>
    </property>
    <property name="price">
        <value>999999</value>
    </property>
</bean>

若是用FactoryBean的方式實現就會靈活一些,下例經過逗號分割符的方式一次性地爲Car的全部屬性指定配置值:

public class CarFactoryBean implements FactoryBean<Car>  {    
    private String carInfo ;    
    public Car getObject() throws  Exception  {    
        Car car =  new  Car() ;    
        String []  infos =  carInfo .split ( "," ) ;    
        car.setBrand ( infos [ 0 ]) ;    
        car.setMaxSpeed ( Integer. valueOf ( infos [ 1 ])) ;    
        car.setPrice ( Double. valueOf ( infos [ 2 ])) ;    
        return  car;    
    }    
    public  Class<Car> getObjectType()   {    
        return  Car. class ;    
    }    
    public   boolean  isSingleton()   {    
        return   false ;    
    }    
    public  String getCarInfo()   {    
        return   this.carInfo ;    
    }    
    
    // 接受逗號分割符設置屬性信息    
    public   void  setCarInfo( String carInfo )   {    
        this.carInfocarInfo  = carInfo;    
    }    
}    

有了這個CarFactoryBean後,就能夠在配置文件中使用下面這種自定義的配置方式配置Car Bean了:

<bean id="car" class="com.dxz.demo.CarFactoryBean" carInfo="超級跑車,400,2000000"/> 

當調用getBean("car") 時,Spring經過反射機制發現CarFactoryBean實現了FactoryBean的接口,這時Spring容器就調用接口方法CarFactoryBean.getObject()方法返回。若是但願獲取CarFactoryBean的實例,則須要在使用getBean(beanName) 方法時在beanName前顯示的加上 "&" 前綴,例如getBean("&car")。(當使用ApplicationContext的getBean()方法獲取FactoryBean實例自己而不是它所產生的bean,則要使用&符號+id。好比,現有FactoryBean,它有id,在容器上調用getBean("myBean")將返回FactoryBean所產生的bean,調用getBean("&myBean")將返回FactoryBean它自己的實例。) 

相關文章
相關標籤/搜索