SpringAOP的一些基本概念及原理
seefly
seefly
发布于 2020-07-30 / 79 阅读 / 0 评论 / 0 点赞

SpringAOP的一些基本概念及原理

一、SpringAOP基本概念

1、JoinPoint 连接点

连接点就是程序调用过程中经过的一些点,例如方法调用、字段访问、异常处理等。在SpringAOP中仅支持到方法级别的连接点。所以后面所有的操作都是针对于方法级别的AOP。

joinPoint

2、PointCut 切入点

​ 由于所有方法上都具有连接点,而我们实际使用中只希望在指定的地方用AOP做增强处理,切入点的作用就是使用一组规则来匹配指定的连接点。可以看到AspectJExpressionPointcut实现了ClassFilter和MethodMatcher两个接口来实现对连接点的筛选。

pointCut

3、Advice 通知

​ 通知就是我们定义的横切逻辑,也就是需要在目标方法上增强的逻辑。上面的切点帮我们确定了“在那里”调用通知的能力,但还需要确定”何时“调用,所以通知有下面几种类型。

  • 前置通知(Before Advice)
  • 后置通知(After Advice)
  • 返回通知(After Returning Advice)
  • 环绕通知(Around Advice)
  • 异常通知(After Throwing Advice)

advice

4、Aspect/Advisor

​ 在AOP的概念中还有一个切面的概念,它并没有具体的类或接口来定义,只是一个概念。一个切面整合了切点和通知两个模块。就拿做一件事情来说,切点解决了 ”在哪里“ 的问题,通知解决了 ”什么时候“ 和 ”怎么做“ 的问题,这两者结合就是一个切面。但在spring中增强器(Advisor )的作用和切面很像。一个增强器只包含一个切点和一个通知。

advisor

二、SpringAOP初始化流程

Aop的初始化流程会包含在IOC容器的初始化流程中,这里只关注AOP初始化部分。

主要逻辑

  • @EnableAspectJAutoProxy引入AnnotationAwareAspectJAutoProxyCreator 到容器。

    这个东西看一下继承树是InstantiationBeanPostProcessor的子类,这意味着容器中所有Bean的实例化前

    实例化后、初始化前、初始化后都会调用它的逻辑。而我们的AOP功能的实现就依赖这些钩子方法。

  • 在Bean初始化前调判断该Bean是否被标注了@Aspect注解

  • 如果标注了注解就对该类型中的方法进行判断,如果被标注了@Before @AfterRetuning等注解的话

    就包装成一个增强器放入缓存。

  • 在所有Bean实例化并初始化之后会执行AOP后置通知,通过上面缓存的增强器判断当前Bean是否需要被增强

  • 如果需要被增强则判断使用哪种代理方式(JDK CGLIB),默认情况没有实现接口的话使用JDK,否则是CGLIb。当然@EnableAspectJAutoProxyproxyTargetClass=true 的话就强制使用CGLIb。

  • 最后返回被增强的代理对象,其中包含调用目标方法的执行链。这样就完成了增强

1、AnnotationAwareAspectJAutoProxyCreator创建增强器

​ 我们对容器中的实例做增强,首先需要我们定义好的一个个增强器,所以增强器的创建则在后置处理器的

2、创建代理对象

1>AbstractApplicationContext#refresh
  // 初始化所有非懒加载的单例
 2>DefaultListableBeanFactory#preInstantiateSingletons
  3>AbstractBeanFactory#getBean
   4>AbstractBeanFactory#doGetBean
    5>DefaultSingletonBeanRegistry#getSingleton
      // 创建bean实例,填充bean实例,应用后处理器等。
     6>AbstractAutowireCapableBeanFactory#createBean
      // 这一步比较重要,在每个Bean实例化之前会调用后置处理器的postProcessBeforeInstantiation
      // 方法,会尝试着让后置处理器去创建该Bean。如果后置处理器创建了Bean则直接返回
      // 跳过后面的填充、调用初始化逻辑等..
      // 而我们的AOP创建增强器则是在这一步完成,并不是直接在这里就创建代理对象了。
      7>AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
       // 循环调用所有后置处理器的postProcessBeforeInstantiation方法
       // 来尝试对该Bean进行实例化,而这里我们只关注AOP相关的后置处理
       8>AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation
        9>AbstractAutoProxyCreator#postProcessBeforeInstantiation
         // 判断是否应跳过,重写了父类的这个方法,这一步比较重要
         // 因为是在这里获取容器中所有的增强器
         10>AspectJAwareAdvisorAutoProxyCreator#shouldSkip  
          // 这里先调用父类的重载方法获取增强器,然后使用自己的逻辑再获取一遍。两个加起来
          11>AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
           // 【这一步非常重要】它从容器中拿出所有在容器中的类型
           // 判断类上是不是有@Aspect注解,有的话解析其中的方法
           // 生成一个一个的增强器,放到缓存中(advisorsCache)
           12>BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
            // 如果类上标注了@Aspect注解,获取它所有的方法进行遍历
            13>ReflectiveAspectJAdvisorFactory#getAdvisors
             // 通过AspectJExpressionPointcut类判断当前方法上是否标注了
             // @After @Before...注解,如果标注了。则创建一个切入点(AspectJExpressionPointcut)
             // 将之作为参数创建一个增强器(InstantiationModelAwarePointcutAdvisorImpl)返回
             14>ReflectiveAspectJAdvisorFactory#getAdvisor



// 上面这些是创建并缓存增强器
// 这里开始创建Bean实例,中间跳过一些初始化填充属性的环节,直接到应用AOP后置处理器的逻辑
1>AbstractAutoProxyCreator#postProcessAfterInitialization
 2>AbstractAutoProxyCreator#wrapIfNecessary
  3>AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean
    // 找到针对于当前Bean适用的增强器
    // 我们之前缓存的增强器会在这里取出来,然后排序并返回
   4>AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
    // 先找出之前缓存的所有增强器
    5>AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
    // 从这些增强器中拿出切入点,然后循环遍历当前Bean的方法进行匹配。匹配上的就返回这个增强器。
    // 所以一个类中的A B C 方法分别匹配了 a b c三种增强器。
    // 那么创建这个对象的代理的时候就会有3个增强器。在真正调用A的时候再执行匹配选出a增强器
    // 进行增强
    6>AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
   // 这时候就创建好了代理对象
 2>AbstractAutoProxyCreator#createProxy
  3>ProxyFactory#getProxy()
   4>ProxyCreatorSupport#createAopProxy
    // 在这里进行判断,有接口就走JDK代理,没有就走CGLIB
    5>DefaultAopProxyFactory#createAopProxy(AdvisedSupport config)

三、拦截器链的创建和执行

1、创建方法调用

这里演示CGLIB方式的动态代理,我们在调用目标方法时由于对象已经被代理增强,所以会执行到下面这个逻辑。主要就是从缓存中获取拦截器链,然后创建方法调用,由方法调用来驱动对整个拦截器链的执行。

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Object target = null;
			TargetSource targetSource = this.advised.getTargetSource();
			try {
			 // 判断是否暴露代理对象,如果是则将代理对象放到上下文中供以后使用
				if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
				target = targetSource.getTarget();
				Class<?> targetClass = (target != null ? target.getClass() : null);
				
				// 获取拦截器链接,即由多个MethodInterceptor组成的有序连
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// 判断连接器连是否为空,如果为空则直接调用目标方法
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
					retVal = methodProxy.invoke(target, argsToUse);
				}
				else {
					// 否则就创建一个方法调用对象,由MethodInvocation来驱动对拦截器链的遍历执行。
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null && !targetSource.isStatic()) {
					targetSource.releaseTarget(target);
				}
				if (setProxyContext) {
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

2、创建拦截器链

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
		MethodCacheKey cacheKey = new MethodCacheKey(method);
    // 先取缓存(也就是一个ConcurrentHahsMap)
		List<Object> cached = this.methodCache.get(cacheKey);
		if (cached == null) {
		  // 缓存没有,则创建拦截器链
		  // 里面的逻辑就是
		  // 1、拿出适用于当前实例的所有增强器
		  // 2、挨个遍历这些增强器判断是否能够适用于当前的方法
		  // 3、如果能够适用于当前的方法则将增强器中组合的Advice拿出来进行下一步操作
		  // 4、如果这个Advice是一个MethodInvocation那就直接放到拦截器链中
		  // 5、如果不是则使用对应的适配器来包装一下放到拦截器链中
		  // 例如:AspectJMethodBeforeAdvice 需要被 MethodBeforeAdviceInterceptor包装
		  // 因为它只实现了BeforeAdvice。
			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
			this.methodCache.put(cacheKey, cached);
		}
		return cached;
	}

3、调用拦截器链

拦截器链的调用入口即 CglibMethodInvocation.proceed()

public Object proceed() throws Throwable {
		//判断当前的拦截器是否处于拦截器链的最后一个,是的话就直接调用目标方法
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

    // 从拦截器链中拿出当前的拦截器(MethodInterceptor)
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // 调用MethodINterceptor的Invoke方法并传入自己(MethodInvocation)
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}

以异常通(AspectJAfterThrowingAdvice)知为例

public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			// 直接调用MethodInvocation.proceed()去执行下一个拦截器
			return mi.proceed();
		}
		catch (Throwable ex) {
		  // 如果抛出异常则调用异常通知
			if (shouldInvokeOnThrowing(ex)) {
				invokeAdviceMethod(getJoinPointMatch(), null, ex);
			}
			throw ex;
		}
	}

4、Aop调用流程

  • MethodInvocation.proceed()方法会从连接器链中拿出一个拦截器去执行,同时推进拦截器的执行位置,并自己传入其中。
  • MethodInterceptor.invoke(MethodInvocation)则负责执行自己的业务逻辑以及再次调用步骤1的方法
  • 这种像是两个方法在递归彼此,比单个方法的自我递归高级。这叫责任链模式

评论