背景
有一个使用SSM搭建的服务,之前由于功能扩展需要引入AOP,但是测试的时候AOP却无法生效,通过检查最终确认了问题,还顺带送了另一个问题~
被代理的类是一个controller,位置在com.xr.servlet包中。然后系统使用了两个容器管理,分别是Spring容器和Spring MVC容器,各自的配置文件如下:
- Spring:
<context:component-scan base-package="com.xr"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
- Spring MVC:
<context:component-scan base-package="com.xr.servlet"/>
问题
从配置文件可以看出来,AOP之所以不生效,是由于MVC的配置文件中没有配置aspectj-autoproxy,导致MVC容器中的对象,也就是缓存的path对应的处理器,实际上并不是代理对象,而是原生对象。这个问题比较简单,但是从配置文件还可以看出另一个问题。
Spring和MVC管理的包有重合:com.xr.servlet是包含在com.xr中的,那么在com.xr.servlet包中所有需要被管理的对象就会存在两份,一份在Spring容器的beanFactory,一份在MVC容器的beanFactory,两个容器以组合的方式形成一种父子关系。
那么对于这个系统而言,com.xr.servlet包中的这些被管理的类,其实并不是单例。而且对于MVC的应用来说,在Spring容器中的com.xr.servlet包下的controller实例对象,通过正常请求永远也无法访问到。
因为MVC在注册handlerMethods的过程中,真正获取对象的时候,会优先从MVC容器获取,如果没找到才会去从父容器寻找。现在在MVC容器中找到了,则会放到handlerMethods中,后续直接根据path从handlerMethods获取处理器(HandlerMethod)。
那么,Spring容器中重复的这部分对象可以算是一种另类的"内存泄露":它们被引用着,无法被GC回收,但是又不会被访问到。
解决
如果使用两个容器,那么MVC和Spring的容器还是要各司其职,各自负责管理各自的类,这是个老项目,可能当时搭建的时候没有注意这个问题。处理的话,就在Spring配置的component-scan节点中使用exclude-filter将其排除出去即可。虽然问题简单,但还是记录下来~






还没有评论,来说两句吧...