Spring 管理數據源
不管通過何種持久化技術,都必須通過數據連接訪問數據庫,在Spring中,數據連接是通過數據源獲得的。在以往的應用中,數據源一般是Web應用服務器提供的。在Spring中,你不但可以通過JNDI獲取應用服務器的數據源,也可以直接在Spring容器中配置數據源,此外,你還可以通過代碼的方式創建一個數據源,以便進行無依賴的單元測試配置一個數據源。
Spring在第三方依賴包中包含了兩個數據源的實現類包,其一是Apache的DBCP,其二是 C3P0。可以在Spring配置文件中利用這兩者中任何一個配置數據源。
1. Spring 配置DataSource 的三種方式
1.1. 使用Spring自帶的DriverManagerDataSource
DriverManagerDataSource建立連接是只要有連接就新建一個connection,根本沒有連接池的作用。 這里的引用屬性是從配置文件jdbc.properties 中讀取的。
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/jdbc.properties"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
說明:由於其沒有使用連接池,故少在項目中用到。
1.2. 使用數據源
這是一種推薦使用的數據源配置方式,它真正使用了連接池技術。Spring在第三方依賴包中包含了兩個數據源的實現類包,其一是Apache的DBCP,其二是 C3P0,這里使用了DBCP。
1.2.1 DBCP數據源:org.apache.commons.dbcp.BasicDataSource
DBCP是一個依賴 Jakarta commons-pool對象池機制的數據庫連接池,要在Spring中使用DBCP連接池,需要引入spring-framework-2.0-ml\lob\jakarta-commons文件夾中的commons-collections.jar、commons-dbcp.jar和commons-pool.jar。下面是DBCP配置片段:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/jdbc.properties"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
說明:BasicDataSource提供了close()方法關閉數據源,所以必須設定destroy-method=”close”屬性, 以便Spring容器關閉時,數據源能夠正常關閉。除以上必須的數據源屬性外,還有一些常用的屬性:
defaultAutoCommit:設置從數據源中返回的連接是否采用自動提交機制,默認值為 true;
defaultReadOnly:設置數據源是否僅能執行只讀操作, 默認值為 false;
maxActive:最大連接數據庫連接數,設置為0時,表示沒有限制;
maxIdle:最大等待連接中的數量,設置為0時,表示沒有限制;
maxWait:最大等待秒數,單位為毫秒, 超過時間會報出錯誤信息;
validationQuery:用於驗證連接是否成功的查詢SQL語句,SQL語句必須至少要返回一行數據, 如你可以簡單地設置為:“select count(*) from user”;
removeAbandoned:是否自我中斷,默認是 false ;
removeAbandonedTimeout:幾秒后數據連接會自動斷開,在removeAbandoned為true,提供該值;
logAbandoned:是否記錄中斷事件, 默認為 false;
1.2.2 C3P0數據源:com.mchange.v2.c3p0.ComboPooledDataSource
C3P0是一個開放源代碼的JDBC數據源實現項目,它在lib目錄中與Hibernate一起發布,實現了JDBC3和JDBC2擴展規范說明的 Connection 和Statement 池。C3P0類包位於/lib/c3p0/c3p0-0.9.0.4.jar。下面是C3P0配置片段:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value=" oracle.jdbc.driver.OracleDriver "/>
<property name="jdbcUrl" value=" jdbc:oracle:thin:@localhost:1521:ora9i "/>
<property name="user" value="admin"/>
<property name="password" value="1234"/>
</bean>
說明:ComboPooledDataSource和BasicDataSource一樣提供了一個用於關閉數據源的close()方法,這樣我們就可以保證Spring容器關閉時數據源能夠成功釋放。
C3P0擁有比DBCP更豐富的配置屬性,通過這些屬性,可以對數據源進行各種有效的控制:
acquireIncrement:當連接池中的連接用完時,C3P0一次性創建新連接的數目;
acquireRetryAttempts:定義在從數據庫獲取新連接失敗后重復嘗試獲取的次數,默認為30;
acquireRetryDelay:兩次連接中間隔時間,單位毫秒,默認為1000;
autoCommitOnClose:連接關閉時默認將所有未提交的操作回滾。默認為false;
automaticTestTable: C3P0將建一張名為Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個參數,那么屬性preferredTestQuery將被忽略。你 不能在這張Test表上進行任何操作,它將中為C3P0測試所用,默認為null;
breakAfterAcquireFailure:獲取連接失敗將會引起所有等待獲取連接的線程拋出異常。但是數據源仍有效保留,並在下次調 用getConnection()的時候繼續嘗試獲取連接。如果設為true,那么在嘗試獲取連接失敗后該數據源將申明已斷開並永久關閉。默認為 false;
checkoutTimeout:當連接池用完時客戶端調用getConnection()后等待獲取新連接的時間,超時后將拋出SQLException,如設為0則無限期等待。單位毫秒,默認為0;
connectionTesterClassName: 通過實現ConnectionTester或QueryConnectionTester的類來測試連接,類名需設置為全限定名。默認為 com.mchange.v2.C3P0.impl.DefaultConnectionTester;
idleConnectionTestPeriod:隔多少秒檢查所有連接池中的空閑連接,默認為0表示不檢查;
initialPoolSize:初始化時創建的連接數,應在minPoolSize與maxPoolSize之間取值。默認為3;
maxIdleTime:最大空閑時間,超過空閑時間的連接將被丟棄。為0或負數則永不丟棄。默認為0;
maxPoolSize:連接池中保留的最大連接數。默認為15;
maxStatements:JDBC的標准參數,用以控制數據源內加載的PreparedStatement數量。但由於預緩存的Statement屬 於單個Connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素,如果maxStatements與 maxStatementsPerConnection均為0,則緩存被關閉。默認為0;
maxStatementsPerConnection:連接池內單個連接所擁有的最大緩存Statement數。默認為0;
numHelperThreads:C3P0是異步操作的,緩慢的JDBC操作通過幫助進程完成。擴展這些操作可以有效的提升性能,通過多線程實現多個操作同時被執行。默認為3;
preferredTestQuery:定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個參數能顯著提高測試速度。測試的表必須在初始數據源的時候就存在。默認為null;
propertyCycle: 用戶修改系統配置參數執行前最多等待的秒數。默認為300;
testConnectionOnCheckout:因性能消耗大請只在需要的時候使用它。如果設為true那么在每個connection提交的時候都 將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
等方法來提升連接測試的性能。默認為false;
testConnectionOnCheckin:如果設為true那么在取得連接的同時將校驗連接的有效性。默認為false。
1.2.3 其他數據源
oracle.jdbc.pool.OracleDataSource:使用Oracle自帶的數據源oracle.jdbc.pool.OracleDataSource,Spring使用完這個數據源提供的數據連接后並不進行關閉操作,需要顯式的關閉連接。
org.logicalcobwebs.proxool.ProxoolDataSource: Proxool是一種Java數據庫連接池技術。sourceforge下的一個開源項目,這個項目提供一個健壯、易用的連接池,最為關鍵的是這個連接池提供監控的功能,方便易用,便於發現連接泄漏的情況。目前是和DBCP以及C3P0一起,最為常見的三種JDBC連接池技術。
com.jolbox.bonecp.BoneCPDataSource :bonecp數據連接池, BoneCP最大的特點就是效率,BoneCP號稱是目前市面上最快的Java連接池,從官方的評測來看其效率遠遠超越了其它同類的Java連 接池產品
1.3. 使用Tomcat提供的JNDI
說明:JndiObjectFactoryBean 能夠通過JNDI獲取DataSource
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/roseindiaDB_local</value>
</property>
</bean>
總結:這種方式需要在web server中配置數據源,不方便於部署。
1.4. 總結
3種方式中的第一種沒有使用連接池,故少在項目中用到,第三種方式需要在web server中配置數據源,不方便於部署,推薦使用第二種方式進行數據源的配置。
2. Spring持久化的設計思想
2.1. JDBC基本的編程模型
由於任何持久化層的封裝實際上都是對java.sql.Connection等相關對象的操作,一個典型的數據操作的流程如下:
在實際使用spring和ibatis的時候,我們都沒有感覺到上面的流程,其實spring已經對外已經屏蔽了上述的操作,讓我們更關注業務邏輯功能
2.2. 開啟事務
在開啟事務的時候,我們需要初始化事務上下文信息,以便在業務完成之后,需要知道事務的狀態,以便進行后續的處理,這個上下文信息可以保存在 ThreadLocal里面,包括是否已經開啟事務,事務的超時時間,隔離級別,傳播級別,是否設置為回滾。這個信息對應用來說是透明的,但是提供給使用者編程接口,以便告知業務結束的時候是提交事務還是回滾事務。
2.3. 獲取連接
首先來看看spring如何獲取數據庫連接的,對於正常情況來看,獲取連接直接調用DataSource.getConnection()就可以了,我們在自己實現的時候也肯定會這么做,但是需要考慮兩種情況(這里面先不引入事務的傳播屬性):
1 還沒有獲取過連接,這是第一次獲取連接
2 已經獲取過連接,不是第一次獲取連接,可以復用連接
解決獲取數據庫連接的關鍵問題就是如何判斷是否已經可用的連接,而不需要開啟新的數據庫連接,同時由於數據庫連接需要給后續的業務操作復用,如何保持這個連接,並且透明的傳遞給后續流程。對於一個簡單的實現就是使用線程上下文變量ThrealLocal來解決以上兩個問題。
具體的實現是:在獲取數據庫連接的時候,判斷當前線程線程變量里面是否已經存在相關連接,如果不存在,就創新一個新的連接,如果存在,就直接獲取其對應的連接。在第一次獲取到數據庫連接的時候,我們還需要做一些特殊處理,就是設置自動提交為false。在業務活動結束的時候在進行提交或者回滾。這個時候就是要調用connection.setAutoCommit(false)方法。
2.4. 執行sql
這一部分和業務邏輯相關,通過對外提供一些編程接口,可以讓業務決定業務完成之后如何處理事務,比較簡單的就是設置事務狀態。
2.5. 提交事務
在開啟事務的時候,事務上下文信息已經保存在線程變量里面了,可以根據事務上下文的信息,來決定是否是提交還是回滾。其實就是調用數據庫連接Connection.commit 和 Connection.rollback 方法。然后需要清空線程變量中的事務上下文信息。相當於結束了當前的事務。
2.6. 關閉連接
關閉連接相對比較簡單,由於當前線程變量保存了連接信息,只需要獲取連接之后,調用connection.close方法即可,接着清空線程變量的數據庫連接信息。
上面幾個流程是一個簡單的事務處理流程,在spring中都有對應的實現,見TransactionTemplate.execute方法。
2.7. 總結
當一個持久化操作結束時,數據庫連接就應該關閉,而spring 封裝了其他操作,我們只需要關注 “執行sql”這一步。
3. Spring的底層類核心代碼分析
3.1. 使用JdbcTemplate 類
JdbcTemplate類使用DataSource得到一個數據庫連接。然后,他調用StatementCreator實例創建要執行的語句。下一步,他調用StatementCallBack完成。
一旦StatementCallBack返回結果,JdbcTemplate類完成所有必要清理工作關閉連接。如果StatementCreator或StatementCallBack拋出異常,JdbcTemplate類會捕獲他們,並轉換為Spring數據訪問異常。
看一個JdbcTemplate里面的比較核心的一個方法:
- //-------------------------------------------------------------------------
- // Methods dealing with prepared statements
- //-------------------------------------------------------------------------
- public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action)
- throws DataAccessException {
- Assert.notNull(psc, "PreparedStatementCreator must not be null");
- Assert.notNull(action, "Callback object must not be null");
- if (logger.isDebugEnabled()) {
- String sql = getSql(psc);
- logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
- }
- Connection con = DataSourceUtils.getConnection(getDataSource());
- PreparedStatement ps = null;
- try {
- Connection conToUse = con;
- if (this.nativeJdbcExtractor != null &&
- this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
- conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
- }
- ps = psc.createPreparedStatement(conToUse);
- applyStatementSettings(ps);
- PreparedStatement psToUse = ps;
- if (this.nativeJdbcExtractor != null) {
- psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
- }
- Object result = action.doInPreparedStatement(psToUse);
- handleWarnings(ps);
- return result;
- }
- catch (SQLException ex) {
- // Release Connection early, to avoid potential connection pool deadlock
- // in the case when the exception translator hasn't been initialized yet.
- if (psc instanceof ParameterDisposer) {
- ((ParameterDisposer) psc).cleanupParameters();
- }
- String sql = getSql(psc);
- psc = null;
- JdbcUtils.closeStatement(ps);
- ps = null;
- DataSourceUtils.releaseConnection(con, getDataSource());
- con = null;
- throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
- }
- finally {
- if (psc instanceof ParameterDisposer) {
- ((ParameterDisposer) psc).cleanupParameters();
- }
- JdbcUtils.closeStatement(ps);
- DataSourceUtils.releaseConnection(con, getDataSource());
- }
- }
顯然,我們在finally里面看到了關閉調用,而且從代碼可以看出 JdbcTemplate是調用了DataSourceUtils的。在 Spring文檔中要求盡量首先使用JdbcTemplate,其次是用DataSourceUtils來獲取和釋放連接
再看看這個關閉調用方法內部:
- /**
- * Close the given Connection, obtained from the given DataSource,
- * if it is not managed externally (that is, not bound to the thread).
- * @param con the Connection to close if necessary
- * (if this is <code>null</code>, the call will be ignored)
- * @param dataSource the DataSource that the Connection was obtained from
- * (may be <code>null</code>)
- * @see #getConnection
- */
- public static void releaseConnection(Connection con, DataSource dataSource) {
- try {
- doReleaseConnection(con, dataSource);
- }
- catch (SQLException ex) {
- logger.debug("Could not close JDBC Connection", ex);
- }
- catch (Throwable ex) {
- logger.debug("Unexpected exception on closing JDBC Connection", ex);
- }
- }
- /**
- * Actually close the given Connection, obtained from the given DataSource.
- * Same as {@link #releaseConnection}, but throwing the original SQLException.
- * <p>Directly accessed by {@link TransactionAwareDataSourceProxy}.
- * @param con the Connection to close if necessary
- * (if this is <code>null</code>, the call will be ignored)
- * @param dataSource the DataSource that the Connection was obtained from
- * (may be <code>null</code>)
- * @throws SQLException if thrown by JDBC methods
- * @see #doGetConnection
- */
- public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {
- if (con == null) {
- return;
- }
- if (dataSource != null) {
- ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
- if (conHolder != null && connectionEquals(conHolder, con)) {
- // It's the transactional Connection: Don't close it.
- conHolder.released();
- return;
- }
- }
- // Leave the Connection open only if the DataSource is our
- // special SmartDataSoruce and it wants the Connection left open.
- if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
- logger.debug("Returning JDBC Connection to DataSource");
- con.close();
- }
- }
主要下面這幾行代碼:
- // Leave the Connection open only if the DataSource is our
- // special SmartDataSoruce and it wants the Connection left open.
- if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
- logger.debug("Returning JDBC Connection to DataSource");
- con.close();
- }
可以看到大部分情況下是自動關閉,除非你使用的是SmartDataSource,且SmartDataSource指定了允許關閉。
3.2. 使用DataSourceUtils類
使用Spring 對DataSource進行事務管理的關鍵在於ConnectionHolder和TransactionSynchronizationManager。
0.先從TransactionSynchronizationManager中嘗試獲取連接
1.如果前一步失敗則在每個線程上,對每個DataSouce只創建一個Connection
2.這個Connection用ConnectionHolder包裝起來,由TransactionSynchronizationManager管理
3.再次請求同一個連接的時候,從TransactionSynchronizationManager返回已經創建的ConnectionHolder,然后調用ConnectionHolder的request將引用計數+1
4.釋放連接時要調用ConnectionHolder的released,將引用計數-1
5.當事物完成后,將ConnectionHolder從TransactionSynchronizationManager中解除。當誰都不用,這個連接被close
以上所有都是可以調用DataSourceUtils化簡代碼。
而JdbcTemplate又是調用DataSourceUtils的。所以在Spring文檔中要求盡量首先使用JdbcTemplate,其次是用DataSourceUtils來獲取和釋放連接。至於TransactionAwareDataSourceProxy,那是下策的下策。不過可以將Spring事務管理和遺留代碼無縫集成。
如使用Spring的事務管理,但是又不想用JdbcTemplate,那么可以考慮TransactionAwareDataSourceProxy。這個類是原來DataSource的代理。
其次,想使用Spring事物,又不想對Spring進行依賴是不可能的。與其試圖自己模擬DataSourceUtils,不如直接使用現成的
此外,如果使用DataSourceUtils類,則得到連接方法DataSourceUtils.getConnection()要與釋放連接方法DataSourceUtils.releaseConnection()配合使用。
3.3. 總結
由底層代碼可以看出,使用JdbcTemplate 來連接數據庫不需要手動關閉連接,但是,有時還需要自己額外的拿到conn進行操作,如下: jdbcTemplate.getDataSource().getConnection() ,那么,就需要關閉連接了
而如果采用其他底層方法的話,需要使用對應的釋放連接。
4. Spring的數據源實現類
4.1. 使用JDBC 模板來實現
Spring JDBC實現模板設計模式,這意味着,代碼中的重復的復雜的任務部分是在模板類中實現的。JdbcTemplate是core包的核心類。它替我們完成了資源的創建以及釋放工作,從而簡化了我們對JDBC的使用。它還可以幫助我們避免一些常見的錯誤,比如忘記關閉數據庫連接。JdbcTemplate將完成JDBC核心處理流程,比如SQL語句的創建、執行,而把SQL語句的生成以及查詢結果的提取工作留給我們的應用代碼。它可以完成SQL查詢、更新以及調用存儲過程,可以對ResultSet進行遍歷並加以提取。它還可以捕獲JDBC異常並將其轉換成org.springframework.dao包中定義的,通用的,信息更豐富的異常。
SpringJdbcTemplate的工作流程如下:
(1).配置數據源:
在applicationContext.xml中設置好數據源。
Spring中,將管理數據庫連接的數據源當作普通Java Bean一樣在Spring IoC容器中管理,當應用使用數據源時Spring IoC容器負責初始化數據源。
(2).將數據源注入JdbcTemplate:
JdbcTemplate中dataSource屬性用於注入配置的數據源,Spring IoC容器通過依賴注入將配置的數據源注入到Spring對Jdbc操作的封裝類JdbcTemplate中。
(3).應用中使用JdbcTemplate:
注入了數據源的JdbcTemplate就可以在應用中使用了,應用中對數據源的增刪改查等操作都可以使用JdbcTemplate對外提供的方法操作數據庫。
實例參考:http://blog.csdn.net/chjttony/article/details/6404089
4.2. 使用DriverManagerDataSource類來實現
Spring本身也提供了一個簡單的數據源實現類DriverManagerDataSource ,它位於org.springframework.jdbc.datasource包中。這個類實現了javax.sql.DataSource接口,但 它並沒有提供池化連接的機制,每次調用getConnection()獲取新連接時,只是簡單地創建一個新的連接。因此,這個數據源類比較適合在單元測試 或簡單的獨立應用中使用,因為它不需要額外的依賴類。
說明:
DriverManagerDataSource :簡單封裝了DriverManager獲取數據庫連接;通過DriverManager的getConnection方法獲取數據庫連接;
SingleConnectionDataSource :內部封裝了一個連接,該連接使用后不會關閉,且不能在多線程環境中使用,一般用於測試;
LazyConnectionDataSourceProxy :包裝一個DataSource,用於延遲獲取數據庫連接,只有在真正創建Statement等時才獲取連接,因此再說實際項目中最后使用該代理包裝原始DataSource從而使得只有在真正需要連接時才去獲取。
DataSourceUtils: Spring JDBC抽象框架內部都是通過它的getConnection(DataSource dataSource)方法獲取數據庫連接,releaseConnection(Connection con, DataSource dataSource) 用於釋放數據庫連接,DataSourceUtils用於支持Spring管理事務,只有使用DataSourceUtils獲取的連接才具有Spring管理事務。
4.3. 使用spring 的getSession()
Spring與hibernate 集成的環境里,getSession獲取的是沒有經過Spring包裝的原始的session,使用完之后不會自動關閉,需要調用手動調用close方法,或者releaseSession(session);而getHibernateTemplate()方法是經過spring封裝的,例如添加相應的聲明式事務管理,由spring管理相應的連接,所以用getHibernateTemplate().find這些方法之后,spring會幫你控制數據庫連接的關閉。
在實際的使用過程中發現的確getHibernateTemplate()比getSession()方法要好很多,但是有些方法在getHibernateTemplate()並沒有提供,這時我們用HibernateCallback回調的方法管理數據庫.
例如如下代碼:
return this.getHibernateTemplate().executeFind(new HibernateCallback(){
public List doInHibernate(Session session) throws HibernateException, SQLException {
Query query=session.createQuery(hqlString);
query.setFirstResult(startRow1);
query.setMaxResults(pageSize1);
return query.list();
}
});
但是,如果是配置了OpenSessionInView模式,則getSession拿到的session,Spring就會負責關閉了!
5. Spring中bean的scope 詳解
在spring2.0之前bean只有2種作用域即:singleton(單例)、non-singleton(也稱 prototype), Spring2.0以后,增加了session、request、global session三種專用於Web應用程序上下文的Bean。因此,默認情況下Spring2.0現在有五種類型的Bean。當然,Spring2.0對 Bean的類型的設計進行了重構,並設計出靈活的Bean類型支持,理論上可以有無數多種類型的Bean,用戶可以根據自己的需要,增加新的Bean類 型,滿足實際應用需求。
<bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/> 這里的scope就是用來配置spring bean的作用域,它標識bean的作用域。
5.1. singleton作用域(scope 默認值)
當一個bean的作用域設置為singleton, 那么Spring IOC容器中只會存在一個共享的bean實例,並且所有對bean的請求,只要id與該bean定義相匹配,則只會返回bean的同一實例。換言之,當把 一個bean定義設置為singleton作用域時,Spring IOC容器只會創建該bean定義的唯一實例。這個單一實例會被存儲到單例緩存(singleton cache)中,並且所有針對該bean的后續請求和引用都 將返回被緩存的對象實例,這里要注意的是singleton作用域和GOF設計模式中的單例是完全不同的,單例設計模式表示一個ClassLoader中 只有一個class存在,而這里的singleton則表示一個容器對應一個bean,也就是說當一個bean被標識為singleton時 候,spring的IOC容器中只會存在一個該bean。
配置實例:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/>
或者
<bean id="role" class="spring.chapter2.maryGame.Role" singleton="true"/>
5.2. prototype
prototype作用域部署的bean,每一次請求(將其注入到另一個bean中,或者以程序的方式調用容器的getBean()方法)都會產生一個新的bean實例,相當與一個new的操作,對於prototype作用域的bean,有一點非常重要,那就是Spring不能對一個prototype bean的整個生命周期負責,容器在初始化、配置、裝飾或者是裝配完一個prototype實例后,將它交給客戶端,隨后就對該prototype實例不聞不問了。不管何種作用域,容器都會調用所有對象的初始化生命周期回調方法,而對prototype而言,任何配置好的析構生命周期回調方法都將不會被調用。 清除prototype作用域的對象並釋放任何prototype bean所持有的昂貴資源,都是客戶端代碼的職責。(讓Spring容器釋放被singleton作用域bean占用資源的一種可行方式是,通過使用 bean的后置處理器,該處理器持有要被清除的bean的引用。)
配置實例:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="prototype"/>
或者
<beanid="role" class="spring.chapter2.maryGame.Role" singleton="false"/>
5.3. request
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"/>
5.4. session
session作用域表示該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP session內有效,配置實例:
配置實例:
和request配置實例的前提一樣,配置好web啟動文件就可以如下配置:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="session"/>
5.5. global session
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"/>
5.6. 自定義bean裝配作用域
在spring2.0中作用域是可以任意擴展的,你可以自定義作用域,甚至你也可以重新定義已有的作用域(但是你不能覆蓋singleton和 prototype),spring的作用域由接口org.springframework.beans.factory.config.Scope來定 義,自定義自己的作用域只要實現該接口即可,下面給個實例:
我們建立一個線程的scope,該scope在表示一個線程中有效,代碼如下:
publicclass MyScope implements Scope {
privatefinal ThreadLocal threadScope = new ThreadLocal() {
protected Object initialValue() {
returnnew HashMap();
}
};
public Object get(String name, ObjectFactory objectFactory) {
Map scope = (Map) threadScope.get();
Object object = scope.get(name);
if(object==null) {
object = objectFactory.getObject();
scope.put(name, object);
}
return object;
}
public Object remove(String name) {
Map scope = (Map) threadScope.get();
return scope.remove(name);
}
publicvoid registerDestructionCallback(String name, Runnable callback) {
}
public String getConversationId() {
// TODO Auto-generated method stub
returnnull;
}
}
6. 使用Spring操作數據庫需要顯式關閉數據庫連接的情況
1、 配置數據源時,需要添加 destroy-method 屬性,DBCP和C3PO 都提供了close()方法關閉數據源,所以必須設定destroy-method=”close” 屬性, 以便Spring容器關閉時,數據源能夠正常關閉
2、 如果使用spring 中 bean 的作用域為 prototype ,需要在應用程序中 手動關閉數據庫
3、 使用JdbcTemplate 來連接數據庫不需要手動關閉連接,但是,有時還需要自己額外的拿到conn進行操作,如下: jdbcTemplate.getDataSource().getConnection() 那么,就需要關閉連接了
4、 直接使用DataSourceUtils.getConnection()方法得到連接,則需要使用DataSourceUtils.releaseConnection()方法 來釋放連接了
5、 若是使用的是比DataSourceUtils 更底層的代碼,則必須要顯式關閉連接了