Spring 管理數據源


Spring 管理數據源

 

不管通過何種持久化技術,都必須通過數據連接訪問數據庫,在Spring中,數據連接是通過數據源獲得的。在以往的應用中,數據源一般是Web應用服務器提供的。在Spring中,你不但可以通過JNDI獲取應用服務器的數據源,也可以直接在Spring容器中配置數據源,此外,你還可以通過代碼的方式創建一個數據源,以便進行無依賴的單元測試配置一個數據源。

    Spring在第三方依賴包中包含了兩個數據源的實現類包,其一是ApacheDBCP,其二是 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在第三方依賴包中包含了兩個數據源的實現類包,其一是ApacheDBCP,其二是 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.jarcommons-dbcp.jarcommons-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:幾秒后數據連接會自動斷開,在removeAbandonedtrue,提供該值;

logAbandoned:是否記錄中斷事件, 默認為 false

 

1.2.2  C3P0數據源:com.mchange.v2.c3p0.ComboPooledDataSource

C3P0是一個開放源代碼的JDBC數據源實現項目,它在lib目錄中與Hibernate一起發布,實現了JDBC3JDBC2擴展規范說明的 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>

 

 

說明:ComboPooledDataSourceBasicDataSource一樣提供了一個用於關閉數據源的close()方法,這樣我們就可以保證Spring容器關閉時數據源能夠成功釋放。

    C3P0擁有比DBCP更豐富的配置屬性,通過這些屬性,可以對數據源進行各種有效的控制:

    acquireIncrement:當連接池中的連接用完時,C3P0一次性創建新連接的數目;

    acquireRetryAttempts:定義在從數據庫獲取新連接失敗后重復嘗試獲取的次數,默認為30

    acquireRetryDelay:兩次連接中間隔時間,單位毫秒,默認為1000

    autoCommitOnClose:連接關閉時默認將所有未提交的操作回滾。默認為false

    automaticTestTableC3P0將建一張名為Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個參數,那么屬性preferredTestQuery將被忽略。你 不能在這張Test表上進行任何操作,它將中為C3P0測試所用,默認為null

    breakAfterAcquireFailure:獲取連接失敗將會引起所有等待獲取連接的線程拋出異常。但是數據源仍有效保留,並在下次調   用getConnection()的時候繼續嘗試獲取連接。如果設為true,那么在嘗試獲取連接失敗后該數據源將申明已斷開並永久關閉。默認為 false

    checkoutTimeout:當連接池用完時客戶端調用getConnection()后等待獲取新連接的時間,超時后將拋出SQLException,如設為0則無限期等待。單位毫秒,默認為0

    connectionTesterClassName: 通過實現ConnectionTesterQueryConnectionTester的類來測試連接,類名需設置為全限定名。默認為 com.mchange.v2.C3P0.impl.DefaultConnectionTester

    idleConnectionTestPeriod:隔多少秒檢查所有連接池中的空閑連接,默認為0表示不檢查;

    initialPoolSize:初始化時創建的連接數,應在minPoolSizemaxPoolSize之間取值。默認為3

    maxIdleTime:最大空閑時間,超過空閑時間的連接將被丟棄。為0或負數則永不丟棄。默認為0

    maxPoolSize:連接池中保留的最大連接數。默認為15

    maxStatementsJDBC的標准參數,用以控制數據源內加載的PreparedStatement數量。但由於預緩存的Statement屬 於單個Connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素,如果maxStatementsmaxStatementsPerConnection均為0,則緩存被關閉。默認為0

    maxStatementsPerConnection:連接池內單個連接所擁有的最大緩存Statement數。默認為0

    numHelperThreadsC3P0是異步操作的,緩慢的JDBC操作通過幫助進程完成。擴展這些操作可以有效的提升性能,通過多線程實現多個操作同時被執行。默認為3

    preferredTestQuery:定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個參數能顯著提高測試速度。測試的表必須在初始數據源的時候就存在。默認為null

    propertyCycle: 用戶修改系統配置參數執行前最多等待的秒數。默認為300

    testConnectionOnCheckout:因性能消耗大請只在需要的時候使用它。如果設為true那么在每個connection提交的時候都 將校驗其有效性。建議使用idleConnectionTestPeriodautomaticTestTable

等方法來提升連接測試的性能。默認為false

    testConnectionOnCheckin:如果設為true那么在取得連接的同時將校驗連接的有效性。默認為false

 

1.2.3  其他數據源

oracle.jdbc.pool.OracleDataSource:使用Oracle自帶的數據源oracle.jdbc.pool.OracleDataSourceSpring使用完這個數據源提供的數據連接后並不進行關閉操作,需要顯式的關閉連接。

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等相關對象的操作,一個典型的數據操作的流程如下:

 

在實際使用springibatis的時候,我們都沒有感覺到上面的流程,其實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類完成所有必要清理工作關閉連接。如果StatementCreatorStatementCallBack拋出異常,JdbcTemplate類會捕獲他們,並轉換為Spring數據訪問異常。

 

 

看一個JdbcTemplate里面的比較核心的一個方法:

  1. //-------------------------------------------------------------------------  
  2. // Methods dealing with prepared statements  
  3. //-------------------------------------------------------------------------  
  4. public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action)  
  5. throws DataAccessException {  
  6. Assert.notNull(psc, "PreparedStatementCreator must not be null");  
  7. Assert.notNull(action, "Callback object must not be null");  
  8. if (logger.isDebugEnabled()) {  
  9. String sql = getSql(psc);  
  10. logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));  
  11. }  
  12. Connection con = DataSourceUtils.getConnection(getDataSource());  
  13. PreparedStatement ps = null;  
  14. try {  
  15. Connection conToUse = con;  
  16. if (this.nativeJdbcExtractor != null &&  
  17. this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {  
  18. conToUse = this.nativeJdbcExtractor.getNativeConnection(con);  
  19. }  
  20. ps = psc.createPreparedStatement(conToUse);  
  21. applyStatementSettings(ps);  
  22. PreparedStatement psToUse = ps;  
  23. if (this.nativeJdbcExtractor != null) {  
  24. psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);  
  25. }  
  26. Object result = action.doInPreparedStatement(psToUse);  
  27. handleWarnings(ps);  
  28. return result;  
  29. }  
  30. catch (SQLException ex) {  
  31. // Release Connection early, to avoid potential connection pool deadlock  
  32. // in the case when the exception translator hasn't been initialized yet.  
  33. if (psc instanceof ParameterDisposer) {  
  34. ((ParameterDisposer) psc).cleanupParameters();  
  35. }  
  36. String sql = getSql(psc);  
  37. psc = null;  
  38. JdbcUtils.closeStatement(ps);  
  39. ps = null;  
  40. DataSourceUtils.releaseConnection(con, getDataSource());  
  41. con = null;  
  42. throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);  
  43. }  
  44. finally {  
  45. if (psc instanceof ParameterDisposer) {  
  46. ((ParameterDisposer) psc).cleanupParameters();  
  47. }  
  48. JdbcUtils.closeStatement(ps);  
  49. DataSourceUtils.releaseConnection(con, getDataSource());  
  50. }  
  51. }  

 

顯然,我們在finally里面看到了關閉調用,而且從代碼可以看出 JdbcTemplate是調用了DataSourceUtils的。在 Spring文檔中要求盡量首先使用JdbcTemplate,其次是用DataSourceUtils來獲取和釋放連接

 

再看看這個關閉調用方法內部: 

  1. /** 
  2. * Close the given Connection, obtained from the given DataSource, 
  3. * if it is not managed externally (that is, not bound to the thread). 
  4. * @param con the Connection to close if necessary 
  5. * (if this is <code>null</code>, the call will be ignored) 
  6. * @param dataSource the DataSource that the Connection was obtained from 
  7. * (may be <code>null</code>) 
  8. * @see #getConnection 
  9. */  
  10. public static void releaseConnection(Connection con, DataSource dataSource) {  
  11. try {  
  12. doReleaseConnection(con, dataSource);  
  13. }  
  14. catch (SQLException ex) {  
  15. logger.debug("Could not close JDBC Connection", ex);  
  16. }  
  17. catch (Throwable ex) {  
  18. logger.debug("Unexpected exception on closing JDBC Connection", ex);  
  19. }  
  20. }  
  21. /** 
  22. * Actually close the given Connection, obtained from the given DataSource. 
  23. * Same as {@link #releaseConnection}, but throwing the original SQLException. 
  24. * <p>Directly accessed by {@link TransactionAwareDataSourceProxy}. 
  25. * @param con the Connection to close if necessary 
  26. * (if this is <code>null</code>, the call will be ignored) 
  27. * @param dataSource the DataSource that the Connection was obtained from 
  28. * (may be <code>null</code>) 
  29. * @throws SQLException if thrown by JDBC methods 
  30. * @see #doGetConnection 
  31. */  
  32. public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {  
  33. if (con == null) {  
  34. return;  
  35. }  
  36. if (dataSource != null) {  
  37. ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  
  38. if (conHolder != null && connectionEquals(conHolder, con)) {  
  39. // It's the transactional Connection: Don't close it.  
  40. conHolder.released();  
  41. return;  
  42. }  
  43. }  
  44. // Leave the Connection open only if the DataSource is our  
  45. // special SmartDataSoruce and it wants the Connection left open.  
  46. if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {  
  47. logger.debug("Returning JDBC Connection to DataSource");  
  48. con.close();  
  49. }  
  50. }  


主要下面這幾行代碼

  1. // Leave the Connection open only if the DataSource is our  
  2. // special SmartDataSoruce and it wants the Connection left open.  
  3. if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {  
  4. logger.debug("Returning JDBC Connection to DataSource");  
  5. con.close();  
  6. }  

可以看到大部分情況下是自動關閉,除非你使用的是SmartDataSource,且SmartDataSource指定了允許關閉。

 

3.2. 使用DataSourceUtils

使用Spring DataSource進行事務管理的關鍵在於ConnectionHolderTransactionSynchronizationManager

  0.先從TransactionSynchronizationManager中嘗試獲取連接

  1.如果前一步失敗則在每個線程上,對每個DataSouce只創建一個Connection

  2.這個ConnectionConnectionHolder包裝起來,由TransactionSynchronizationManager管理

  3.再次請求同一個連接的時候,從TransactionSynchronizationManager返回已經創建的ConnectionHolder,然后調用ConnectionHolderrequest將引用計數+1

  4.釋放連接時要調用ConnectionHolderreleased,將引用計數-1

  5.當事物完成后,將ConnectionHolderTransactionSynchronizationManager中解除。當誰都不用,這個連接被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實現模板設計模式,這意味着,代碼中的重復的復雜的任務部分是在模板類中實現的。JdbcTemplatecore包的核心類。它替我們完成了資源的創建以及釋放工作,從而簡化了我們對JDBC的使用。它還可以幫助我們避免一些常見的錯誤,比如忘記關閉數據庫連接。JdbcTemplate將完成JDBC核心處理流程,比如SQL語句的創建、執行,而把SQL語句的生成以及查詢結果的提取工作留給我們的應用代碼。它可以完成SQL查詢、更新以及調用存儲過程,可以對ResultSet進行遍歷並加以提取。它還可以捕獲JDBC異常並將其轉換成org.springframework.dao包中定義的,通用的,信息更豐富的異常。

 

SpringJdbcTemplate的工作流程如下:

(1).配置數據源:

applicationContext.xml中設置好數據源。

Spring中,將管理數據庫連接的數據源當作普通Java Bean一樣在Spring IoC容器中管理,當應用使用數據源時Spring IoC容器負責初始化數據源。

(2).將數據源注入JdbcTemplate

JdbcTemplatedataSource屬性用於注入配置的數據源,Spring IoC容器通過依賴注入將配置的數據源注入到SpringJdbc操作的封裝類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獲取數據庫連接;通過DriverManagergetConnection方法獲取數據庫連接;

SingleConnectionDataSource :內部封裝了一個連接,該連接使用后不會關閉,且不能在多線程環境中使用,一般用於測試;

LazyConnectionDataSourceProxy :包裝一個DataSource,用於延遲獲取數據庫連接,只有在真正創建Statement等時才獲取連接,因此再說實際項目中最后使用該代理包裝原始DataSource從而使得只有在真正需要連接時才去獲取。

 

DataSourceUtilsSpring JDBC抽象框架內部都是通過它的getConnection(DataSource dataSource)方法獲取數據庫連接,releaseConnection(Connection con, DataSource dataSource) 用於釋放數據庫連接,DataSourceUtils用於支持Spring管理事務,只有使用DataSourceUtils獲取的連接才具有Spring管理事務。

 

4.3. 使用spring getSession()

Springhibernate 集成的環境里,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拿到的sessionSpring就會負責關閉了!

 

 

5. Springbeanscope 詳解

spring2.0之前bean只有2種作用域即:singleton(單例)non-singleton(也稱 prototype, Spring2.0以后,增加了sessionrequestglobal session三種專用於Web應用程序上下文的Bean。因此,默認情況下Spring2.0現在有五種類型的Bean。當然,Spring2.0Bean的類型的設計進行了重構,並設計出靈活的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時 候,springIOC容器中只會存在一個該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內有效,配置實例:

requestsessionglobal session使用的時候首先要在初始化webweb.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作用域,不過它僅僅在基於portletweb應用中才有意義。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中作用域是可以任意擴展的,你可以自定義作用域,甚至你也可以重新定義已有的作用域(但是你不能覆蓋singletonprototype),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 屬性,DBCPC3PO 都提供了close()方法關閉數據源,所以必須設定destroy-method=”close” 屬性, 以便Spring容器關閉時,數據源能夠正常關閉

2、 如果使用spring bean 的作用域為 prototype ,需要在應用程序中 手動關閉數據庫

3、 使用JdbcTemplate 來連接數據庫不需要手動關閉連接,但是,有時還需要自己額外的拿到conn進行操作,如下: jdbcTemplate.getDataSource().getConnection() 那么,就需要關閉連接了

4、 直接使用DataSourceUtils.getConnection()方法得到連接,則需要使用DataSourceUtils.releaseConnection()方法 來釋放連接了

5、 若是使用的是比DataSourceUtils 更底層的代碼,則必須要顯式關閉連接了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM