Spring应用上下文对于AOP增强的影响

1,063 阅读2分钟

对于丁雪丰老师在 理解Spring的应用上下文 视频中的总结

Set up

声明TestBean

    public class TestBean {
        private String context;
    
        public void hello() {
            log.info("hello " + context);
        }
    }

声明FooAspect 切面类

    @Aspect
    @Slf4j
    public class FooAspect {
        @AfterReturning("bean(testBean*)")
        public void printAfter() {
            log.info("after hello()");
        }
    }

创建相应的ApplicationContext

设置两种不同的ApplicaitionContext声明

FooConfig

    @Configuration
    @EnableAspectJAutoProxy
    public class FooConfig {
        @Bean
        public TestBean testBeanX() {
            return new TestBean("foo");
        }
    
        @Bean
        public TestBean testBeanY() {
            return new TestBean("foo");
        }
    
        @Bean
        public FooAspect fooAspect() {
            return new FooAspect();
        }
    }

applicationContext.xml

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 条件1 -->
    	<aop:aspectj-autoproxy/> 
    
        <bean id="testBeanX" class="geektime.spring.web.context.TestBean">
            <constructor-arg name="context" value="Bar"/>
        </bean>
    		
        <!-- 条件2 -->
        <bean id="fooAspect" class="geektime.spring.web.foo.FooAspect" />
    </beans>

启动类

创建ApplicationContext

    @SpringBootApplication
    @Slf4j
    public class ContextHierarchyDemoApplication implements ApplicationRunner {
    
        public static void main(String[] args) {
            SpringApplication.run(ContextHierarchyDemoApplication.class, args);
        }
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            ApplicationContext fooContext = new AnnotationConfigApplicationContext(FooConfig.class);
            ClassPathXmlApplicationContext barContext = new ClassPathXmlApplicationContext(
                    new String[]{"applicationContext.xml"}, fooContext);
            TestBean bean = fooContext.getBean("testBeanX", TestBean.class);
            bean.hello();
    
            log.info("=============");
    
            bean = barContext.getBean("testBeanX", TestBean.class);
            bean.hello();
    
            bean = barContext.getBean("testBeanY", TestBean.class);
            bean.hello();
        }
    }

当前结构

ParentContext: fooContext

ChildContext: barContext

fooContext 管理 testBeanX(foo)testBeanY(foo)

barContext 管理 testBeanX(bar)

不同情况分析

条件一

  • 父上下文开启@EnableAspectJAutoProxy
  • 子上下文中不开启<aop:aspectj-autoproxy/>
  • FooAspect切面声明在父上下文中

结果

AOP已增强:fooContext.testBeanX , barContext.testBeanY

AOP未增强:barContext.textBeanX

分析

父上下文开启了增强,所以fooContext.testBeanX 生效。barContext中不存在testBeanY,所以向父上下文中寻找fooContext.testBeanY 所以增强生效。

子上下文中未开启增强,所以barContext.textBeanX 未被增强。

条件二

  • 父上下文开启@EnableAspectJAutoProxy
  • 子上下文中开启<aop:aspectj-autoproxy/>
  • FooAspect切面声明在子上下文中

结果:

AOP已增强:barContext.textBeanX

AOP未增强:fooContext.testBeanX , barContext.testBeanY

分析:

切面声明在子上下文中,所有父上下文中的bean不被增强。

条件三

  • 父上下文开启@EnableAspectJAutoProxy
  • 子上下文中开启<aop:aspectj-autoproxy/>
  • FooAspect切面声明在父上下文中

结果:

均被增强

分析:

首先,父、子上下文均支持增强。(@EnableAspectJAutoProxy / <aop:aspectj-autoproxy/>)。

其次,切面处理声明在父上下文中,可知当子上下文缺少切面处理Bean时会向父上下文中寻找。

总结

  • 子上下文Bean查找存在委托机制,上下文中不存在的Bean会委托父上下文去寻找。
  • 增强是否生效存在两个先决条件:
    1. 开启增强支持(@EnableAspectJAutoProxy / <aop:aspectj-autoproxy/>
    2. AOP增强的处理类要能够被当前上下文所寻找到(委托寻找到也可)。

详情可参考Spring官方文档:

EnableAspectJAutoProxy (Spring Framework 5.2.4.RELEASE API)

  • @EnableAspectJAutoProxy = <aop:aspectj-autoproxy/>

  • @EnableAspectJAutoProxy applies to its local application context only, allowing for selective proxying of beans at different levels. Please redeclare @EnableAspectJAutoProxy in each individual context, e.g. the common root web application context and any separate DispatcherServlet application contexts, if you need to apply its behavior at multiple levels.(@EnableAspectJAutoProxy切面增强仅对当前上下文提供支持,允许选择性的增强不同层级中的Bean。如果需要将该增强应用于所有层级,那么请在所有上下文中均声明,例如web root上下文与任何分离的DispatcherServlet上下文中。)

详细代码

geektime-geekbang/geektime-spring-family/context-hierarchy-demo