版本
- spring 5.0.8.BUILD-SNAPSHOT
- aspectjweaver 1.8.13
如何扫描Advice
接上一回,讲到了getAdvicesAndAdvisorsForBean方法,该方法的目的是获取并生成Advisor Bean。其中包含了扫描通过@Aspect注解配置且与Bean方法的匹配的Advice,也是本章主要讲的内容
getAdvicesAndAdvisorsForBean
/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java
@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();
}
/**
* 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();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
....
关注findEligibleAdvisors方法可以清楚地知道这里主要分四步:
- findCandidateAdvisors (获取候选Advisor)
- findAdvisorsThatCanApply (获取适用于bean的Advisor: 例如Pointcut匹配)
- extendAdvisors (特殊处理,这里不赘述)
- sortAdvisors (排序,不赘述)
什么是Advisor? 首先,Advice是增强方法,即@Around, @Before等注解修饰的方法。而Advisor则是在Advice之上再包了一层。例如PointcutAdvisor则包有Advice和Pointcut
下面接着看findCandidateAdvisors和findAdvisorsThatCanApply
findCandidateAdvisors
/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java
....
/**
* 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();
}
....
/org/springframework/aop/aspectj/annotation/AnnotationAwareAspectJAutoProxyCreator.java
....
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
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;
}
....
这里findCandidateAdvisors在AbstractAdvisorAutoProxyCreator中有实现,同时被AnnotationAwareAspectJAutoProxyCreator重写了。不过可以看到重写的方法中先调用了super.findCandidateAdvisor,因此两个方法的代码都被执行了。
- this.advisorRetrievalHelper.findAdvisorBeans (该方法主要从BeanFactory中获取Advisor Bean)
- this.aspectJAdvisorsBuilder.buildAspectJAdvisors (从所有Bean中获取@Aspect配置的Bean并创建Advisor,也是我们关注的内容,下面细讲)
buildAspectJAdvisors
/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java
....
/**
* 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() {
// aspectBeanNames,缓存
// tips: aspectBeanNames由volatile修饰
// volatile: 保证变量可见性,指令不可重排
List<String> aspectNames = this.aspectBeanNames;
// 如缓存存在,则跳过初始化步骤
if (aspectNames == null) {
// synchronized, 同步锁
// 锁住当前实例,里面的内容同一时间只会被一个线程执行
synchronized (this) {
// 拿到锁后再次获取缓存,避免重复初始化
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 获取原始类为Object的bean,即所有bean
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
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.
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// isAspect 是否为@Aspect注解修饰的Bean
// 是不是很亲切方法,终于读到它了。这是我们在第二章一开始就提到的方法。
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
// 由beanType,beanName组合Metadata,包含了创建Advisor需要的内容
AspectMetadata amd = new AspectMetadata(beanType, beanName);
// PerClauseKind.SINGLETON 单例模式
// 由@Aspect中的value参数配置,这个参数Bean的scope 有点类似,用来配置生命 周期,默认都为单例。可配为"每..."的模式
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// 生成实例工厂类
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 生成Advisors实例
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
// 如果Aspect Bean是单例,则缓存到advisorsCache
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
// 如果不是,则将工厂缓存到aspectFactoryCache
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
// bean为单例,@Aspect也要配置为单例
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
// 跟前一个分支一样,生成Advisors实例,然后将工厂缓存到aspectFactoryCache
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) {
// 通过aspectName(beanName)获取advisors缓存
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
// 如已存在,则加载advisorsCache
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
// 如不存在,则加载factoryCache,再从工厂生成advisors,与上面初始时候的两个分支对应
// ps:这里并没有做factory的空判断...
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
....
由于这段代码比较长,我将过程注释在代码中。其中英文为源码注释。
那么,以上便是通过beanName扫描@Aspect配置并生成Advisor的过程了。其中this.advisorFactory.getAdvisors(factory)是生成Advisor类的具体内容。深挖的话还能再写一篇文章,这里就不细说了。有兴趣的可以自行阅读。
findAdvisorsThatCanApply
现在我们获得了所有的候选Advisor,那么找出和当前Bean匹配的Advisor呢?
/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java
....
/**
* Search the given candidate Advisors to find all Advisors that
* can apply to the specified bean.
* @param candidateAdvisors the candidate Advisors
* @param beanClass the target's bean class
* @param beanName the target's bean name
* @return the List of applicable Advisors
* @see ProxyCreationContext#getCurrentProxiedBeanName()
*/
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
....
一步一步往下探
/org/springframework/aop/support/AopUtils.java
....
/**
* Determine the sublist of the {@code candidateAdvisors} list
* that is applicable to the given class.
* @param candidateAdvisors the Advisors to evaluate
* @param clazz the target class
* @return sublist of Advisors that can apply to an object of the given class
* (may be the incoming List as-is)
*/
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;
}
....
/**
* Can the given advisor apply at all on the given class?
* This is an important test as it can be used to optimize
* out a advisor for a class.
* @param advisor the advisor to check
* @param targetClass class we're testing
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Advisor advisor, Class<?> targetClass) {
return canApply(advisor, targetClass, false);
}
/**
* Can the given advisor apply at all on the given class?
* <p>This is an important test as it can be used to optimize out a advisor for a class.
* This version also takes into account introductions (for IntroductionAwareMethodMatchers).
* @param advisor the advisor to check
* @param targetClass class we're testing
* @param hasIntroductions whether or not the advisor chain for this bean includes
* any introductions
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
....
最后定位到canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions)方法
/org/springframework/aop/support/AopUtils.java
/**
* Can the given pointcut apply at all on the given class?
* <p>This is an important test as it can be used to optimize
* out a pointcut for a class.
* @param pc the static or dynamic pointcut to check
* @param targetClass the class to test
* @param hasIntroductions whether or not the advisor chain
* for this bean includes any introductions
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
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 ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
可以看出判断是否是该bean合适的advisor,是通过advisor.getPointcut().getClassFilter().matches(targetClass)方法来判断的。匹配完class以后下面还有MethodMatcher来匹配method。回想我们在配置pointcut的时候不仅仅有class的规则,也有method的规则。
当然,再深入matches方法进去的话就是pointcut的匹配语法实现了。有兴趣的可以自行阅读。
一些总结
读到这儿,Spring AOP如何扫描@Aspect配置,生成Advisor类,并匹配对应的Bean整个流程已经很清楚了。这里再总结一下:
- 获取已在BeanFactory的Advisor Bean
- 获取所有Object Bean,过滤出@Aspect注解修饰的Bean,并生成Advisor
- 遍历上述获取的所有Advisor,由Advisor的Pointcut ClassFilter匹配合适的Bean