【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