Spring AOP 源码分析:获得通知
在文章 Spring AOP 处理流程概述 中,对 Spring AOP 有了一个整体认识。在文章 Spring AOP 源码分析:入门 中,对 Spring AOP 的相关入口做了分析。这篇文章就带大家看一看,Spring AOP 是如何获取通知的?
示例代码
在 如何阅读 Spring 源码?: 示例代码 中,已经给出了一个完整的 AOP 示例代码。为了节省篇幅,请直接参考那篇文章的示例代码,这里就不在赘述。
注册 Advice(通知/增强)
请根据 Spring AOP 源码分析:入门 中提到的关键方法入口处,打上断点,开始调试。
首先,需要明确一点的是:对于切面(使用 @Aspect
注解标注过的类)在 Spring 容器中,也是被统一f封装为 BeanDefinition
实例的,也需要通过一个方式,将其注册到 Spring 容器中。比如,就像 示例代码 那样,通过 ImportSelector
方式,使用类名,将其注册到容器中。这样,就可以利用 Spring 容器对 Bean 的 API 来统一处理了。
Advice(通知/增强)几乎是在意想不到的地方完成注册的:在第一次调用 AbstractAutoProxyCreator#postProcessBeforeInstantiation
方法时,通过 AspectJAwareAdvisorAutoProxyCreator#shouldSkip
方法,完成了切面的注册。下面,我们对这个过程抽丝剥茧,逐步分析。
先来看看 findCandidateAdvisors
方法:
AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
@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.
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
查找 XML 配置的 Advisor
正如上面注释所示,会先通过 super.findCandidateAdvisors()
先获取父类方法加载的切面声明:
AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
直接看 advisorRetrievalHelper.findAdvisorBeans()
方法:
BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] 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!
// 在 beanNamesForTypeIncludingAncestors 方法中,通过遍历所有 Bean 名称来选取合适的对对象,
// 查找的是通过 XML 配置的 <aop:advisor/> Bean。并不会把 @Aspect 标注的类给选出来。
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}
这里通过 BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class<?>, boolean, boolean)
方法来查找 Advisor
。整个过程,就是针对 BeanFactory
递归调用其父 BeanFactory
,遍历所有的 Bean 名称,查找类型为 Advisor
的 Bean 名称,然后调用 beanFactory.getBean(name, Advisor.class)
,来获得对应的 Advisor
Bean 并返回。
上面介绍了查找 XML 配置的 Advisor
过程。
查找通过注解配置的 Advisor
我们回到 AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
方法中, BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
//获取所有的beanName
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
//循环所有的beanName找出对应的增强方法
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.
//获取对应的bean的类型
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
//如果存在Aspect注解
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//解析标记AspectJ注解中的增强方法
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
这里的逻辑比上面要简单清晰好多:查找出所有的 Bean 名称,然后选出类型标注了 @Aspect
注解的 Bean 类型,把 Bean 名称添加到 BeanFactoryAspectJAdvisorsBuilder#aspectBeanNames
实例变量中;根据类型信息,使用反射针对符合添加的方法,构建 Advisor
对象(实现类为 InstantiationModelAwarePointcutAdvisorImpl
),然后将其加入到 BeanFactoryAspectJAdvisorsBuilder#advisorsCache
变量中。
值得注意的是根据通知的类型,创建不同的 Advice
对象,也是在上面的这个过程中,在 ReflectiveAspectJAdvisorFactory#getAdvice
方法中完成的。
经过上面的处理,所有对应的 Advice(通知/增强)都会被查找出来。接下来,我们看一看如何针对特定的 Bean 选择出合适的 Advice(通知/增强)的。
这里说“注册”其实意思不太正确。 Advice(通知/增强)没有什么注册一说,它只是被解析后缓存了起来。下次再使用时,就不需要解析了。
选取 Advice(通知/增强)
上面解析后的 Advice(通知/增强)都被存放在了 BeanFactoryAspectJAdvisorsBuilder#Map<String, List<Advisor>> advisorsCache
变量中。所以,从这里拿到所有通知后再去做筛选。
在 Spring Bean 生命周期概述 中已经强调过了,AOP 代理的创建是在执行 BeanPostProcessor#postProcessAfterInitialization
,也就是 AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization
方法中。
在 Spring AOP 源码分析:入门 中提到`getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource)` 获取了所有符合条件的增强信息。
结合上面两点,找到对应的方法就是 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
。但是,这个方法几乎没啥代码,而是把处理全部委托给了 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
方法来处理。所以,可以直接看这个方法:
AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
重点在 findAdvisorsThatCanApply
方法上,从方法名上来看,这似乎是要查找可用的 Advisor。来看一下具体实现:
AopUtils#findAdvisorsThatCanApply
// 找到合适的 advisors,引介增强在前,其他普通增强在后
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
// 首先处理引介增强
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
// 这里来解析切入点表达式
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
经过多个重载 canApply
方法的来回传递,最后由如下方法来进行处理:
AopUtils#canApply(Pointcut, Class<?>, boolean)
// 表达式被解析成 AspectJExpressionPointcut 对象
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
// 这里先判断类型是否匹配。交给 AspectJ 来完成了,不再深究。
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
// 针对每个方法,判断是否符合表达式要求
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
// 匹配操作都交给了 AspectJ 来完成,不再深究
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
结果类型和方法的双重筛选后,就可以把符合条件的 Advice(通知/增强)给选择出来了。下一篇文章,来介绍一下如果创建代理类: Spring AOP 源码分析:创建代理(一) 和 Spring AOP 源码分析:创建代理(二)。