对于丁雪丰老师在 理解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会委托父上下文去寻找。
- 增强是否生效存在两个先决条件:
- 开启增强支持(
@EnableAspectJAutoProxy
/<aop:aspectj-autoproxy/>
) - 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