Spring 管理数据源

 

不管通过何种持久化技术,都必须通过数据连接访问数据库,在Spring中,数据连接是通过数据源获得的。在以往的应用中,数据源一般是Web应用服务器提供的。在Spring中,你不但可以通过JNDI获取应用服务器的数据源,也可以直接在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容器关闭时,数据源能够正常关闭。除以上必须的数据源属性外,还有一些常用的属性:

:设置从数据源中返回的连接是否采用自动提交机制,默认值为 true;

:设置数据源是否仅能执行只读操作, 默认值为 false;

:最大连接数据库连接数,设置为0时,表示没有限制;

:最大等待连接中的数量,设置为0时,表示没有限制;

:最大等待秒数,单位为毫秒, 超过时间会报出错误信息;

:用于验证连接是否成功的查询SQL语句,SQL语句必须至少要返回一行数据, 如你可以简单地设置为:“select count(*) from user”;

:是否自我中断,默认是 false ;

:几秒后数据连接会自动断开,在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容器关闭时数据源能够成功释放。

拥有比DBCP更丰富的配置属性,通过这些属性,可以对数据源进行各种有效的控制:

:当连接池中的连接用完时,C3P0一次性创建新连接的数目;

:定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30;

:两次连接中间隔时间,单位毫秒,默认为1000;

:连接关闭时默认将所有未提交的操作回滚。默认为false;

: C3P0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数,那么属性preferredTestQuery将被忽略。你 不能在这张Test表上进行任何操作,它将中为C3P0测试所用,默认为null;

:获取连接失败将会引起所有等待获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调   用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认为 false;

:当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒,默认为0;

: 通过实现ConnectionTester或QueryConnectionTester的类来测试连接,类名需设置为全限定名。默认为 com.mchange.v2.C3P0.impl.DefaultConnectionTester;

:隔多少秒检查所有连接池中的空闲连接,默认为0表示不检查;

:初始化时创建的连接数,应在minPoolSize与maxPoolSize之间取值。默认为3;

:最大空闲时间,超过空闲时间的连接将被丢弃。为0或负数则永不丢弃。默认为0;

:连接池中保留的最大连接数。默认为15;

:JDBC的标准参数,用以控制数据源内加载的PreparedStatement数量。但由于预缓存的Statement属 于单个Connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素,如果maxStatements与 maxStatementsPerConnection均为0,则缓存被关闭。默认为0;

:连接池内单个连接所拥有的最大缓存Statement数。默认为0;

:C3P0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能,通过多线程实现多个操作同时被执行。默认为3;

:定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个参数能显著提高测试速度。测试的表必须在初始数据源的时候就存在。默认为null;

: 用户修改系统配置参数执行前最多等待的秒数。默认为300;

:因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的时候都 将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable

等方法来提升连接测试的性能。默认为false;

:如果设为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里面的比较核心的一个方法:

  1. //-------------------------------------------------------------------------
  2. // Methods dealing with prepared statements
  3. //-------------------------------------------------------------------------

  4. publicObject execute(PreparedStatementCreator psc, PreparedStatementCallback action)
  5. throwsDataAccessException {

  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. returnresult;
  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(pscinstanceofParameterDisposer) {
  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. throwgetExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
  43. }
  44. finally{
  45. if(pscinstanceofParameterDisposer) {
  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. publicstaticvoidreleaseConnection(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. publicstaticvoiddoReleaseConnection(Connection con, DataSource dataSource)throwsSQLException {
  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(!(dataSourceinstanceofSmartDataSource) || ((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(!(dataSourceinstanceofSmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
  4. logger.debug("Returning JDBC Connection to DataSource");
  5. con.close();
  6. }

可以看到大部分情况下是自动关闭,除非你使用的是SmartDataSource,且SmartDataSource指定了允许关闭。

 

3.2. 使用DataSourceUtils类

使用Spring 对DataSource进行事务管理的关键在于ConnectionHolder和TransactionSynchronizationManager。

先从TransactionSynchronizationManager中尝试获取连接

如果前一步失败则在每个线程上,对每个DataSouce只创建一个Connection

这个Connection用ConnectionHolder包装起来,由TransactionSynchronizationManager管理

再次请求同一个连接的时候,从TransactionSynchronizationManager返回已经创建的ConnectionHolder,然后调用ConnectionHolder的request将引用计数+1

释放连接时要调用ConnectionHolder的released,将引用计数-1

当事物完成后,将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对外提供的方法操作数据库。


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

表示该针对每一次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

作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效,配置实例:

 配置实例:

request配置实例的前提一样,配置好web启动文件就可以如下配置:

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

5.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"/>

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 更底层的代码,则必须要显式关闭连接了


作者:​​​panie​​​