在使用struts+hibernater+spring开发一套小系统的时候,在业务中用
ApplicationContext ac = new FileSystemXmlApplicationContext("../webapps/reply/WEB-INF/applicationContext.xml");
WorklistServiceDao workplandao = (WorklistServiceDao) ac.getBean("worklistService");这种方式获取 springIOC容器提供的bean 时出现数据库连接不能关闭的情况。具体情况是这样的:在系统运行一段时间后会宕机。查看后台日志说:Caused by: java.sql.SQLException: Data source rejected establishment of connecti on, message from server: "Too many connections"。经测试确实是在WorklistServiceDao workplandao = (WorklistServiceDao) ac.getBean("worklistService")时没关闭,每执行一次都是开一个姓的连接。且没关闭。
用show processlist命令查看mysql数据库的连接。发现100个连接全被占满了。都没释放。
WorklistServiceDao中有对数据库的操作。如果仅仅是ApplicationContext ac = new FileSystemXmlApplicationContext("../webapps/reply/WEB-INF/applicationContext.xml")。不获取WorklistServiceDao,就没问题。
spring的连接池我是这样配置的
<bean id="dataSource"
                class="org.apache.commons.dbcp.BasicDataSource">
                <property name="driverClassName"
                        value="com.mysql.jdbc.Driver">
                </property>
                <property name="url"
                        value="jdbc:mysql://localhost:3306/reply?useUnicode=true&characterEncoding=utf-8">
                </property>
                <property name="username" value="root"></property>
                <property name="password" value="000000"></property>
                <property name="maxActive" value="10" />  <!-- 最大连接数 -->
                <property name="maxIdle" value="30" />  <!-- 数据库连接的最大空闲时间。超过此空闲时间,数据库连接将被标记为不可用,然后被释放。设为0表示无限制。 -->
                <property name="maxWait" value="10000" />  <!-- 最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。 -->
        </bean>        <!-- 定义Hibernate的sessionFactory,通过该Bean,可以获得Hibernate的Session-->
        <bean id="sessionFactory"
                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
                <property name="dataSource">
                        <ref bean="dataSource" />
                </property>
                <property name="hibernateProperties">
                        <props>
                                <prop key="generate_statistics">true</prop> 
                                <prop key="hibernate.dialect">
                                        org.hibernate.dialect.MySQLDialect
                                </prop>
                                <!--设置二级缓冲-->
                                <prop key="hibernate.cache.provider_class">
                                        org.hibernate.cache.EhCacheProvider
                                </prop>
                                <!--设置二级缓冲,打开查询缓冲-->
                                <prop key="hibernate.cache.use_query_cache">false</prop>
                                <!--设置显示Hibernate操作的SQL语句-->
                                <prop key="hibernate.show_sql">false</prop>
                        </props>
                </property>
。后面的省略
如果不手工获取ApplicationContext,spring的连接池能正常关闭,完全没问题,问题就出在手工获取ApplicationContext这里,虽然找到了问题所在,但由于学艺不精,不能解决,请教大家,望指点一下
废话比较多,也不知道说清楚了没有

解决方案 »

  1.   

    问题现象:在做web应用时会碰到这种情况某些地方无法通过web当中ApplicationContext来获得springIOC容器提供bean比如提供给外界webservice接口这个时候就需要手工通过ClassPathXmlApplicationContext等方式来获取ApplicationContext代码如下:ApplicationContext context =  ClassPathXmlApplicationContext(
        "applicationContext-*.xml");
    IXXXService xxxservice = (IXXXService ) context
        .getBean("xxxservice ");  这是段很典型加载  然而正是这种看似到处都是加载却为后面BUG埋下伏笔  xxxservice是具体业务类它向下和DAO依赖并控制着事务这里代表了个经典而且简单service具体配置略去值得提是scope这里没有指定默认是单例  切都是那么顺利像这样service代码写应该不下几百个可能诸位写更多过程依然很陶醉修改完毕测试再测试什么?ORA-12519!见鬼我打造这套号称简易快速SSH2框架已经在多个项目好评无数久经考验了写了不下几百次service居然报ORA-12519  迅速打开PLSQL检查数据库sessionSelect Count(1) From v$session t Where t.SCHEMANAME='XXX';  随着service执行session数在增加没有减少意思是当时就是这样  解决思路:这种出现在久经考验框架当中我心里是相当不安居然会有这种低级趣味整理思路开始分析:这段代码唯和以前区别地方就是我们在web应用中是通过容器加载提供bean只有容器启动时候才会加载xml那么重点就应该是关注XML加载方式了  在这里我们用是ApplicationContext接口注意看spring文档3.5.1.2.2 在非web应用中优雅地关闭springioc容器它这里用到是AbstractApplicationContext在取得bean后再执行个context.registerShutdownHook;  这里实验把将ApplicationContext改成AbstractApplicationContext执行context.close结果出来了session已被正常回收真相渐渐浮出水面  结论:每次加载context做法相当于每次都生成了次新spring容器在默认单例情况下如果不及时关闭contextservice所依赖DAO当中创建dataSource也直存在(包括所有单例情况下所生成类)从日志看service事务管辖中session确实已经关闭但SessionFactory还是存在只有在容器关闭情况下并指定了dataSource例子配置中destroy-method="close"dataSource单例才会被释放  spring文档当中对生命周期也描述很清楚通过DisposableBean或者指定destroy-method都能很好释放单例对象而prototype类型对象需要客户端显式指定释放释放对象完全是客户端控制,spring不负责释放  所以要改善context加载方式尽量少多次去加载实在没办法情况下定要记得关闭  最后写代码随意性图省事不经研究是造成这种BUG罪恶根源
      

  2.   

    ApplicationContext ac = new FileSystemXmlApplicationContext("../webapps/reply/WEB-INF/applicationContext.xml"); 
    -- 这样肯定不对。应该用ServletContext servletContext = this.getServletContext();WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
    WorklistServiceDao workplandao = (WorklistServiceDao) wac.getBean("worklistService");
      

  3.   

    我的业务已经脱离了Web容器(tomcat),是一个定时作业,直接java虚拟机中执行,不能获取到ServletContext 。
      

  4.   

    我的业务已经脱离了Web容器(tomcat),是一个定时作业,直接java虚拟机中执行,不能获取到ServletContext 。
    --- 有两种做法:
        1) Tomcat 中定义定时作业,比如用Quartz
        2) 同1楼的思路,ApplicationContext退出时,关闭数据库datasource。我个人推荐第1种做法,容易维护和编程。