版本
- spring 5.0.8.BUILD-SNAPSHOT
- aspectjweaver 1.8.13
从注解开始
由于在本人实际应用中使用的是注解配置AOP,也更倾向于了解Spring AOP的整个实现,而不仅仅是关键实现。于是本篇源码解析,将会从注解开始。了解Spring AOP是怎么扫描Aspect配置,匹配,并生成AOP代理的。
注解@Aspect定了一个类为AOP的配置。那么,便从@Aspect的源码引用开始吧。
@Aspect的引用
先从源码中找有引用到@Aspect,用来判断Class是否有该注解的代码。找到方法。
/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java
...
@Override
public boolean isAspect(Class> clazz) {
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class> clazz) {
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}
/**
* We need to detect this as "code-style" AspectJ aspects should not be
* interpreted by Spring AOP.
*/
private boolean compiledByAjc(Class> clazz) {
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
// an implementation detail of the AspectJ compiler.
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().startsWith(AJC_MAGIC)) {
return true;
}
}
return false;
}
...
isAspect(Class> clazz)用来判断clazz对象是否包含Aspect.class注解并且未被AspectJ编译过,其中hasAspectAnnotation(Class> clazz)内容很明显就不多提。倒是compiledByAjc(Class> clazz)的实现比较特别。它的实现是用字段前缀来判断是否为"code-style" aspects,看起来是一种比较Hack的方法。
这里我有一个疑惑点,就是"code-style" aspects和"annotation-style" aspects的具体所指,查了一圈也没有看到明确的解释。只在 IDEA的帮助文档 Overview of AspectJ support这段上有看到相关的解释。我的理解是"code-style"是由AspectJ Language所定义的aspect,会由AspectJ来编译,而"annotation-style"则是由使用了@Aspect注解的Java语言所定义的aspect,如有错误烦请指出。
入口在Bean的生命周期中
通过一步步阅读和调试,可以一层一层向上找到org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java中有两个入口。
postProcessBeforeInstantiation和postProcessAfterInitialization,是Bean生命周期中的两个步骤:
- postProcessBeforeInstantiation: 在Bean实例化之前执行
- postProcessAfterInitialization: 在Bean初始化之后执行
postProcessBeforeInstantiation:
/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java
...
@Override
public Object postProcessBeforeInstantiation(Class> beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
...
这个方法的目的是将含有custom TargetSource的bean进行增强处理。可分为两部份,前半部分利用缓存和几个方法判断是否需要增强。后半部分则进入主题判断是否含有custom TargetSource。不过这里我对custom TargetSource不是特别理解,也没有细看,因为通过@Aspect注解配置不会执行这里面的代码,留着以后有时间再看。
这里还有另外两个方法:
- isInfrastructureClass(是否是基础类,如Advice、Pointcut、Advisor、AopInfrastructureBean 及其超类)
- shouldSkip(主要目的是判断是否是已注册的@Aspect配置Bean,其实扫描@Aspect注解配置的方法就在这里面被调用到了,这个后面再说)
postProcessAfterInitialization
/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java
....
/**
* 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) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return 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;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
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;
}
可以看到关键内容就在wrapIfNecessary里面。顾名思义:必要时转成AOP代理。前半部分判断是否是不需要增强的,跟postProcessBeforeInstantiation的前半部分有点类似。后半部分根据是否有合适的Advice方法,有则将Bean转成代理。
好了,这里其实就是整个流程最关键的两个地方了:
- getAdvicesAndAdvisorsForBean(获取适合该Bean的Advice方法,里面包含了扫描@Aspect注解配置Bean的方法)
- createProxy(创建AOP代理,里面包含了AOP代理的实现)
这两个方法的具体内容,将在接下来的文章介绍