@Aspect注解背后的奥秘--下

Posted 热爱编程的大忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了@Aspect注解背后的奥秘--下相关的知识,希望对你有一定的参考价值。

@Aspect注解背后的奥秘--下


前言

上一篇文章我们减少了aop原始时代中通过ProxyFactory和AspectJProxyFactory手动创建代理对象的过程,本文我们来探究一下新生代中aop自动化是如何实现的。


手动化进行到自动化靠的是什么

Spring设计了一套完善的bean生命周期流程,并在流程中各个阶段都预留了相关的生命周期回调接口,如果检测到有回调接口实现类,便会在创建每个bean时都在各个生命周期阶段回调这些实现类相应的生命周期回调接口。

如果我们想对放入容器中的每个bean都尝试进行修改,那么就需要提供实现相应的生命周期接口,然后将实现类注入到容器中即可。

这里生命周期回调接口在spring中具体指的是BeanPostProcessor这个接口体系:

因此,如果要实现自动化判断bean是否需要被代理,并在需要代理时,进行代理,我们只需要提供一个对应的bean后置处理器即可,该后置处理器只需要关心postProcessAfterInitialization和getEarlyReference这两个回调接口即可。

postProcessAfterInitialization方法中创建代理对象,getEarlyReference方法确保在存在循环依赖时,依然返回的是代理对象。

getEarlyReference回调接口的作用,参考下面这篇文章,后面我就不再提了:

spring三级缓存


自动代理创建器

spring已经为我们提供了一个这样的bean后置处理器,由于它能自动帮助我们判断某个bean是否需要进行代理,并在需要是进行代理,所有这个bean后置处理器又被称为自动代理创建器。


不考虑循环依赖导致提前暴露的问题的话,AbstractAutoProxyCreator只会在postProcessAfterInitialization回调接口中对bean尝试进行代理,也就是bean的初始化相关方法被调用后:

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) 
		if (bean != null) 
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			//如果产生了循环依赖,那么提前暴露的bean已经在getEarlyReference方法中尝试进行过代理了,此时跳过会bean的代理尝试
			if (this.earlyProxyReferences.remove(cacheKey) != bean) 
			//我们不考虑循环依赖情况,普通情况下,bean会在此处被判断是否需要进行代理
				return wrapIfNecessary(bean, beanName, cacheKey);
			
		
		return bean;
	

去掉一些判断逻辑:

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) 
		 ...
		//如果寻找到能够切入当前bean的增强器链,那么就进行代理
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) 
			...
			//进行代理
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			...
			return proxy;
		
		...
		//如果不存在advisor能够应用到当前bean上,那么跳过代理,返回原对象
		return bean;
	

上面这段流程的关键在于getAdvicesAndAdvisorsForBean方法,即自动代理创建器如何筛选得到能够应用到当前bean上的advisors呢?


如何搜寻并对增强器集合进行过滤

AbstractAutoCreator自动代理创建器的顶层抽象类规定好了自动代理创建器的核心工作流程,但是将获取advisors并筛选获取能应用到当前bean上的advisors的方法交给子类进行实现:

BeanNameAutoProxyCreator可以通过设置beanName匹配列表,如: user*,那么会将beanName以user开头的bean的自动创建代理,而所应用的拦截器是AbstractAutoCreator提供的通用拦截器,即会应用到所有bean上的拦截器,如果不手动设置,默认是空。

我们这里更关注的是AbstractAdvisorAutoProxyCreator:

AbstractAdvisorAutoProxyCreator通过搜寻beanFactory中所有类型为Advisors的bean完成对增强器的查找过程:

	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) 
        //搜索所有能够应用到当前bean上的增强器
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) 
			return DO_NOT_PROXY;
		
		return advisors.toArray();
	
	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) 
	    //寻找所有可用的候选advisor
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		//对候选增强器进行过滤,只挑选出那些能应用在当前bean上的增强器
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		//钩子函数,留给子类扩展可用增强器
		extendAdvisors(eligibleAdvisors);
		//为增强器进行排序
		if (!eligibleAdvisors.isEmpty()) 
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		
		return eligibleAdvisors;
	

1.寻找所有可用的候选advisor

	private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;


	@Override
	public void setBeanFactory(BeanFactory beanFactory) 
		//设置并检查beanFactory
		...
		initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
	

	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) 
		this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);
	

核心是通过BeanFactoryAdvisorRetrievalHelper完成的,该类会从BeanFactory进行搜寻:

	public List<Advisor> findAdvisorBeans() 
		String[] advisorNames = this.cachedAdvisorBeanNames;
		if (advisorNames == null) 
			//从IOC容器中获得所有类型为Advisor的bean
			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);
			this.cachedAdvisorBeanNames = advisorNames;
		
		if (advisorNames.length == 0) 
			return new ArrayList<>();
		
        
		List<Advisor> advisors = new ArrayList<>();
		//对每个advisor进行过滤判断
		for (String name : advisorNames) 
		    //当前advisor是否有效--BeanFactoryAdvisorRetrievalHelper的isEligibleBean默认返回true
		    //但是子类进行了覆写,实际调用的是AbstractAdvisorAutoProxyCreator的isEligibleAdvisorBean方法
		    //该方法主要有两个分支: 通常情况下返回true, 或者只对spring基础设施bean进行代理
			if (isEligibleBean(name)) 
			   //跳过正在创建的advisor
				if (this.beanFactory.isCurrentlyInCreation(name)) 
					if (logger.isTraceEnabled()) 
						logger.trace("Skipping currently created advisor '" + name + "'");
					
				
				else 
					try 
					//其他的advisor加入增强器集合,最后返回
						advisors.add(this.beanFactory.getBean(name, Advisor.class));
					
					catch (BeanCreationException ex) 
						....
					
				
			
		
		return advisors;
	
    
    protected boolean isEligibleBean(String beanName) 
		return true;
	
    
    //BeanFactoryAdvisorRetrievalHelper子类覆写了isEligibleBean方法
    //实际调用的是AbstractAdvisorAutoProxyCreator的isEligibleAdvisorBean方法进行的判断
	private class BeanFactoryAdvisorRetrievalHelperAdapter extends BeanFactoryAdvisorRetrievalHelper 
	     ...
		@Override
		protected boolean isEligibleBean(String beanName) 
			return AbstractAdvisorAutoProxyCreator.this.isEligibleAdvisorBean(beanName);
		
	        

1.1 isEligibleBean两种分支情况

AbstractAdvisorAutoProxyCreator类的isEligibleAdvisorBean方法负责在搜寻可用advisor阶段,对advisor进行一次预先过滤,该方法默认返回true,但是其中两个子类对其进行了覆写:

DefaultAdvisorAutoProxyCreator:

DefaultAdvisorAutoProxyCreator的前缀匹配默认是不开启,一般情况下也不会开启,所以可以认为默认返回true。

InfrastructureAdvisorAutoProxyCreator会提前过滤掉不是spring基础设施bean类型的增强器:

InfrastructureAdvisorAutoProxyCreator最典型的使用案例就是Spring声明式事务,Spring声明式事务开启后会向容器中注入一个InfrastructureAdvisorAutoProxyCreator类型的自动代理创建器,同时向容器中注入一个用于处理事务的增强器:

该增强器被标识为了Spring基础设施bean,因此会被InfrastructureAdvisorAutoProxyCreator搜寻到,而其他非基础设施类型的增强器会被InfrastructureAdvisorAutoProxyCreator过滤掉。

spring事务模块源码解析


2.过滤候选增强器

过滤候选增强器集合,筛选出能够切入当前bean的增强器过程由AopUtils工具类完成:

	protected List<Advisor> findAdvisorsThatCanApply(
			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) 
        ...
			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
		...
	

这个工具类的findAdvisorsThatCanApply方法在上一篇文章中也带领大家阅读过,不清楚可以回看:

@Aspect注解背后的奥秘–上

核心过滤思路: 取出advisor内部的pointcut,先通过classFilter进行过滤,如果通过了,再判断目标对象包括其继承的所有接口是否有任何一个方法能够被methodMatcher切入,如果有当前advisor才会预留下来。


3.扩展增强器

AbstractAdvisorAutoProxyCreator提供的扩展增强器钩子方法默认是空实现,存在一个子实例类对该钩子方法进行了覆写:

AspectJAwareAdvisorAutoProxyCreator重写了该钩子方法:

	@Override
	protected void extendAdvisors(List<Advisor> candidateAdvisors) 
		AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
	

AspectJProxyUtils调用makeAdvisorChainAspectJCapableIfNecessary添加一个ExposeInvocationInterceptor到增强器链头部, 当然添加的前提是当前增强器集合中,存在由相关增强方法转换过来的advisor。

为啥要添加这样一个拦截器,上一篇文章也进行了解释,不清楚回看上一篇文章。

那么这里又产生了一个问题,AbstractAdvisorAutoProxyCreator提供的findCandidateAdvisors方法默认是去IOC容器中寻找所有类型为advisor的bean,并进行一波预先过滤,那么标注了@AspectJ注解的切面类难道就不管了吗?

肯定是要管的,那么又是在何时对切面类进行搜寻并完成解析转换工作的呢?

这个过程,待会分析。


4.对增强器进行排序

Spring中要对某个组件集合进行排序,都是借助的AnnotationAwareOrderComparator完成的:

	protected List<Advisor> sortAdvisors(List<Advisor> advisors) 
		AnnotationAwareOrderComparator.sort(advisors);
		return advisors;
	

核心排序逻辑在AnnotationAwareOrderComparator的getOrder方法中体现:

	@Override
	@Nullable
	protected Integer findOrder(Object obj) 
	    //先找是否实现了Order接口,如果实现了调用getOrder方法顺序值
		Integer order = super.findOrder(obj);
		if (order != null) 
			return order;
		
		//如果没有再查询是否标注了@Order注解,根据@Order注解提供的值作为顺序值
		return findOrderFromAnnotation(obj);
	
	
	//父类的getOrder方法实现 
	@Nullable
	protected Integer findOrder(Object obj) 
		return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null);
	 

compare方法是JDK提供的比较器接口,我们可以简单看一下OrderComparator对其实现:

	@Override
	public int compare(@Nullable Object o1, @Nullable Object o2) 
		return doCompare(o1, o2, null);
	

	private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) 
	   //实现了PriorityOrdered接口的优先级会更高
		boolean p1 = (o1 instanceof PriorityOrdered);
		boolean p2 = (o2 instanceof PriorityOrdered);
		if (p1 && !p2) 
			return -1;
		
		else if (p2 && !p1) 
			return 1;
		
       //否则正常获取每个组件的优先级,比较进行排序
		int i1 = getOrder(o1, sourceProvider);
		int i2 = getOrder(o2, sourceProvider);
		return Integer.compare(i1, i2);
	

	private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) 
		Integer order = null;
		//暂时先忽略OrderSourceProvider的作用,看最简单的排序过程实现
		...
		return (order != null ? order : getOrder(obj));
	

	protected int getOrder(@Nullable Object obj) 
		if (obj != null) 
			Integer order = findOrder(obj);
			if (order != null) 
				return order;
			
		
		//如果没有找到@Order注解或者实现Order接口,那么返回最低优先级
		return Ordered.LOWEST_PRECEDENCE;
	

如果我们想要指定增强器的优先级,可以通过自定义增强器,然后让增强器实现Order接口,或者在增强器类上标注@Order注解完成。

但是注意,上一篇文章中我们分析过,由切面类中的增强方法转换得到的advisor而言,他们的优先级和所属切面类优先级一致,无法手动指定。


搜寻所有切面类并完成解析转换过程

AnnotationAwareAspectJAutoProxyCreator覆写了AbstractAdvisorAutoProxyCreator的findCandidateAdvisors方法:

	@Override
	protected List<Advisor> findCandidateAdvisors() 
		// 调用父类的方法,去IOC容器中寻找所有可用的advisor类型bean
		List<Advisor> advisors = super.findCandidateAdvisors();
		// BeanFactoryAspectJAdvisorsBuilder负责完成从容器中获取所有切面类并进行解析转换得到一批advisor的任务
		if (this.aspectJAdvisorsBuilder != null) 
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		
		return advisors;
	

BeanFactoryAspectJAdvisorsBuilder承担了搜索切面类并完成解析转换的核心工作:

public List<Advisor> buildAspectJAdvisors() 
       //从容器中寻找出所有切面类,寻找过程比较耗时,通过aspectBeanNames缓存上一次找到的切面类beanName
       //不直接缓存切面类对应的bean实例,是考虑到某些切面类不是单例类型,每次获取需要重新创建
		List<String> aspectNames = this.aspectBeanNames;
        //双重锁确保并发安全  
		if (aspectNames == null) 
			synchronized (this) 
				aspectNames = this.aspectBeanNames;
				if (aspectNames == null) 
                    //存放结果的集合
					List<Advisor> advisors = new ArrayList<>();
					aspectNames = new ArrayList<>();
					//从容器中取出所有bean
                    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
                    //依次对每个bean进行panda
					for (String beanName : beanNames) 
						//套路和BeanFactoryAdvisorRetrievalHelper一样
                        //最终调用的是AnnotationAwareAspectJAutoProxyCreator的isEligibleAspectBean
                        //最终调用的方法会判断是否设置了AspectBeanNamePattern
                        //如果设置了就利用这个pattern进行预先过滤,过滤掉不符合命名规范的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.
						Class<?> beanType = this.beanFactory.getType(beanName, false);
						if (beanType == null) 
							continue;
						
                        //判断当前bean是否是切面类
						if (this.advisorFactory.isAspect(beanType)) 
							aspectNames.add(beanName);
							//构建切面元数据类
                            AspectMetadata amd = new AspectMetadata(beanType, beanName);
							//切面类类型为singleton的情况--这里不是spring中单例意思
                            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) 
                                //构建拥有切面元数据和切面实例的工厂
								MetadataAwareAspectInstanceFactory factory =
										new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                //advisorFactory利用MetadataAwareAspectInstanceFactory
                                //提供的切面元数据信息解析得到一批advisor
								List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                               	//如果切面类是spring单例bean,那么直接缓存当前切面类解析得到的一批
                                //增强器集合
								if (this.beanFactory.isSingleton(beanName)) 
									this.advisorsCache.put(beanName, classAdvisors);
								
								else 
                                    //如果切面类不是单例的,那么只会缓存MetadataAwareAspectInstanceFactory工厂对象,每次都需要利用该工厂对象获取一个新的切面实例
                                    //再利用advisorFactory解析获取一批advisor
									this.aspectFactoryCache.put(beanName, factory);
								
								advisors.addAll(classAdvisors);
							
							else 
                                //大部分情况下@AspectJ切面都是sigleton类型,其他情况可以不考虑
								...
						
					
					this.aspectBeanNames = aspectNames;
					return advisors;
				
			
		
        //存在缓存的情况下--满足下面这个条件,说明容器中不存在切面类
		if (aspectNames.isEmpty()) 
			return Collections.emptyList();
		
		List<Advisor>以上是关于@Aspect注解背后的奥秘--下的主要内容,如果未能解决你的问题,请参考以下文章

文智背后的奥秘系列篇——情感分类

.NET平台系列29:.NET Core 跨平台奥秘

文智背后的奥秘系列篇——结构化抽取平台

文智背后的奥秘系列篇——分布式爬虫之WebKit

SwiftUI 中某些环境自动更新背后的奥秘

借汇编之力窥探String背后的数据结构奥秘