1. 程式人生 > >【spring】BeanFactory原始碼+例項(下)FactoryBean+依賴注入

【spring】BeanFactory原始碼+例項(下)FactoryBean+依賴注入

前言:

    原始碼+例項:你、值得擁有

3. FactoryBean介面

實現了FactoryBean介面的bean是一類叫做factory的bean。其特點是,spring會在使用getBean()呼叫獲得該bean時,會自動呼叫該bean的getObject()方法,所以返回的不是factory這個bean,而是這個bean.getOjbect()方法的返回值

public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

典型的例子有spring與mybatis的結合:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="configLocation" value="classpath:config/mybatis-config-master.xml" />
      <property name="mapperLocations" value="classpath*:config/mappers/master/**/*.xml" />
    </bean>

我們看上面該bean,因為實現了FactoryBean介面,所以返回的不是 SqlSessionFactoryBean 的例項,而是她的 SqlSessionFactoryBean.getObject() 的返回值:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

  private static final Log logger = LogFactory.getLog(SqlSessionFactoryBean.class);

  private Resource configLocation;

  private Resource[] mapperLocations;

  private DataSource dataSource;
  
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

其實他是一個專門生產 sqlSessionFactory 的工廠,所以才叫 SqlSessionFactoryBean。 而SqlSessionFactory又是生產SqlSession的工廠。

還有spring與ibatis的結合:

  <!-- Spring提供的iBatis的SqlMap配置 -->
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="classpath:sqlmap/sqlmap-config.xml" />
        <property name="dataSource" ref="dataSource" />
    </bean>
SqlMapClientFactoryBean 返回的是 getObject() 中返回的 sqlMapClient, 而不是 SqlMapClientFactoryBean 自己的例項
public class SqlMapClientFactoryBean implements FactoryBean<SqlMapClient>, InitializingBean {
    private Resource[] configLocations;
    private Resource[] mappingLocations;
    private Properties sqlMapClientProperties;
    private DataSource dataSource;
    private boolean useTransactionAwareDataSource = true;
    private Class transactionConfigClass = ExternalTransactionConfig.class;
    private Properties transactionConfigProperties;
    private LobHandler lobHandler;
    private SqlMapClient sqlMapClient;
    public SqlMapClient getObject() {
        return this.sqlMapClient;
    }

4. 依賴注入(DI)

1) 依賴注入的方式分為建構函式注入和setter方法注入:

 建構函式注入:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
//對於非簡單引數,需要使用ref
    <constructor-arg index="1" ref="bar"/>
</bean>
<bean id="bar" class="x.y.Bar"/>

setter方法注入:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="configLocation" value="classpath:config/mybatis-config.xml" />
      <property name="mapperLocations" value="classpath*:config/mappers/**/*.xml" />
    </bean>

2)集合等複雜型別的注入:

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">[email protected]</prop>
            <prop key="support">[email protected]</prop>
            <prop key="development">[email protected]</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

也很簡單,list屬性就是 <list>裡面包含<value>或者<ref>或者<bean>, set也類似。map是<map>裡面包含<entry>這個也好理解,因為map的實現就是使用內部類Entry來儲存key和value. Properties是 <props>裡面包含<prop>.

5. <bean> 元素可以配置的屬性:

<bean> 除了 id 和 class 屬性之外,還有一些可選的屬性:

1) scope屬性

預設<bean> 的 scope就是 singleton="true", springmvc和struts2的重要區別之一就是spring的controll是單例的,而struts2的action是:scope="prototype" ,還有 scope="request" , scope="session",scope="globalSession"(僅用於portlet)

     1.singleton:ioc容器只有一個共享bean例項(儲存在單例快取中),對該bean的後繼請求和引用都返回被快取的物件例項;設計模式裡的單例:一個classloader中只有一個class存在,不一樣滴;

      讓Spring容器釋放被singleton作用域bean佔用資源的一種可行方式是,通過使用 bean的後置處理器,該處理器持有要被清除的bean的引用

<bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/>
或者
<bean id="role" class="spring.chapter2.maryGame.Role" singleton="true"/>

    struts2的Action不是執行緒安全的,要求在多執行緒環境下必須是一個執行緒對應一個獨立的例項,不能使用 singleton。

   只要是帶資料成員變數的類,為了防止多個執行緒混用資料,就不能使用singleton。對於我們用到的Service、Dao,之所以用了singleton,就是因為他們沒有用到資料成員變數,如果誰的 Service需要資料成員變數,請設定singleton=false。 
    有狀態的bean都使用Prototype作用域,而對無狀態的bean則應該使用singleton作用域。 

     2. prototype:每一次請求(將其注入到另一個bean中或程式呼叫容器getBean)都產生一個新的bean例項,相當與一個new操作;

       作用域:spring容器在初始化 配置 裝飾 或裝配完一個prototype例項後,將其交給客戶端管理便不再過問;

        不管何種作用域,容器都會呼叫所有物件的初始化生命週期回撥方法,而對prototype而言,任何配置好的析構生命週期回撥方法都將不會被呼叫。 清除prototype作用域的物件並釋放任何prototype bean所持有的昂貴資源,都是客戶端程式碼的職責。

      3.  request針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效,配置例項:

     request、session、global session使用的時候首先要在初始化web的web.xml中做如下配置:

如果你使用的是Servlet 2.4及以上的web容器,那麼你僅需要在web應用的XML宣告檔案web.xml中增加下述ContextListener即可: 

<web-app>
   ...
  <listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>
   ...
</web-app>

如果是Servlet2.4以前的web容器,那麼你要使用一個javax.servlet.Filter的實現: 

<web-app>
 ..
 <filter> 
    <filter-name>requestContextFilter</filter-name> 
    <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
 </filter> 
 <filter-mapping> 
    <filter-name>requestContextFilter</filter-name> 
    <url-pattern>/*</url-pattern>
 </filter-mapping>
   ...
</web-app>

接著既可以配置bean的作用域了:

<bean id="role" class="spring.chapter2.maryGame.Role" scope="request"/>

4、session每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP session內有效,配置例項:

和request配置例項的前提一樣,配置好web啟動檔案就可以如下配置: 

<bean id="role" class="spring.chapter2.maryGame.Role" scope="session"/>

5、global session

      類似於標準的HTTP Session作用域,僅在基於portlet的web應用中才有意義。Portlet規範定義了全域性Session的概念,它被所有構成某個 portlet web應用的各種不同的portlet所共享。在global session作用域中定義的bean被限定於全域性portlet Session的生命週期範圍內。如果你在web中使用global session作用域來標識bean,那麼web會自動當成session型別來使用。

配置例項:

和request配置例項的前提一樣,配置好web啟動檔案就可以如下配置: 

<bean id="role" class="spring.chapter2.maryGame.Role" scope="global session"/>

6、自定義:在spring2.0中作用域是可以任意擴充套件的,你可以自定義作用域,甚至你也可以重新定義已有的作用域(但是你不能覆蓋singleton和prototype),spring的作用域由介面org.springframework.beans.factory.con**.Scope來定義,自定義自己的作用域只要實現該介面即可

2)abstract屬性,是否是抽象的bean:

<bean id="baseDAO" abstract="true">
        <property name="dataSource" ref="dataSource" />
        <property name="sqlMapClient" ref="sqlMapClient" />
    </bean>    
    <bean id="collectionDAO" class="net.minisns.dal.dao.CollectionDAOImpl" parent="baseDAO" />
    <bean id="commentDAO" class="net.minisns.dal.dao.CommentDAOImpl" parent="baseDAO" />

3)depends-on 依賴於某個bean,其必須先初始化:<bean id="xxx" class="xxx" depends-on="refbean" />

4)lazy-init="true" 是否延遲初始化,預設為 false

5) dependency-check 是否對bean依賴的其它bean進行檢查,預設值為 none,可取值有:none, simple, object, all等

6)factory-method 和 factory-bean用於靜態工廠和非靜態工廠:

<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>
<bean id="barFactory" class="...NonStaticBarInterfaceFactory"/> 
<bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>

7)init-method, destory-method 指定bean初始化和死亡時呼叫的方法,常用於 dataSource的連線池的配置

8) lookup-method 方法注入:

<bean id="newsBean" class="..xxx" singleton="false"> 
<bean id="mockPersister" class="..impl.MockNewsPersister">
  <lookup-method name="getNewsBean" bean="newsBean"/> 
</bean>  

表示 mockPersister 有一個依賴屬性 newsBean,該屬性的每次注入都是通過呼叫newsBean.getNewsBean() 方法獲得的。

9) autowire 是否啟用自動裝配依賴,預設為 no, 其它取值還有:byName, byType, constructor