上一篇中,我们已经找到了AOP的源码入口,我们今天继续分析下面的代码,不过在此之前我们需要看下Spring中如何使用切面的,以便于我们理解我们的源码。代码如下:
package com.younger.web.aspect;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
/**
* 日志切面
*/
@Component
@Aspect
public class LogAspect implements Ordered {
/**
* 定义切点
*/
@Pointcut("execution(* com.younger.web.service.impl.system(..))")
public void pointcut(){
}
private void pointCut1(){}
/**
* 前置通知
*/
@Before("pointcut()")
private static void before(){
System.out.println("---前置通知---");
}
/**
* 后置通知
*/
@AfterReturning("pointcut()")
public void afterReturning(){
System.out.println("---后置通知---");
}
@Override
public int getOrder() {
return 1;
}
}
我们在项目中一般是通过@Aspect注解标记一个切面类的,在上面这个切面中实现了ordered接口,spring 会通过ordered的顺序来执行切面的,ordered值越小,优先级越高。那Spring是如何通过@Aspect注解找到切面类的呢?接下来我们就开始继续分析源码吧,上篇文章我们分析到了这个位置:
首先如果这个bean已经被处理过的话,那就直接返回,如果这个bean不需要被增强,就直接返回,如果这个bean被指定为不需要进行代理,也直接返回,就是一些常规校验。接着就会调用方法getAdvicesAndAdvisorsForBean(),我们到方法中看下:
这个方法主要就是调用findEligibleAdvisors()方法来获取合适的增强。我们到这个findEligibleAdvisors()方法看下:
在这个方法主要有两个主要的步骤:
1、调用findCandidateAdvisors方法获取所有的增强。
2、调用findAdvisorsThatCanApply方法找到与当前bean相匹配的增强。
我们先看下findCandidateAdvisors()方法是怎么获取所有的增强的,代码如下:
我们发现,在这个findCandidateAdvisors()方法中,首先调用了super.findCandidateAdvisors()方法来获取xml中配置的增强,也就是说我们不仅可以通过@Aspect注解方式配置的AOP,也可以使用xml方式配置AOP的。接着又调用了一个buildAspectJAdvisors()方法,为@Aspect注解标注的切面类构建增强,因为我们目前使用的是@Aspect注解的方式,所以super.findCandidateAdvisors()这行代码会返回一个空集合,我们现在来看下buildAspectJAdvisors()方法:
public List<Advisor> buildAspectJAdvisors() {
// @Aspect 注解标注切面类的beanName数组
List<String> aspectNames = this.aspectBeanNames;
// aspectNames为null表示之前没有构建过增强Advisors
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 获取IOC容器中所有的bean
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
// 依次遍历没个bean
for (String beanName : beanNames) {
// 过滤不需要的bean
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, false);
if (beanType == null) {
continue;
}
// 判断这个bean是否为切面,就是看下这个类上有没有加 @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);
// 为切面类中的增强方法构建Advisors
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
// 如果是单例,就将构建好的增强 classAdvisors 放入到缓存中
this.advisorsCache.put(beanName, classAdvisors);
}
else {
// 如果不是单例,那么就缓存 factory
this.aspectFactoryCache.put(beanName, factory);
}
// 添加刚构建好的增强 Advisors
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));
}
}
}
// 设置处理过切面类的beanName数组,方便下一次直接从缓存中获取
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
// 先尝试直接从缓存中获取增强 Advisors
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
// 如果缓存advisorsCache 中没有获取到增强 advisors, 那么就是要 factory 快速构建新的增强 advisors
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
我们可以看到,首先第一次调用这个buildAspectJAdvisors()方法时,这个aspectNames一定是个null,接着就会进入到这个if分支的处理中,首先从IOC容器中获取所有的bean,然后遍历依次处理每个bean。在处理bean的时候,主要有两个操作:
1、通过advisorFactory.isAspect()找到加了@Aspect注解的类;
2、通过this.advisorFactory.getAdvisors(factory)进一步找到切面类中的增强方法,并将增强方法构建为Advisor。
那我们就先来看下isAspect()方法是如何来找切面类的,isAspect()的方法代码如下:
可以看到,在isAspect方法中调用了hasAspectAnnotation() 方法,接着又调用了AnnotationUtils.findAnnotation()。而在方法中传入了Aspect.class参数,我们看一下这个Aspect:
这个类就是我们在切面类上加的那个注解@Aspect。我们看下这个AnnotationUtils.findAnnotation方法:
其实这个AnnotationUtils.findAnnotation()方法,就是专门用来在指定类上找特定注解的。
我们继续看下面的流程:
在分析这个构建 Advisors源码之前,我需要知道我们平时在使用AOP时,增强逻辑都会放在切面中一个一个的方法中,那么在为目标方法添加增强逻辑时,当然要拿到切面类中声明的这些方法,这样才能去执行对应的增强逻辑。所以这里不光要找到切面类,还要获取到切面类中方法,目前为止我们已经找到了切面类,那么下一步当然就是要获取到切面类中的方法,来看下Spring是怎么获取到切面类中方法的:
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
// 获取切面类中声明的方法
for (Method method : getAdvisorMethods(aspectClass)) {
// 为增强方法构建增强实例 Advisors, 这个 Advisors 就是一个 InstantiationModelAwarePointcutAdvisorImpl
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
可以看到,这里首先通过getAdvisorMethods(aspectClass)方法获取切面类中声明的方法,然后调用Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName)依次为每一个方法生成对应的增强Advisor。
首先我们要看的,就是获取切面类中方法的逻辑了,这个时候我们可以点进去getAdvisorMethods(aspectClass)方法来看下,此时代码如下:
我们可以看到,这里主要调用了ReflectionUtils.doWithMethods()来获取切面类中的方法。并且我们可以看到调用doWithMethods()方法时需要传入一些参数,那这些参数代表什么意思呢?我们来看下这个doWithMethods()方法的定义:
此时我们看到这个doWithMethods()方法定义了三个参数,分别是Class<?> clazz、MethodCallback mc和MethodFilter mf,第一个参数就是一个class对象,第二个和第三个参数通过参数名我们可以判断出,第二个参数MethodCallback是一个回调方法,而第三个参数是一个过滤条件。我们分别来看一下MethodCallback和MethodFilter分别是怎么定义的吧,如下图:
可以看到MethodCallback和MethodFilter的定义中,它们都加了@FunctionalInterface注解,所以MethodCallback和MethodFilter都是函数式接口,我们可以使用Lambda表达式调用它们。现在我们知道了doWithMethods()方法的第二个参数和第三个参数都是函数式接口,那我们调用doWithMethods()方法时,这三个参数都是怎么指定的呢?我们再回头来看下调用doWithMethods()方法的地方吧,如下图:
首先第一个参数aspectClass,就是指定切面类,第二个参数回调方法的本质,其实就是实现了MethodCallback接口的doWith()方法了,而第三个参数过滤条件的本质,则是实现了MethodFilter接口的matches()方法。看到这三个参数,我们就大致明白了doWithMethods()方法要做的事情,就是过滤出指定类aspectClass中的方法,过滤条件就是这个ReflectionUtils.USER_DECLARED_METHODS,最后通过回调方法将满足条件的method放到一个List集合中。我们知道了doWithMethods()方法要做的事情只之后,那现在就来看下它是怎么实现的吧,我们看这里:
首先就是先调用getDeclaredMethods()方法获取切面类中声明的方法methods,接着遍历处理这些method。
先调用mf.matches(method)过滤出要处理的方法,最后将过滤出来的方法,通过回调逻辑统一放到一个List集合中,我们这里来看一下方法过滤是怎么做的。我们点进去mf.matches(method)来看下,这个mf大家还记得吗?它其实就是一个函数式接口的实现类,而我们传进来的是ReflectionUtils.USER_DECLARED_METHODS,所以我们现在来看下这个ReflectionUtils.USER_DECLARED_METHODS到底是个啥,如下图:
可以看到,这里直接调用了Method对象的isBridge()方法和isSynthetic()方法来做的过滤,而这个isBridge()方法是用来判断当前方法是否为桥接方法,而isSynthetic()方法是用来判断当前方法是否为合成方法。简单来说,桥接方法和合成方法是编译器由于内部需要,编译器自己创建出来的方法,而不是我们自己创建的方法。!method.isBridge() && !method.isSynthetic() 这行代码在当前方法为非桥接方法且非合成方法时会返回true,说白了就是过滤出我们自己创建的方法,排除掉那些编译器内部创建的方法。那么当mf.matches(method)这行代码执行完毕,究竟能从切面类中过滤出来哪些方法呢?我们这里以日志切面类为例,来看下哪些方法是非桥接且非合成方法吧,首先我们先回顾一下日志切面类LogAspect ,代码如下:
在切面类LogAspect中的pointcut()、 before()、 afterReturning()、getOrder()这四个方法既不是桥接方法,也不是合成的方法,这些方法都是我们自己创建的方法,所以这四个方法执行到mf.matches()方法时,mf.matches()方法返回的都是true。mf.matches()方法执行完之后,就开始回调执行方法了,如下图:
这里其实直接调用了入参传进来mc的doWith()方法,这个mc实现类是我们自己传递进来的,此时就会回调到下面的逻辑,我们看这里:
其实就是将过滤出来的方法放到一个List集合中,但是我们发现在此之前,还有一个if条件,就是AnnotationUtils.getAnnotation(method, Pointcut.class) == null这行代码,只有当这个条件成立时,才会将方法放入到List集合中。并且我们还发现在调用getAnnotation()方法时,除了将method传进去之外,还传进去了一个Pointcut.class,这个Pointcut.class又是个什么?我们点进去看下Pointcut.class,代码如下:
这个就是我们的@Pointcut注解,我看下:
在切面类LogAspect的pointcut() 方法上加了一个@Pointcut注解,用来定义切点表达式使用的。那这AnnotationUtils.getAnnotation(method, Pointcut.class) 方法到底是干嘛的?我们可以先点进去看下AnnotationUtils.getAnnotation()方法的定义,如下图:
我们可以看到这个方法有两个入参,第一个参数是方法对象本身,第二个参数是注解类型,通过上面的注释,我们可以判断出这个getAnnotation()方法,其实就是用来在指定的方法method上获取特定注解的。那也就是说AnnotationUtils.getAnnotation(method, Pointcut.class) 方法是用来获取方法上的@Pointcut
注解的,那么AnnotationUtils.getAnnotation(method, Pointcut.class) == null又是什么意思呢?首先这个AnnotationUtils.getAnnotation(method, Pointcut.class) 方法什么时候会返回空?当然是这个方法上没有加@Pointcut注解的时候才会返回null,因为此时在这个方法上找不到@Pointcut注解,那当然要返回null,那么此时AnnotationUtils.getAnnotation(method, Pointcut.class) == null的结果为true,那么就会将这个方法添加到到List集合中,其实就是执行下边这行代码:
那如果此时当前方法加了@Pointcut注解呢?比如切面类LogAspect的pointcut() 方法上就加了一个@Pointcut注解。那此时AnnotationUtils.getAnnotation(method, Pointcut.class) == null的结果就为false,此时就不会将这个pointcut() 方法添加到List结合中。其实说白了这个条件就是用来排除掉@Pointcut注解的pointcut() 方法的。以切面类LogAspect为例,在执行完mf.matches()时,由于pointcut()、before()、afterReturning()、getOrder()这四个方法既不是桥接方法,也不是合成方法,所以这四个方法都可以正常通过。而执行到AnnotationUtils.getAnnotation(method, Pointcut.class) == null这个条件时,会将pointcut()方法给排除掉,因为pointcut()方法上加了@Pointcut注解,所以此时就只剩下before()、afterReturning()、getOrder()这三个方法了,最后就会将这三个方法放入到一个List集合中,然后作为getAdvisorMethods()方法的结果返回。
我们通过一张流程图总结一下今天的内容: