Spring父子容器问题

3,838 阅读2分钟

这个问题老早就存在了,只是今天组长让我看AOP不生效的时候,才真实遇到这个问题,之前都是用的Spring Boot开发,不会存在这个问题。

问题描述

如果使用传统的方式来开发Spring项目,要部署在Tomcat上面,一般会依赖Spring与Spring MVC,在Tomcat的web.xml中会配置一个加载service的配置文件,这个在Tomcat启动的时候会进行加载,会生成一个Spring的容器。

默认情况下,Tomcat会在资源目录下加载配置servlet名称的另外一个xml配置文件,比如servlet名称为test,那么会加载test-servlet.xml配置文件,如果使用Spring MVC的话,会解析这个配置文件并且再生成一个Spring的容器,同时设置Tomcat启动时创建的那个容器为父容器。

image-20190329172613784

容器分离的影响:

  • 一般在父容器中会加载service,dao之类的东西,不加载controller
  • 在mvc容器中只加载controller

两个容器负责不同的bean,但是如果在父容器中配置了一些AOP想要处理controller的内容,因为容器的隔离,AOP就不会生效。

参考我之前的Spring AOP实现原理分析

因为在解析bean的时候,它会获取BeanPostProcesser(BPP),不同容器获取的BPP也是不相同的,理所当然在子容器解析Controller的bean时候,获取不到父容器配置的BPP,也就无法生成代理对象。

解决方案

如果明白为什么会出现父子容器问题,那么可以想到如下方案

  • 如果只需要处理service的bean,那么只在父容器的配置文件中操作
  • 如果只处理controller的bean,那么在mvc的配置文件中修改
  • 如果同时要处理service,还要处理controller,那么在两个配置文件中都进行修改

个人觉得,根据自己的业务处理,尝试重新ApplicationContext的getBeanFactory方法,交给其父容器获得,如果父容器为空,再由自己处理,参考Classloader的双亲委派机制,当然这只是我的一种设想。

@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
   if(getParent() != null){
       getParent().getBeanFactory();
   }
   synchronized (this.beanFactoryMonitor) {
      if (this.beanFactory == null) {
         throw new IllegalStateException("BeanFactory not initialized or already closed - " +
               "call 'refresh' before accessing beans via the ApplicationContext");
      }
      return this.beanFactory;
   }
}

最后

记录