zoukankan      html  css  js  c++  java
  • 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 更底层的代码,则必须要显式关闭连接了

  • 相关阅读:
    'Undefined symbols for architecture i386,clang: error: linker command failed with exit code 1
    The codesign tool requires there only be one 解决办法
    XCode iOS project only shows “My Mac 64bit” but not simulator or device
    Provisioning profile XXXX can't be found 的解决办法
    UIView 中的控件事件穿透 Passthrough 的实现
    Xcode4.5出现时的OC新语法
    xcode 快捷键(持续更新)
    打越狱包
    php缓存与加速分析与汇总
    浏览器的判断
  • 原文地址:https://www.cnblogs.com/panie2015/p/5630142.html
Copyright © 2011-2022 走看看