开一篇吧,面试的时候经常会被问到这个问题,而我又是个没脑子的家伙,解决了就忘了,后面遇到问题也会写在这里
- ApplicationListener<ContextRefreshedEvent>的实现类中方法onApplicationEvent被调用多次,而且根据项目启动打印日志,每个POJO类也被加载了多次。
- 根据项目启动的打印日志,每个POJO类都被加载了多次,很明显是启动了多个Spring容器。然而为什么呢?在没有认为因素的情况下,Spring容器的创建与项目的配置有关系,而且同一个容器不会默认创建多个。
- 项目中Spring+SpringMVC的配置方式:
- 方式一:web.xml中配置DispatcherServlet的参数为SpringMVC的配置文件;在SpringMVC是配置文件中import Spring的配置文件;
- 方式二:web.xml中配置①DispatcherServlet的参数为SpringMVC的配置文件②Spring的ContextLoaderListener,并添加Spring的配置文件,即SpringMVC和Spring的配置文件独立配置;
- 两种项目的配置方式,带来的不同:
- 方式一使得系统只创建一个容器,这个容器是:
当前容器:WebApplicationContext for namespace 'DispatcherServlet-servlet': startup date [Tue Dec 11 09:24:18 CST 2018]; root of context hierarchy 当前父容器:null
- 方式二使得视同创建两个容器:
当前容器:Root WebApplicationContext: startup date [Tue Dec 11 09:20:28 CST 2018]; root of context hierarchy 当前父容器:null 当前容器:WebApplicationContext for namespace 'DispatcherServlet-servlet': startup date [Tue Dec 11 09:20:42 CST 2018]; parent: Root WebApplicationContext 当前父容器:Root WebApplicationContext: startup date [Tue Dec 11 09:20:28 CST 2018]; root of context hierarchy
- 方式一使得系统只创建一个容器,这个容器是:
- 很明显,当我们使用import配置文件的方式的时候,项目中只有一个Servlet的Web容器,而使用独立配置的时候则产生了另外一个Root Web容器,而这个Root Web容器作为Servlet Web容器的父容器,这就是不同点。
- 我的验证方式:在接口ApplicationListener<ContextRefreshedEvent>的实现类的方法onApplicationEvent中,从参数事件上获取当前的容器及其父容器,打印即可,当然也可以从源码的角度解释,这里就不做阐述了。
- 上述问题的解决方式:无厘头,感觉无处下爪,只能项目回滚到正常再一个一个加版本验证。因为项目是我创建的,我确定开始的时候是不存在这个问题的,肯定是有同事改了或者添加了什么东西,结果最后定位是在两个定时任务的类中,问题就在于同事在类中手动创建了多个ClassPathXMLApplicationContext,而这些new的操作都是在static代码块中,所以才导致项目启动就会创建多个多余的容器,然而每个容器的创建都会去加载配置文件做一堆事情,如扫描mapper文件和接口,POJO类等,解析mapper.xml文件等等
- 冗余容器带来的:一句话百害而无一利。从内存的角度讲,系统创建了多个ClassPathXMLApplicationContext容器,而系统会自动创建对应的Web容器,所以手动创建的容器占用了本不该占用的内存;从项目的启动速度上讲,大大的拖延服务器启动速度,因为每个容器都要去扫描所有的资源,造成启动时间的浪费。
- 这个问题给我们开发人员一个沉重的思考:项目中严禁任何时候手动创建容器,如果需要从容器中找Bean,注入即可。
网友评论