1

1、先简单的概述下

关于切面的概念可以参考下这篇文章,已经解释的很好了: https://mp.weixin.qq.com/s/BYxXMAEIfOSCCx1KanP9lg

但是上面流于概念的讲解,少了一些具体源码上的分析。

下面按照我的理解,说明一下吧。

1.1 实现一个切面功能的建议代码

定义一个功能类

@Component
public class MathCalculator {

    public int divile(int a, int b) {

        // step1 : copy refence
        // step2 : patse whithout format : com.wy.aop.MathCalculator#divile
        return a / b;
    }

}

声明一个切面:

@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(public int com.wy.aop.MathCalculator.divile(..))")
    public void pointCut() {

    }

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        System.out
            .println(joinPoint.getSignature() + " Before 除法运行:方法名:" + "参数表:" + Arrays.asList(joinPoint.getArgs()));
    }

    @After("com.wy.aop.LogAspect.pointCut()")
    public void logEnd(JoinPoint joinPoint) {
        System.out.println(joinPoint.getSignature() + "After 除法结束:");
    }

    @AfterReturning(value = "pointCut()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result) {
        System.out.println(joinPoint.getSignature() + "AfterReturning 除法返回:" + result);
    }

    @AfterThrowing(value = "pointCut()", throwing = "exception")
    public void logExp(JoinPoint joinPoint, Exception exception) {
        System.out.println(joinPoint.getSignature() + "AfterThrowing 除法异常:" + exception.getMessage());
    }
}

配置配置类

@EnableAspectJAutoProxy
@ComponentScan(value = "com.wy.aop")
@Configuration
public class MainConfigOfAOP {

}

1.2 简单分析

具体可以分为三大步

1、通过 EnableAspectJAutoProxy 的注解:给IOC容器中注册一个组件:AnnotationAwareAspectJAutoProxyCreator
参考另外一篇文章:六. 容器的创建流程

AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器。

具体实现类名 :org.springframework.aop.config.internalAutoProxyCreator

internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator

2、初始化 bean 的时候,对目标方法获取增强的代理类:CglibAopProxy

finishBeanFactoryInitialization() 初始化剩下的单实例bean

  • 1)、创建业务逻辑组件和切面组件
  • 2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
  • 3)、组件创建完之后,判断组件是否需要增强

    是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);

把切面的通知方法,包装成增强器(Advisor)

给业务逻辑组件创建一个代理对象(CglibAopProxy)

3、使用 代理对象 CglibAopProxy 增强类,执行目标方法,触发拦截器链

通过 CglibAopProxy.intercept( ) 拦截。

执行顺序:

具体的代码流程:下面再论述。

2. @EnableAspectJAutoProxy

AOP 的核心理解:

  1. 看给容器中注册了什么组件?
  2. 这个组件什么时候工作?
  3. 这个组件的功能是什么?

2.1 @EnableAspectJAutoProxy 声明了什么?

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
  
}

通过这个注解,从 IOC 容器中定义一个组件 : org.springframework.aop.config.internalAutoProxyCreator

image-20200112164437805.png

image-20200112165803823.png

具体的 bean 是 : AnnotationAwareAspectJAutoProxyCreator

2.2 internalAutoProxyCreator 是什么?

先看看他的实现继承结构

image-20200112164838790.png

主要关注: 它实现了 BeanPostProcessor (在bean初始化完成前后做事情)、自动装配BeanFactory (Aware)

3. AnnotationAwareAspectJAutpProxyCreator 做了什么?

此节请结合 六、容器的创建流程

3.1 postProcessBeforeInstantiation

关键源码如下 :

if (this.advisedBeans.containsKey(cacheKey)) {
  return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
  this.advisedBeans.put(cacheKey, Boolean.FALSE);
  return null;
}

这里主要关心 业务类(MathCalculator) 和 切面类(LogAspect)

大致步骤如下:

1、判断当前 bean 是否在 advisedBeans 中(保存了所有需要增强bean)

2、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,

或者是否是切面(@Aspect)

3、是否需要跳过

如果包含 则直接 ruturn

如果都不包含,则进入后续,做一些简单的处理

image-20200112154555679.png

把所有的切面对应的增强信息 包装成了 Advisor

InstantiationModelAwarePointcutAdvisor: expression [pointCut()]; advice method [public void com.wy.aop.LogAspect.logStart(org.aspectj.lang.JoinPoint)]; perClauseKind=SINGLETON

增强器是 : InstantiationModelAwarePointcutAdvisor

所以 postProcessBeforeInstantiation 主要做一些前置工作。

3.2 postProcessAfterInitialization

bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

主要步骤: wrapIfNecessary

总结:经过这一步骤。如果这个bean 有canApply 的Adivse 。就会得到一个增强类的代理对象 CglibAopProxy

return wrapIfNecessary(bean, beanName, cacheKey);

// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);


1、获取当前 bean 的所有增强器

返回的 specificInterceptors 数组

  • 1、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
  • 2、获取到能在bean使用的增强器。
  • 3、给增强器排序
具体的可以 debug 看下源码

2. 保存当前 bean 在 advisedBeans

this.advisedBeans.put(cacheKey, Boolean.TRUE);

标识当前 bean 已经被增强过

3. 创建当前 bean 的代理对象

Object proxy = createProxy(
      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

给容器中返回当前组件使用cglib增强了的代理对象;

之后,容器中获得到的就是这个组件(bean:MathCalculator)的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;也就是切面的执行流程


所以保护 Advice 的 bean 的生命周期是:

image-20200112163341840.png

4. 使用增强器

在调用目标方法的时候 :

MathCalculator mathCalculator = ctx.getBean(MathCalculator.class);
// new MathCalculator() 不会起作用 因为没有使用容器 spring容器管理的 
int divile = mathCalculator.divile(4, 2);

此时的目标方法 是 : CglibAopProxy 增强后的代理对象。 里面包括 五个增强器(Advice)

保存了通知方法的信息。

4.1 使用拦截器 intercept

通过 CglibAopProxy.intercept() 拦截目标方法的执行

首先获取 Interception 链

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

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    retVal = methodProxy.invoke(target, argsToUse);
}
else {
        // We need to create a method invocation...
        retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain,         methodProxy).proceed();
}

拦截器链。 就是多个 intercept 组成的链式结构

核心 是1. 如何获取的拦截器连

2.拦截器链是如何工作的

4.2 如何获取拦截器链

源码类 : org/springframework/aop/framework/DefaultAdvisorChainFactory.java

this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);

image-20200112173048296.png

遍历所有的增强器转换为 MethodInterceptor

获得 list 就是拦截器链 chain

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

最终目的 : 获取CglibMethodInvocation,调用 proceed() 方法

就是触发拦截器链的触发过程。

4.3 拦截器链的触发过程

执行 实现类的方法

int divile = mathCalculator.divile(4, 2);

源码如下:

@Override
@Nullable
public Object proceed() throws Throwable {
  //    We start with an index of -1 and increment early.
  if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    return invokeJoinpoint();
  }

  Object interceptorOrInterceptionAdvice =
    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    // Evaluate dynamic method matcher here: static part will already have
    // been evaluated and found to match.
    InterceptorAndDynamicMethodMatcher dm =
      (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
      return dm.interceptor.invoke(this);
    }
    else {
      // Dynamic matching failed.
      // Skip this interceptor and invoke the next in the chain.
      return proceed();
    }
  }
  else {
    // It's an interceptor, so we just invoke it: The pointcut will have
    // been evaluated statically before this object was constructed.
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  }

拦截器链的触发过程;

  • 1)、如果没有拦截器执行执行目标方法,或者执行到了最后一个拦截器 。 执行目标方法;
  • 2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;

    拦截器链的机制,保证通知方法与目标方法的执行顺序;

为了减少企业,先看一下调用的流程图

这个设计的也太经典了

image-20200112182215734.png

对应的调用链:

image-20200112174559572.png

执行代码:

还没有执行方法前。 依次调用所有的Aspect 的拦截器

image-20200112175556628.png

执行最后一个前置的时候

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
  this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
  return mi.proceed();
}

然后执行目标方法 : invokeJoinpoint()

然后执行

try {
  return mi.proceed();
}
finally {
  // 返回上一次方法
  // 每一个上级方法 Advise 都有自己单独做的事情
  invokeAdviceMethod(getJoinPointMatch(), null, null);
}

每一个拦截器等待下一个拦截器 执行完之后 返回在执行

依次是

@Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object retVal = mi.proceed();
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }

如果抛出异常:

try {
  return mi.proceed();
}
catch (Throwable ex) {
  if (shouldInvokeOnThrowing(ex)) {
    invokeAdviceMethod(getJoinPointMatch(), null, ex);
  }
  throw ex;
}

拦截器链。 保证了通知方法和目标方法的顺序, before invoke return 的顺序


天真真不知路漫漫
70 声望6 粉丝

1