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

  • 相关阅读:
    jquery.cookie.js插件一个小bug
    MVC3的学习笔记
    枚举和位标志学习
    局域网中访问IIS站点
    文件内容比较
    未能加载文件或程序集“xxx”或它的某一个依赖项。生成此程序集的运行时比当前加载的运行时新,无法加载此程序集。
    MSSQLSERVER服务不能启动
    初步学习lock的见解
    “base64 字符数组的无效长度”错误解决方案
    获取服务IP
  • 原文地址:https://www.cnblogs.com/panie2015/p/5630142.html
Copyright © 2020-2023  润新知