spring源码之切面收集

导读:本篇文章讲解 spring源码之切面收集,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

AOP中有三个重要的概念:advisor(切面)、切点(pointcut)、advice(通知)
spring会对有切面的类生成代理

@EnableAspectJAutoProxy开启切面功能

图一
AspectJAutoProxyRegistrar中注册aop的入口类
在这里插入图片描述
在这里插入图片描述

上图将AnnotationAwareAspectJAutoProxyCreator注册到BeanDefinitionRegistry中,看继承关系如图所示:
在这里插入图片描述

所以AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator。在进行属性注入完成之后,会走到initializeBean方法,这是aop的入口在这里插入图片描述

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
	.....
	//这里不是合成的
	if (mbd == null || !mbd.isSynthetic()) {
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}
	.....
	}

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
		throws BeansException {

	Object result = existingBean;
	for (BeanPostProcessor processor : getBeanPostProcessors()) {
		Object current = processor.postProcessAfterInitialization(result, beanName);
		if (current == null) {
			return result;
		}
		result = current;
	}
	return result;
}

获取BeanPostProcessor ,并循环调用postProcessAfterInitialization方法,通过继承关系知道AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator,所以这里会调到AbstractAutoProxyCreator的postProcessAfterInitialization方法,如图所示:

在这里插入图片描述

在这里插入图片描述

找切面的过程:

看getAdvicesAndAdvisorsForBean方法,找到合适的切面

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();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
	//寻找有@Aspectj注解,把所有有这个注解的类封装成Advisor返回
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	//判断候选的切面是否作用在当前beanClass上面,匹配判断
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
	//对有@Aspect注解切面添加了一个默认的切面 DefaultPointcutAdvisor
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
		//对有@Order@Priority进行排序
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
	}
	return eligibleAdvisors;
}

寻找合格切面的过程:
1、寻找有@Aspectj注解的类,把所有有这个注解的类封装成Advisor返回
2、判断候选的切面是否作用在当前beanClass上面
3、对有@Aspect注解切面添加了一个默认的切面 DefaultPointcutAdvisor
4、对切面进行排序


看寻找合格切面过程的第一步findCandidateAdvisors方法
在这里插入图片描述
上图中第一步找自定义advisor过程比较简单,这里不赘述,看收集有@Aspect注解的buildAspectJAdvisors方法

public List<Advisor> buildAspectJAdvisors() {
		List<String> aspectNames = this.aspectBeanNames;

		if (aspectNames == null) {
			synchronized (this) {
				aspectNames = this.aspectBeanNames;
				if (aspectNames == null) {
					.....
					//获取spring容器中的所有bean的名称BeanName
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
					for (String beanName : beanNames) {
						.....
						//判断类上是否有@Aspect注解
						if (this.advisorFactory.isAspect(beanType)) {
							aspectNames.add(beanName);
							AspectMetadata amd = new AspectMetadata(beanType, beanName);
							if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {

								//创建获取有@Aspect注解类的实例工厂,负责获取有@Aspect注解类的实例
								MetadataAwareAspectInstanceFactory factory =
										new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);

								//创建切面advisor对象
								List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
								.....
								advisors.addAll(classAdvisors);
							}
							.....
						}
					}
					this.aspectBeanNames = aspectNames;
					return advisors;
				}
			}
		}

		.....
		return advisors;
	}

上面代码中创建获取有@Aspect注解类的实例工厂,负责获取有@Aspect注解类的实例。
看这个方法具体的实现:
看这个this.advisorFactory.getAdvisors(factory)方法
在这里插入图片描述
上图中循环的是类中没有pointcut的方法,也就是advice方法,将advice方法传入构造advisor切面,看上图的构造切面的getAdvisor方法
在这里插入图片描述
看上图获取pointcut对象的方法
在这里插入图片描述
至此,pointcut创建好了,由之前传过来的advice方法构造完成advisor切面,如图所示:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在getAdvice方法中会获取advice方法上面的注解,根据注解的不同,构造不同的advice
在这里插入图片描述
至此,切面中两个必备的元素pointcut和advice都已经具备,一个advisor切面就创建好了。

创建完所有的切面后,接下来过滤出匹配的切面,如图所示:
在这里插入图片描述
在这里插入图片描述
会调到canApply方法中进行类和方法的匹配:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		//调用ClassFilter的matches方法,判断类是否匹配
		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;
	}

过滤出匹配的切面之后,接下来针对@Aspect注解切面添加了一个默认的切面 DefaultPointcutAdvisor。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后对所有匹配到的advisor进行排序后返回。

如果返回的advisor数组不为空,则生成代理对象。
在这里插入图片描述

切面收集总结:

  1. 寻找有@Aspect注解,把所有有这个注解的类封装成Advisor返回。

    1)找到自定义的advisor,即实现了Advisor接口的
    2)找到有@Aspect注解的类,生成切面
    	构造切面:循环没有@PoinitCut注解的方法,也就是advice方法
    		构造pointcut对象,从advice方法的注解中拿到表达式,
    		并设置到pointcut对象中
    		构造advice对象,根据advice方法上不同的注解类型,构造不同的advice
    
  2. 判断候选的切面是否作用在当前beanClass上面,匹配判断,需要类和方法都匹配。

    循环找到的所有切面,判断当前beanClass上是否有切面,先进行类的匹配
    	1)类匹配
     		在进行类匹配的时候,
     		根据advice中的表达式,找到@PointCut注解的方法,
     		然后将pointcut对象中的表达式的值替换成@PointCut注解中的表达式,
     		然后判断当前beanClass是否在表达式中
    	2)方法匹配
    
  3. 对有@Aspect注解切面添加了一个默认的切面 DefaultPointcutAdvisor

  4. 对切面进行排序

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13824.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!