积累,创造,分享!

BlogJava 首页 新随笔 联系 聚合 管理
  25 Posts :: 13 Stories :: 26 Comments :: 0 Trackbacks

问题现象:在做web应用时会碰到这种情况,某些地方无法通过web当中的ApplicationContext来获得springIOC容器提供的bean,比如提供给外界的webservice接口,这个时候就需要手工通过ClassPathXmlApplicationContext等方式来获取ApplicationContext,代码如下:
ApplicationContext context = new ClassPathXmlApplicationContext(
    "applicationContext-*.xml");
IXXXService xxxservice = (IXXXService ) context
    .getBean("xxxservice ");
这是一段很典型的加载。
然而,正是这种看似到处都是的加载却为后面的BUG埋下伏笔。
xxxservice是具体的业务类,它向下与DAO依赖并控制着事务,这里代表了一个经典而且简单的service,具体配置略去,值得一提的是scope,这里没有指定,默认的是单例。
一切都是那么顺利,像这样的service代码写的应该不下几百个,可能诸位写的更多,过程依然很陶醉,修改完毕。测试,再测试。什么?ORA-12519错误!见鬼,我打造的这套号称简易快速的SSH2框架已经在多个项目好评无数久经考验了,写了不下几百次的service居然报ORA-12519错误。
迅速打开PLSQL,检查数据库session,Select 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容器,在默认单例的情况下,如果不及时关闭context。service所依赖的DAO当中创建的dataSource也一直存在(包括所有的单例情况下所生成的类),从日志看,service事务管辖中的session确实已经关闭,但SessionFactory还是存在的。只有在容器关闭的情况下,并指定了dataSource实例配置中的destroy-method="close",dataSource单例才会被释放。
spring文档当中对生命周期也描述的很清楚。通过DisposableBean或者指定destroy-method都能很好的释放单例对象。而prototype类型的对象需要客户端显式的指定释放,释放对象完全是客户端控制,spring不负责释放。
所以,要改善context的加载方式,尽量的少多次去加载,实在没办法的情况下,一定要记得关闭。
最后,写代码的随意性,图省事,不经思考,是造成这种BUG的罪恶根源。

posted on 2009-04-16 17:27 nighthawk 阅读(2717) 评论(3)  编辑  收藏

Feedback

# re: 警惕手工加载spring配置文件导致数据库session无法释放[未登录] 2009-04-17 11:11 gavin
为什么每次都要加载一次配置文件?  回复  更多评论
  

# re: 警惕手工加载spring配置文件导致数据库session无法释放 2009-04-17 16:02 空白
集成ApplicationContextAware,获取web环境中实例化的ApplicationContex,就可以获取已经实例化的bean;

public class SpringContextUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext;

public void setApplicationContext(ApplicationContext arg0)
throws BeansException {
SpringContextUtil.applicationContext = arg0;
}

public static ApplicationContext getApplicationContext() {
return SpringContextUtil.applicationContext;
}

/**
* <p>根据名字获取spring容器中bean</p>
* @param name
* @return
*/
public static Object getBean(String name) {
return SpringContextUtil.getApplicationContext().getBean(name);
}
}  回复  更多评论
  

# re: 警惕手工加载spring配置文件导致数据库session无法释放 2010-03-19 23:01 plusir
在找关于registerShutdownHook是否能在web环境下起作用的文章,随机do到了你的blog。
文章随便点了点,没有细看,但是只看标题,就仿佛看到了镜子里另外一个自己。不知道你的重构看完了没,我的那本摆了好几年了,始终是摆设。core j2ee patterns一直似看不看的翻着,也不知道后ejb时代这些东西是不是有价值,不过福勒的企业应用架构模式到是反过来调过去的看着。
关注着j2ee,SSH,EJB3等等等等,同时思考着设计分析框架软件过程那些东西,经历何其的相似。看看你最后一篇文章的日期,想想自己那久不维护的blog,心里小小的酸楚了一下,轻轻的问候一声,最近还好吧,我的同行 :)  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航:
博客园   IT新闻   Chat2DB   C++博客   博问