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

Posted 热爱编程的大忽悠

tags:

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

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


引言

Spring为我们提供了简单易用的声明式AOP实现方案,我们只需要通过@Aspect注解标注一个切面类,并通过@Around,@Before等注解标注切面类中相关增强方法,注解内部标注切入范围,即可一键完成切入,程序员只需要关注切入后的拦截逻辑实现即可,下面给出的是一个简单的实现案例:

@Aspect
@Component
public class RateLimiterAspect 
    @Pointcut("")
    public void pointCut() 

    
    
    @Before("@annotation(limiter)")
    public void doBefore(JoinPoint point, Limiter limiter)
        Method method = getMethod(point);
        System.out.println("before拦截:  方法名为"+method.getName());
    

    @After("@annotation(limiter)")
    public void doAfter(JoinPoint point, Limiter limiter)
        Method method = getMethod(point);
        System.out.println("after拦截:  方法名为"+method.getName());
    

    @Around(value = "@annotation(limiter)")
    public Object doAround(ProceedingJoinPoint point, Limiter limiter) throws Throwable
        Method method = getMethod(point);
        System.out.println("around拦截:  方法名为"+method.getName());
        return point.proceed();
    

    @AfterReturning(value = "@annotation(limiter)",returning = "res")
    public void doReturning(JoinPoint point,Limiter limiter,Object res)
    
        Method method = getMethod(point);
        System.out.println("returning拦截:  方法名为"+method.getName());
    

    @AfterThrowing(value = "@annotation(limiter)",throwing = "ex")
    public void doThrowing(JoinPoint point,Limiter limiter,Exception ex)
    
        Method method = getMethod(point);
        System.out.println("throwing拦截:  方法名为"+method.getName());
    

    private Method getMethod(JoinPoint point) 
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        return method;
    

aop一键式开启的背后,究竟发生了什么呢? 本文将带领大家一步步探究这背后的奥秘。


aop的原始时代

在spring aop最初诞生时,还不支持自动化的aop实现,如果我们想要对某个bean进行代理,需要手动操作,例如:

public class ProxyTest 
    @Test
    public void test()
        noAspectJProxy();
        AspectJProxy();
    

    private void AspectJProxy() 
        AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory();
        aspectJProxyFactory.setTarget(new A());
        aspectJProxyFactory.addAspect(RateLimiterAspect.class);
        A proxyA = (A) aspectJProxyFactory.getProxy();
        proxyA.say();
    

    private void noAspectJProxy() 
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(new A());
        //强制采用cglib代理,这里不设置也是采用cglib进行代理,因为A没有实现任何接口
        proxyFactory.setProxyTargetClass(true);
        proxyFactory.addAdvice(new MethodInterceptor() 
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable 
                System.out.println("拦截器执行");
                return invocation.proceed();
            
        );
        A proxyA = (A) proxyFactory.getProxy();
        proxyA.say();
    


    public static class A 
        @Limiter
        public void say()
            System.out.println("A说你好!");
        
    


ProxyFactory和AspectJProxyFactory都是手动创建代理类的工厂实现,区别在于ProxyFactory添加的都是普通的advisor实现,内部已经提供了pointCut和Advice的实现,无需特别处理; 而AspectJProxyFactory添加的是切面类,负责将切面类进行解析,然后将其中的增强方法包装为一个个advisor,因此多了解析和适配的步骤。


ProxyFactory实现思路

由于Spring AOP模块涉及内容较多,所以受限于篇幅限制,本文只会简要提一下相关类起到的作用,具体源码大家可以自行查看,或者通过阅读我的Spring源码专栏中AOP源码解读部分进行学习。

  • ProxyConfig类提供相关属性控制代理的具体过程,如: proxyTargetClass选项强制设置采用cglib进行代理
  • AdvisedTagetClassAware两个接口提供了对目标对象和应用到目标对象上的advisor进行管理的相关接口
  • AdvisedSupport负责管理目标对象相关信息,如包装了目标对象的TargetSource和目前对象需要实现的接口数组interfaces;advisor的管理,其中一个核心是利用AdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法获取能够切入某个方法的advisor执行链。
  • ProxyCreatorSupport类的核心功能体现在create上,主要负责利用内部的AopProxyFactory创建代理对象
  • ProxyFactory负责创建代理对象的工厂类,核心通过getProxy方法得到一个代理对象,利用的是ProxyCreatorSupport提供的createAopProxy方法,相关代理信息的保存由AdvisedSupport负责完成。

AopProxyFactory是根据AdvisedSupport提供的代理配置信息创建AopProxy对象的工厂类。

AopProxy提供的getProxy方法负责创建真正的代理对象,而AopProxyFactory根据AdvisedSupport提供的代理配置信息决定选择何种方式生成代理对象,是jdk还是cglib,然后分别创建对应类型的AopProxy返回。

TargetSource包装了目标对象,默认使用的是SingletonTargetSource,每次获取目标对象时,只是简单返回内部封装的目标对象。


AspectJProxyFactory的实现思路


AspectJProxyFactory相比ProxyFactory多出了将切面类进行解析,并将内部的增强方法全部适配为一个个advisor的过程,这个过程体现在addAspectJ方法中:

    //添加的是标注了@AspectJ注解的切面类
	public void addAspect(Object aspectInstance) 
	    //获取切面类的Class
		Class<?> aspectClass = aspectInstance.getClass();
		//切面类的类名
		String aspectName = aspectClass.getName();
		//解析切面类形成切面类的元数据信息---如果传入的不是标注了@AspectJ注解的切面类,解析过程中会抛出异常
		AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
		//有关AspectJ的知识,通常切面类默认都是SINGLETON单例实现
		if (am.getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON) 
			throw new IllegalArgumentException(
					"Aspect class [" + aspectClass.getName() + "] does not define a singleton aspect");
		
		//该方法是解析切面类,并筛选advisor的核心方法
		addAdvisorsFromAspectInstanceFactory(
		     //负责实例化单例切面类的工厂--但是由于单例的切面类是通过构造函数传入的
		     //因此可以把工厂看做是保存切面类单例实例对象和切面类元数据的工厂类
				new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, aspectName));
	

addAdvisorsFromAspectInstanceFactory方法负责根据工厂类中保存的切面元数据信息解析并转换得到一批advisors,再利用advisor内部的poincut进行筛选,留下那些能够切入目标对象的advisors:

	private void addAdvisorsFromAspectInstanceFactory(MetadataAwareAspectInstanceFactory instanceFactory) 
	    //切面工厂负责根据instanceFactory内部的存储的切面元数据信息解析并转换得到一批advisor
		List<Advisor> advisors = this.aspectFactory.getAdvisors(instanceFactory);
		//得到目标对象类型--如果先addAspect,再设置目标对象,此时就会因为目标对象还未被及时设置而抛出异常
		Class<?> targetClass = getTargetClass();
		Assert.state(targetClass != null, "Unresolvable target class");
		//利用AopUtils判断哪些advisor能够切入当前目标对象,
		advisors = AopUtils.findAdvisorsThatCanApply(advisors, targetClass);
		//添加ExposeInvocationInterceptor到已有增强链头部
		AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(advisors);
		//对advisor进行排序
		AnnotationAwareOrderComparator.sort(advisors);
		addAdvisors(advisors);
	

1.切面元数据解析过程


根据切面元数据解析并转换得到一批advisors的工作是通过ReflectiveAspectJAdvisorFactory完成的:

	public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) 
	    //拿到切面类型和切面名
		Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
		String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
		//主要校验切面类上是否存在@AspectJ注解
		validate(aspectClass);
        //AspectInstanceFactory工厂作用是创建一个切面类实现,他有很多实现,例如我们上面看到的SingletonMetadataAwareAspectInstanceFactory
        //但是有一些实现类,可能每次调用getAspectInstance方法都会重新创建一个切面实例
        //因此LazySingleton的希望能够实现懒加载并且保证切面类的单例性--源码比较简单,大家可以自行阅读
		MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
				new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
        
		List<Advisor> advisors = new ArrayList<>();
		//getAdvisorMethods获取当前切面类,父类,接口中所有方法,除了标注了@PointCut注解的方法
		for (Method method : getAdvisorMethods(aspectClass)) 
		    //判断当前方法上是否存@Around等注解,如果存在则将方法转换为一个advisor
			Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
			//返回的advisor为null,说明当前方法上不存在相关注解
			if (advisor != null) 
				advisors.add(advisor);
			
		
		...
		// 处理切面类字段上存在的@DeclaredParent注解--用的比较少,这里直接跳过
		for (Field field : aspectClass.getDeclaredFields()) ...
		//返回解析切面类获得的一批advisors
		return advisors;
	

判断方法是否为增强方法,如果是则转换为advisor:

	public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
			int declarationOrderInAspect, String aspectName) 
	    ...		
	    //如果方法上存在@Before,@After相关注解,则提取注解中的切入表达式,包装为一个PointCut返回
		AspectJExpressionPointcut expressionPointcut = getPointcut(
				candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
		//返回空,说明当前方法不是增强方法
		if (expressionPointcut == null) 
			return null;
		
        //将当前增强方法封装为一个InstantiationModelAwarePointcutAdvisorImpl返回
        //该advisor内部持有增强方法,拥有切面实例的aspectInstanceFactory
        //还有上面得到的AspectJExpressionPointcut 
		return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
				this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
	    
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) 
		AspectJAnnotation<?> aspectJAnnotation =
		        //寻找@Before,@After相关注解
				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
		//不存在相关注解
		if (aspectJAnnotation == null) 
			return null;
		
        //存在则将注解中的切点表达式封装为一个AspectJExpressionPointcut
        //该AspectJExpressionPointcut的classFilter和methodMatcher方法都是根据切点表达式进行过滤的
		AspectJExpressionPointcut ajexp =
				new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
		ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
		if (this.beanFactory != null) 
			ajexp.setBeanFactory(this.beanFactory);
		
		//返回pointCut
		return ajexp;
	

2.InstantiationModelAwarePointcutAdvisorImpl获取advice

advisor是由pointcut和advice组成的,InstantiationModelAwarePointcutAdvisorImpl获取pointcut时,直接返回设置进去的AspectJExpressionPointcut 即可。

但是获取advice时,该怎么处理呢?

	public synchronized Advice getAdvice() 
	    //第一次获取advice时为空,先实例化
		if (this.instantiatedAdvice == null) 
			this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
		
		return this.instantiatedAdvice;
	

	private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) 
	    //利用aspectJAdvisorFactory工厂根据增强方法上的注解创建不同类型的advice实现
		Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
				this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
		return (advice != null ? advice : EMPTY_ADVICE);
	

aspectJAdvisorFactory工厂提供的主要功能接口如下:

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
			MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) 
		Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
		...
		AspectJAnnotation<?> aspectJAnnotation =
				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
		...
		AbstractAspectJAdvice springAdvice;
		//根据注解类型的不同创建不同类型的advice实现
		switch (aspectJAnnotation.getAnnotationType()) 
			case AtPointcut:
				if (logger.isDebugEnabled()) 
					logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
				
				return null;
			case AtAround:
				springAdvice = new AspectJAroundAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtBefore:
				springAdvice = new AspectJMethodBeforeAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfter:
				springAdvice = new AspectJAfterAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfterReturning:
				springAdvice = new AspectJAfterReturningAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
				//注解中指定了接收方法返回值的方法参数名字
				if (StringUtils.hasText(afterReturningAnnotation.returning())) 
					springAdvice.setReturningName(afterReturningAnnotation.returning());
				
				break;
			case AtAfterThrowing:
				springAdvice = new AspectJAfterThrowingAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
				//注解中指定了接收异常参数的方法参数名字
				if (StringUtils.hasText(afterThrowingAnnotation.throwing())) 
					springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
				
				break;
			default:
				throw new UnsupportedOperationException(
						"Unsupported advice type on method: " + candidateAdviceMethod);
		

		springAdvice.setAspectName(aspectName);
		springAdvice.setDeclarationOrder(declarationOrder);
		//参数绑定过程,如果以后遇到了参数绑定问题,可以debug此部分源码寻找原因
		String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
		if (argNames != null) 
			springAdvice.setArgumentNamesFromStringArray(argNames);
		
		springAdvice.calculateArgumentBindings();
		//返回最终生成的advice
		return springAdvice;
	

3.AopUtils筛选能够应用到当前目标对象上的advisors

这里我将AopUtils的findAdvisorsThatCanApply核心流程保留,去掉不常用的分支情况和代码:

	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) 
		if (candidateAdvisors.isEmpty()) 
			return candidateAdvisors;
		以上是关于@Aspect注解背后的奥秘--上的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

ReentrantLock源码探究探究公平锁与非公平锁背后的奥秘

sealer背后实现整个集群一键交付的奥秘 | 龙蜥技术

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