(原创)spring mvc和jersey rest 组合使用时单例对像实例化两次的BUG及解决办法

项目中没用spring 的restTemplate 而是采用 jersey来做rest 的实现,一直用着,也没发现有什么不对,后来加入了,以quartz用硬编码方式实现,结果启动项目的时候报错 ,具体信息为job id重复。后来经排查是因为:jersey依赖于org.springframework.web.context.ContextLoaderListener初始化 的ApplicationContext,而spring mvc 依赖于org.springframework.web.servlet.DispatcherServlet初始化的ApplicationContext,也就是说jersey和spring mvc 虽然是共用一个ApplicationContext但是ApplicationContext被初始化了两次,每次都会调用 beanFactory.preInstantiateSingletons()方法,导致了单例的类被初始化两次,平常使用是没问题的,只是每个单例类都实例化了两个对像(bean工厂中,你访问到的是第二次实例化的),恰巧我们的项目中,SchedulJobManager实例化后我们要设置一些JOB ,且这个JOB的ID是不能重复的,就触发了这个BUG。jersey版本比较老1.4.1,不知新版本的 jersey会不会不存在这问题,且最后我通过修改spring AbstractApplicationContext源码 解决了这问题,具体排查解决过程如下如下图

很是奇怪,然后在构造函数中加了一个日志输出,下如图所示,结果发现 Schedul Initializing 这行条了两次

 

接下来,检查web.xml 的配置 如下图所示

后来还怀疑过是不是 jersey这个springServlet搞的鬼,去掉他,还是有同样的错 ,当时对这事,有个临时的解决办法,加一个全局静态变量 haveInit,在构造函中设置它为true PostConstruct标签的方法run方法中,如haveInit为true 就什么也不错返回。问题是解决了,但不是从根本上解决,于是我在代码中加了调用栈的打印。如图所示

打印出来的结果是  ,org.springframework.web.context.ContextLoaderListener和org.springframework.web.servlet.DispatcherServlet分别都初始化了一次 appcationContext导致的,请看下图

 

解决办法是修改 spring AbstractApplicationContext类,做这样的修改:加一个全局静态变量 haveInitFlg缺省值为0 ,在finishBeanFactoryInitialization方法中

最后一行代码beanFactory.preInstantiateSingletons();  修改为

        if(haveInitFlg==0){
            beanFactory.preInstantiateSingletons();
            haveInitFlg = 1;
        }

即可

posted @ 2013-07-01 13:32  itestAndy  阅读(4479)  评论(1编辑  收藏  举报