前言
内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。
本文主要涉及书中第七章的部分,依照书中内容以及个人理解对Spring源码进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework
正式进入正文前,强烈推荐先阅读文末参考阅读第一篇:关于 Spring AOP (AspectJ) 你该知晓的一切。阅读完你可以了解清楚:
- 面向对象编程为什么需要面向切面编程(AOP),AOP又是什么?
- AspectJ 和 Spring AOP 又是什么关系?
- AOP 中一些基本概念。
- 帮助理解本文第一部分 动态AOP使用示例。
下面正式Spring源码 AOP部分的研究。
I. 动态AOP使用示例
为了在 spring-mytest 模块中使用上Spring AOP,需要额外引入 spring-aop 和 spring-aspects 两个模块, spring-aop 是可以正常引入的,而 spring-aspects 模块我们在编译源码时由于IDEA不识别所以我们当初去除了该模块,参考这篇文章的做法,我添加了打包好的 spring-aspects 模块。下面就是 spring-mytest 模块的 gradle 配置:
dependencies {
compile(project(":spring-beans"))
compile(project(":spring-context"))
compile(project(":spring-aop"))
compile group: 'org.springframework', name: 'spring-aspects', version: '5.0.7.RELEASE'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
创建需要被拦截的bean
在实际工作中,一个 bean 可能是满足业务需要的核心逻辑。例如下面创建的 bean 中的 test()
方法中可能会封装着某个核心业务,这里就用打印语句简单代替一下。但是,如果我们想在 test()
方法内核心业务前后加入日志来跟踪测试,如果直接修改源码并不符合面向对象的设计方法,而且随意改动原有代码也会造成一定的风险,还好Spring AOP能够帮我们做到了这一点。
public class TestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
public void test() {
// 核心业务
System.out.println("test");
}
}
创建Advisor
Spring中摒弃了最原始的繁杂配置方式而采用 @AspectJ
注解对 POJO 进行标注,使AOP的工作大大简化,例如,在 AspectJTest
类中,我们要做的就是在所有类的 test()
方法执行前在控制台打印 beforeTest 。而在所有类的 test()
方法执行后打印 afterTest,同时又使用环绕的方式在所有类的 test()
方法执行前后在此分别打印 before1 和 after1 。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class AspectJTest {
@Pointcut("execution(* *.test(..))")
public void test() {
}
@Before("test()")
public void beforeTest() {
System.out.println("beforeTest");
}
@After("test()")
public void afterTest() {
System.out.println("afterTest");
}
@Around("test()")
public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("before1");
Object o = null;
try {
o = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("after1");
return o;
}
}
创建配置文件
要在Spring中开启AOP功能,还需要在配置文件中作如下声明:
<?xml version='1.0' encoding='UTF-8' ?>
<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">
<aop:aspectj-autoproxy />
<bean id="test" class="guo.ping.aop.usedemo.TestBean" />
<bean class="guo.ping.aop.usedemo.AspectJTest" />
</beans>
可以看到,开启AOP功能的是 <aop:aspectj-autoproxy />
这个自定义标签。
测试
首先,先编写测试方法:
public class AopDemoTest {
@Test
public void testAopDemo() {
ApplicationContext context = new ClassPathXmlApplicationContext("aopDemo-Test.xml");
TestBean testBean = (TestBean) context.getBean("test");
testBean.test();
}
}
测试结果:
Spring实现了对所有类的 test()
方法进行增强,使辅助功能可以独立于核心业务之外,方便与程序的扩展和解耦。这里的测试结果可以发现,先执行环绕通知的前后处理方法,然后再处理 @Before 和 @After 通知。
II. 动态AOP自定义标签
我们配置了 <aop:aspectj-autoproxy />
之后便可以开启Spring的AOP功能,而且该标签属于自定义标签类型,那么回顾之前介绍Spring对于自定义标签解析的流程,实现这一功能必然存在着某个继承自 NamespaceHandlerSupport
的实现类,在其 init()
方法中注册了针对 aspectj-autoproxy 的 BeanDefinitionParser
解析器。进入源码详细查看。
参照之前的几篇文章,我们debug进入到解析自定义标签 <aop:aspectj-autoproxy />
,可以看到即将进入解析自定义标签环节。继续深入。
根据命名空间获取 NamespaceHandlerSupport
。
获取结果为 AopNamespaceHandler
:
在 AopNamespaceHandler
类中的 init()
方法中可以清晰的看到,配置了关于 aspectj-autoproxy 的解析器类 AspectJAutoProxyBeanDefinitionParser
。
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*/
@Override
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
这样,我们就已经知道Spring对于 <aop:aspectj-autoproxy />
标签的解析相关功能是用 AspectJAutoProxyBeanDefinitionParser
类实现的,一起研究一下这个类。
我们着重研究 AspectJAutoProxyBeanDefinitionParser
类的 parse()
方法。
/**
* 解析aspectj-autoproxy自定义标签
* @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
* @param parserContext the object encapsulating the current state of the parsing process;
* provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
* @return
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 注册AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// 对于注解中子类的处理
extendBeanDefinition(element, parserContext);
return null;
}
因为我们该标签主要用于配置,不用于容器进行创建 bean,所以经过两个函数的设置最终返回了 null。我们着重研究两个函数具体做了什么。
注册AnnotationAwareAspectJAutoProxyCreator
首先研究 AopNamespaceUtils
的 registerAspectJAnnotationAutoProxyCreatorIfNecessary()
方法。
/**
* 注册AnnotationAwareAspectJAutoProxyCreator
* @param parserContext
* @param sourceElement
*/
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 注册或升级AutoProxyCreator定义beanName为org.Springframework.aop.config.internalAutoProxyCreator的BeanDefinition
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 对于proxy-target-class以及expose-proxy属性的处理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 注册组件并通知,便于监听器进一步处理
// 其中beanDefinition的className为AnnotationAwareAspectJAutoProxyCreator
registerComponentIfNecessary(beanDefinition, parserContext);
}
这其中调用了三个函数,分别做了三件事情。
① 注册或升级AnnotationAwareAspectJAutoProxyCreator
查看 AopConfigUtils
中的 registerAspectJAnnotationAutoProxyCreatorIfNecessary()
方法,传入的 registry
参数实际就是 DefaultListableBeanFactory
的实例。
/**
* 注册或升级AutoProxyCreator定义beanName为org.Springframework.aop.config.internalAutoProxyCreator的BeanDefinition
* @param registry
* @param source
* @return
*/
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
@Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
继续深入:
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
@Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 如果已经存在了自动代理创建器且存在的自动代理创建器与现在的不一致那么需要根据优先级来判断到底需要使用哪个
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 是否存在org.springframework.aop.config.internalAutoProxyCreator名称的BeanDefinition
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
// 如果已经存在的beanDefinition类型不是AnnotationAwareAspectJAutoProxyCreator
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
// 如果AnnotationAwareAspectJAutoProxyCreator的优先级比原来的高,要进行更改
if (currentPriority < requiredPriority) {
// 改变bean最重要的就是改变bean所对应的className属性
apcDefinition.setBeanClassName(cls.getName());
}
}
// 如果已经存在自动代理创建器并且与将要创建的一致,那么无需再次创建
return null;
}
// 如果不存在org.springframework.aop.config.internalAutoProxyCreator名称的BeanDefinition,则创建并注册
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
// 标注该类的角色为基础设施
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
代码附上了详尽的注释,代码主要进行了确保容器注册了 AnnotationAwareAspectJAutoProxyCreator
,对应的 bean 的 name 为 org.springframework.aop.config.internalAutoProxyCreator
。如果已经存在就与 AnnotationAwareAspectJAutoProxyCreator
类型的优先级进行比较,如果现存的优先级低要进行替换。优先级的查询是通过 findPriorityForClass()
方法获得的。
private static int findPriorityForClass(Class<?> clazz) {
return APC_PRIORITY_LIST.indexOf(clazz);
}
private static int findPriorityForClass(@Nullable String className) {
for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {
Class<?> clazz = APC_PRIORITY_LIST.get(i);
if (clazz.getName().equals(className)) {
return i;
}
}
throw new IllegalArgumentException(
"Class name [" + className + "] is not a known auto-proxy creator class");
}
优先级存储在了 APC_PRIORITY_LIST
中,APC_PRIORITY_LIST
在 AopConfigUtils
的静态代码块中进行了初始化。
static {
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
当然,如果容器中没有定义好的 beanDefinition,那么就新建一个并进行注册(添加至Spring容器的 beanDefinitionMap
中)。
② 处理proxy-target-class和expose-proxy属性
useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement)
函数主要对于自定义标签的 proxy-target-class
以及 expose-proxy
属性的处理,这两个属性设置方式如下:
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
我们进入函数体,方法 registry
参数还是那样,sourceElement
就是自定义标签:
/**
* 对于proxy-target-class以及expose-proxy属性的处理
* @param registry
* @param sourceElement
*/
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) {
// 实现了对proxy-target-class的处理
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
// 当需要使用CGLIB代理和@AspectJ自动代理支持,设置proxy-target-class为true
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 对expose-proxy的处理,设置为true主要是解决目标对象内部的自我调用
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
代码主要分两块,分别对两个属性值存在与否以及是否为 true 进行了判断,从而决定是否进行相应的设置。我们来看一下 AopConfigUtils
中的两个方法。
/**
* 设置proxy-target-class为true
* @param registry
*/
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
/**
* 设置expose-proxy为true
* @param registry
*/
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
两个函数其实就是对第①步创建(或升级)的自动代理创建器 bean 的 beanDefinition 进行了属性的设置,强制使用的过程其实就是对 bean 的定义的修改过程。
那么这两个属性具体有什么作用呢?
proxy-target-class 和Spring的动态代理方式选择相关,如果设置为 true,会强制使用CGLIB动态代理。这一点在后面还会具体分析。
我们具体来看 expose-proxy 属性,顾名思义,其作用是是否暴露代理对象。关于这一点,参考文章中有相关的具体使用场景,这里给大家复现一个简单的案例。
我们简单的修改第一部分的案例。
增加一个 TestBean
的实现接口 ITest
:
public interface ITest {
void test();
void subTest();
}
然后修改 TestBean
,重点是 test()
方法内部调用了 subTest()
方法。
public class TestBean implements ITest{
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
@Override
public void test() {
System.out.println("test");
subTest();
}
@Override
public void subTest() {
System.out.println("subTest");
}
}
对两个方法定义增强,我们增加了一个切点,同时给 test()
方法进行前置和后置通知,给 subTest()
方法设置了环绕通知。
@Aspect
public class AspectJTest {
@Pointcut("execution(* guo.ping.aop.usedemo.TestBean.test(..))")
public void test() {
}
@Pointcut("execution(* guo.ping.aop.usedemo.TestBean.subTest(..))")
public void subTest() {
}
@Before("test()")
public void beforeTest() {
System.out.println("beforeTest");
}
@After("test()")
public void afterTest() {
System.out.println("afterTest");
}
@Around("subTest()")
public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("=========beforeSubTest==========");
Object o = null;
try {
o = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("=========afterSubTest==========");
return o;
}
}
运行测试代码,出现以下结果:
好像没什么问题,test()
方法增强成功了。但是,事情没有这么简单,当这两个方法都是需要开启事务的,结果 subTest()
方法没有开启,仅开启了 test()
方法的,这是有问题的。那么如何解决呢?这里就可以依靠 expose-proxy 属性,将代理对象暴露出去,其实是暴露至 ThreadLocal 中,可以通过 AopContext.currentProxy()
获取到代理对象,这在后文会细说。
我们测试一下。修改配置文件 expose-proxy 的属性为 true。
<?xml version='1.0' encoding='UTF-8' ?>
<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">
<aop:aspectj-autoproxy expose-proxy="true"/>
<bean id="test" class="guo.ping.aop.usedemo.TestBean" />
<bean class="guo.ping.aop.usedemo.AspectJTest" />
</beans>
修改 test()
方法的调用方式,先获取代理对象,然后调用 subTest()
方法。
@Override
public void test() {
System.out.println("test");
ITest proxy = (ITest) AopContext.currentProxy();
proxy.subTest();
}
测试结果如下,完全OK。
③ 注册组件并通知
最后一步,是将前两步精心构造的 beanDefinition 进行组件的注册并通知,便于监听器进一步处理。
/**
* 注册组件并通知,便于监听器进一步处理
* @param beanDefinition
* @param parserContext
*/
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
if (beanDefinition != null) {
BeanComponentDefinition componentDefinition =
new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
parserContext.registerComponent(componentDefinition);
}
}
首先用 BeanComponentDefinition
包装了 beanDefinition 和 beanName,然后向 eventListener
注册 componentDefinition
。
public void registerComponent(ComponentDefinition component) {
CompositeComponentDefinition containingComponent = getContainingComponent();
if (containingComponent != null) {
containingComponent.addNestedComponent(component);
}
else {
this.readerContext.fireComponentRegistered(component);
}
}
/**
* Fire an component-registered event.
*/
public void fireComponentRegistered(ComponentDefinition componentDefinition) {
this.eventListener.componentRegistered(componentDefinition);
}
处理子标签
extendBeanDefinition()
方法对自定义标签的子标签又进行了相应的解析并将其设置到 beanDefinition 中。
private void extendBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinition beanDef =
parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
if (element.hasChildNodes()) {
addIncludePatterns(element, parserContext, beanDef);
}
}
如果存在子节点,那么便会调用 addIncludePatterns()
方法进行解析并设置。
private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
// 用于保存解析的属性
ManagedList<TypedStringValue> includePatterns = new ManagedList<>();
NodeList childNodes = element.getChildNodes();
// 遍历子节点
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node instanceof Element) {
Element includeElement = (Element) node;
TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
valueHolder.setSource(parserContext.extractSource(includeElement));
includePatterns.add(valueHolder);
}
}
if (!includePatterns.isEmpty()) {
includePatterns.setSource(parserContext.extractSource(element));
beanDef.getPropertyValues().add("includePatterns", includePatterns);
}
}
III. 创建AOP代理
上面已经介绍完关于 aspectj-autoproxy 自定义标签的解析器类 AspectJAutoProxyBeanDefinitionParser
中 parse()
方法中的三个步骤,主要就是注册一个名为 org.springframework.aop.config.internalAutoProxyCreator
类型为 AnnotationAwareAspectJAutoProxyCreator
的 beanDefinition。这个 beanDefinition 的定义是为了实例化 bean 所用,最终还是转化为一个 AnnotationAwareAspectJAutoProxyCreator
类型的实例对象。
那么问题来了,这个 AnnotationAwareAspectJAutoProxyCreator
自动代理创建器类到底有什么用?
对应AOP的实现,基本上就是靠该类去完成,它可以根据 @Point 注解定义的切点来自动代理相匹配的 bean。为了简便配置,Spring使用了在XML文件配置 <aop:aspectj-autoproxy />
自定义标签来帮助我们实现自动注册。
我们先看一下该类的继承结构。
我们可以看到,它实现了 BeanPostProcessor
接口,实现了该接口,当Spring依据 beanDefinition 去加载每个 bean 时会在初始化后调用其 postProcessAfterInitialization()
方法。当然, BeanPostProcessor
接口的子类 InstantiationAwareBeanPostProcessor
接口中也包含了一个非常重要的后处理方法。
/**
* 添加了在bean实例化前后的回调,以及在实例化之后但在设置显式属性或自动装配之前的回调。
*
* 通常用于抑制特定目标bean的默认实例化,例如创建具有特殊TargetSource的代理对象(池化目标,延迟初始化目标等),
* 或实现其他注入策略(如字段注入)。
*/
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
/**
* 在bean实例化之前应用此方法,返回的bean对象可以是目标bean的代理对象,有效地抑制了目标bean的默认实例化
*
* 如果此方法返回非null对象,则bean创建过程将被短路。
* 应用的唯一进一步处理是来自配置的BeanPostProcessor的postProcessAfterInitialization回调
*/
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
/**
* 在bean实例化之后,通过构造函数或工厂方法,但在Spring属性填充(来自显式属性或自动装配)之前执行操作。
* 这是在Spring自动装配开始之前在给定bean实例上执行自定义字段注入的理想回调。
*
* 如果要允许bean下一步设置属性则返回true,否则想要跳过则返回false
*/
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
/**
* 在工厂将它们应用于给定bean之前对给定属性值进行后处理,而不需要属性描述符。
*/
@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
return null;
}
/**
* 在工厂将它们应用于给定bean之前对给定属性值进行后处理。
* 允许检查是否已满足所有依赖项,例如,基于bean属性setter上的“Required”批注。
* 还允许替换要应用的属性值,通常是通过基于原始PropertyValues创建新的MutablePropertyValues实例,添加或删除特定值。
*/
@Deprecated
@Nullable
default PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
return pvs;
}
}
该接口在bean加载流程中默认实例化之前会被调用,具体可以参考前文的 createBean(...)
方法中的 resolveBeforeInstantiation(beanName, mbdToUse)
方法的处理流程。该接口调用时机是在bean进行默认实例化之前调用的,还有在实例化之后进行调用。
通过第一大步,我们已经在容器的 beanDefinitionMap
中成功注册了 AnnotationAwareAspectJAutoProxyCreator
类型的 beanDefinition
,那么什么时候实例化的呢?
在上一篇文章的关于 ApplicationContext
的 refresh()
过程中,第六步是注册所有的 BeanPostProcessor
,所有硬编码或者配置文件中配置的 BeanPostProcessor
类型的 beanDefinition
都将按照优先级进行有序的实例化成为具体的对象。这也就保证了在普通 bean
实例化之前,所有的 BeanPostProcessor
已经存在于容器全部可用了。在这里就相当于,自定义标签 <aop:aspectj-autoproxy />
无论定义在配置文件中哪里,都不会影响到 AnnotationAwareAspectJAutoProxyCreator
在普通 bean
之前先进行实例化。
/**
* 注册 拦截Bean创建的 Bean处理器,这里只是注册,真正的调用是在getBean时候
* Instantiate and invoke all registered BeanPostProcessor beans,
* respecting explicit order if given.
* <p>Must be called before any instantiation of application beans.
*/
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
现在,我们已经实例化了 AnnotationAwareAspectJAutoProxyCreator
对象,并交由Spring容器进行管理,此时还没有完全完成 Context
的刷新流程,具体来说应该是从注册所有的 BeanPostProcessor
方法 registerBeanPostProcessors(beanFactory)
返回。当我们去实例化普通的 beanDefinition
时,所有的后处理器便可以开始工作。对于 AnnotationAwareAspectJAutoProxyCreator
后处理器来说,它的所有后处理方法将会被调用。
下面则主要从 AnnotationAwareAspectJAutoProxyCreator
后处理方法的实现说起。我们分析AOP的逻辑也从这里出发。
AnnotationAwareAspectJAutoProxyCreator
对于该扩展方法的重写是在父类 AbstractAutoProxyCreator
中实现的:
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 根据给定的bean的class和name构建出个key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 如果它适合被代理,则需要封装指定bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
回顾 BeanPostProcessor
接口的作用是 bean 的后处理器,能够在 bean 构造函数构造初始化阶段的前后做一些扩展。那么当 AbstractAutoProxyCreator
发现需要动态代理的对象初始化完成后,我们可以对它进行代理增强,最终容器内存放的是代理 bean。 这就是Spring AOP的主要功能实现方式,也是自定义标签注册 AnnotationAwareAspectJAutoProxyCreator
类型实例的意义所在。
重写的 postProcessAfterInitialization()
方法中核心逻辑就是对目标对象,也就是传递来的形参 bean 进行增强并进行返回,方法为 wrapIfNecessary(bean, beanName, cacheKey)
。
其实本文主要的核心就是研究这个方法。
/**
* 包装bean,增强成功返回代理bean
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 如果已经处理过直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 如果无需增强也直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 给定的bean类是否代表一个基础设施类,基础设施类不应代理,或者配置了指定bean不需要自动代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 如果存在增强方法或增强器则创建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果获取到了增强则需要针对增强创建代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理bean,传入用SingletonTargetSource包装的原始bean
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
// 没获取到增强方法或增强器也直接返回
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
其中包含了各种不需要代理直接返回的情况,分别进行判断并进行记录,旁枝末节我们就先略过,直接进入需要增强的部分。
// 如果存在增强方法或增强器则创建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果获取到了增强则需要针对增强创建代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理bean,传入用SingletonTargetSource包装的原始bean
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
// 没获取到增强方法或增强器也直接返回
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
创建并返回代理对象的主要步骤为:
- 获取增强方法或增强器;
- 根据获取到的增强进行代理。
两个步骤分别代表着两个主要方法,分别是 getAdvicesAndAdvisorsForBean()
和 createProxy()
。其中获取增强器首先需要获取所有的,然后再过滤出能够用的,所以本部分会分三小节进行分析流程。
获取增强器
我们进入 getAdvicesAndAdvisorsForBean()
方法。
/**
* 获取增强方法或增强器
* @param beanClass the class of the bean to advise
* @param beanName the name of the bean
* @param targetSource
* @return
*/
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
方法传入的参数是目标对象的类型,bean 的名称。可以看到核心实现最终交给了 findEligibleAdvisors(beanClass, beanName)
方法,而 DO_NOT_PROXY
其实就是 null
。
/**
* 寻找所有合适的增强(增强器并不一定都适用于当前bean,要选出满足我们通配符的增强器)
* Find all eligible Advisors for auto-proxying this class.
* @param beanClass the clazz to find advisors for
* @param beanName the name of the currently proxied bean
* @return the empty List, not {@code null},
* if there are no pointcuts or interceptors
* @see #findCandidateAdvisors
* @see #sortAdvisors
* @see #extendAdvisors
*/
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取所有的增强
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 寻找所有增强中适用于bean的增强并应用
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 在advice链的开始添加ExposeInvocationInterceptor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
可以清晰的看到主要完成三件事,获取所有、过滤出适用于 bean 增强的然后排序,本节我们着重研究第一步,看看获取所有增强器的实现过程。
/**
* 获取候选的所有增强器(包括配置文件中Advisor类型的bean,以及@AspectJ定义的切面类中所有的通知封装成的Advisor)
* @return
*/
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
// 当使用注解方式配置AOP的时候并不是丢弃了对XML配置的支持。在这里调用父类方法加载配置文件中的AOP声明
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// 获取bean的注解方式的增强器(其实是通过遍历所有的beanDefinition寻找有注解@AspectJ定义的切面类中的所有通知封装成了增强器)
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
findCandidateAdvisors()
方法是在 AnnotationAwareAspectJAutoProxyCreator
中,可以看到方法上有 @Override
注解,重写自 AbstractAdvisorAutoProxyCreator
的 findCandidateAdvisors()
方法。由于我们这里分析的是使用注解进行的AOP,所以调用的是 AnnotationAwareAspectJAutoProxyCreator
重写的方法,在实现时当使用注解方式配置AOP的时候并不是丢弃了对XML配置的支持,而是也调用父类方法加载配置文件中的AOP声明,然后再调用获取目标 Bean 的注解并增强。总的来说,所有的增强包括配置文件中 Advisor
类型的 bean,以及 @AspectJ
定义的切面类中所有的通知封装成的 Advisor
。
先来看看父类的方法实现,主要就是从XML配置,也就是 beanFactory 容器 (this.cachedAdvisorBeanNames
) 中寻找,获取到所有增强器名称 beanName 后将其对应的实例添加至 list。
/**
* 从容器中获取所有增强器bean(class为Advisor类型的beanDefinition,然后通过beanFactory的getBean方法转换成对象返回)
* Find all candidate Advisors to use in auto-proxying.
* @return the List of candidate Advisors
*/
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
/**
* 从容器中获取所有增强器bean(class为Advisor类型的beanDefinition,然后通过beanFactory的getBean方法转换成对象返回)
* Find all eligible Advisor beans in the current bean factory,
* ignoring FactoryBeans and excluding beans that are currently in creation.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] advisorNames = null;
synchronized (this) {
// 从beanFactory中获取增强器
advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the auto-proxy creator apply to them!
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
// 如果XML中配置了Advisor.class类型的bean,那么advisorNames长度将不为0,接下来通过getBean将增强器实例化
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
// 获取到所有增强器名称后将其对应的实例添加至list
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
....
....
}
}
}
}
return advisors;
}
分析完父类方法,我们再来看看其特有的获取 bean 注解并增强的逻辑。
// 获取bean的注解增强
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
可以看到,主要是通过 this.aspectJAdvisorsBuilder
的 buildAspectJAdvisors()
方法实现的。我们需要从寻找有 @AspectJ 注解定义的类中的增强器,那么必然会根据容器中现有的 bean,依据它们的 class 然后过滤出用 @AspectJ 注解定义的类,对这些类进行增强器的提取。
总结一下:
- 获取所有 beanName,这一步骤中所有在 beanFactory 中注册的 Bean 都会被提取出来。
- 遍历所有 beanName,并找出声明 @AspectJ 注解的类,进行进一步的处理。
- 对标记为 @AspectJ 注解的类进行增强器的提取(通知封装成Advisor)。
- 将提取结果加入缓存。
下面来看代码是如何实现这个逻辑的。
/**
* 获取bean的注解增强
* Look for AspectJ-annotated aspect beans in the current bean factory,
* and return to a list of Spring AOP Advisors representing them.
* <p>Creates a Spring Advisor for each AspectJ advice method.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> buildAspectJAdvisors() {
// 记录保存了被增强的beanName
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 1. 获取的是所有的beanName,因为类型是Object
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
// 2. 循环遍历所有的beanName,过滤带有@AspectJ注解的类,找出对应的增强方法
for (String beanName : beanNames) {
// 不合法的bean则略过,由子类定义合法规则,默认返回true
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
// 获取对应beanName的bean的类型
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// 如果bean的class类存在@AspectJ注解
if (this.advisorFactory.isAspect(beanType)) {
// 只要被注解的就记录下来
aspectNames.add(beanName);
// 利用AspectMetadata包装了关于该类(用@AspectJ标注的)的一些信息,包括类型,名称,AjType
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// 创建了一个工厂,包含了beanFactory和AspectMetadata信息
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 3. 解析标记了@AspectJ注解的类中定义的增强器
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
// 4. 解析结果记录在缓存中
if (this.beanFactory.isSingleton(beanName)) {
this