AOP原理总结

AOP原理总结

AOP启动

启动之前做的事情:

在启动之前做的事情是IOC解析配置文件注册BeanDefination,在这个阶段会将AOP的配置解析到BeanDefination中,待后续的使用。

启动AOP的注解为@EnableAspectJAutoProxy,通过进入这个注解能够看到引入了AspectJAutoProxyRegistrar到IOC容器中。
AOP原理总结_第1张图片

AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,能够拿到目前所有注册的BeanDefinition还可以自定义逻辑来动态注册BeanDefinition,这就意味着我们可以将AOP加强后的代理类注册到IOC容器当中(我猜的),实际上AspectJAutoProxyRegistrar当前做的就是将AspectJAnnotationAutoProxyCreator注册到IOC中。那么AspectJAnnotationAutoProxyCreator是怎么生成代理类的呢?
AOP原理总结_第2张图片

从这个类结构图看出AspectJAnnotationAutoProxyCreator是实现了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口。结合Bean的生命周期来看:AspectJAnnotationAutoProxyCreator会在IOC生成Bean实例的前后postProcessBeforeInstantiation和postProcessAfterInitialization,所以核心的初始化方法肯定在postProcessBeforeInstantiation和postProcessAfterInitialization中。

AOP原理总结_第3张图片

事实上也是如此 postProcessBeforeInstantiation 在创建Bean实例之前就会找到所有的通知Advice然后将其封装成Adivisor,下一步postProcessAfterInitialization在Bean实例化并且初始化之后找到对应的Adivisor利用cglib和jdk生成代理。

寻找Adivisor

到postProcessBeforeInstantiation中寻找:

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);

    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        // 如果已经在缓存中,则忽略
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        // 是否是aop基础类?是否跳过?
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }

    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    // 如果我们有自定义 TargetSource,请在此处创建代理。抑制目标 bean 的不必要的默认实例化:TargetSource 将以自定义方式处理目标实例。
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    return null;
}

其中有一个关键的方法shouldSkip()判断是否跳过,在这个函数中会判断当前类是否是切面类,实现的逻辑如下

@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    // TODO: Consider optimization by caching the list of the aspect names
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    for (Advisor advisor : candidateAdvisors) {
        if (advisor instanceof AspectJPointcutAdvisor &&
                ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
            return true;
        }
    }
    return super.shouldSkip(beanClass, beanName);
}

先得到所有的Advisor,然后遍历Advisor获得其切面的名称看是否和当前类的名称一样。

findCandidateAdvisors会得到所有的Advisor并保存在缓存中,这个就是我们要找的函数,但又不完全是因为里面又调用了父类的findCandidateAdvisors函数根据父类规则得到spring advisor,然后又this.aspectJAdvisorsBuilder.buildAspectJAdvisors()函数,这个才是寻找我们自己定义的advice。重点就是buildAspectJAdvisors。

这个函数的代码有点冗长,我提取一下关键点:

public List<Advisor> buildAspectJAdvisors() {
    // 得到aspectBeanNames,根据aspectNames就可以得到缓存中的advisor
    List<String> aspectNames = this.aspectBeanNames;
	// 两个if 判断加上 synchronized锁实现DCL双重锁 保证只对aspectNames赋值一次,单例的实现方式
    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                ····
                // 获取所有的Spring BeanName
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                // 遍历
                for (String beanName : beanNames) {
                    ····
                     // 获取对象类型
                    Class<?> beanType = this.beanFactory.getType(beanName, false);
                    // 判断是否是切面类(开始IOC进行BeanDefinition注册的时候会得到这个信息)
                    if (this.advisorFactory.isAspect(beanType)) {
                        ····
                            // 根据Bean建立AspectInstanceFactory然后获得该类中的Advisor
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                        ····
                            // 加入到缓存中                   
                            advisors.addAll(classAdvisors);
                        }
                        else {
                           ····
                        }
                    }
                }
                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    }
// 根据aspectName从缓存中获得advisor
    for (String aspectName : aspectNames) {
        ····
    }
    return advisors;
}

所以我们看到第一次根据每个Bean解析Advisor关键的位置还是在getAdvisors中,在这个函数中会遍历Bean的中的方法,找到advice方法,然后使用getAdvisor方法将advice方法封装成advicor返回,将得到的advicor存放到列表中返回。

getAdvisor的大概过程是提取advice的切入点,方法名,方法参数,class对象等信息作为advisor的属性信息,再使用instantiateAdvice调用getAdvice根据advice方法得到注释类型(Before, After, AfterReturning等)生成相应的advice,并设定该advice的参数列表和次序等信息。

到此位置我们找到了所有的advisor,虽然下面有创建代理的部分但是这个根据注释我们可以看到这个是为自定义的TargetSource创建代理的,真正的代理创建是在postProcessAfterInitialization中的。

创建代理两步走 1.寻找能使用advisor 2.根据advisor创建代理

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   // 如果bean是通过TargetSource接口获取
   if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   // 如果bean是切面类
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   // 如果是aop基础类?是否跳过?
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

  // 重点:获取所有advisor,如果没有获取到,那说明不要进行增强,也就不需要代理了。
  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  if (specificInterceptors != DO_NOT_PROXY) {
    this.advisedBeans.put(cacheKey, Boolean.TRUE);
    // 重点:创建代理
    Object proxy = createProxy(
        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
  }

  this.advisedBeans.put(cacheKey, Boolean.FALSE);
  return bean;
}

筛选适配advisor

getAdvicesAndAdvisorsForBean函数的作用是筛选这个应用到这个类上的Advisor,其中使用了findEligibleAdvisors来寻找,而findEligibleAdvisors在从缓存中得到所有的advisor之后使用findAdvisorsThatCanApply来过滤可用的,最后使用的是AopUtils.findAdvisorsThatCanApply(),这个函数的逻辑是遍历所有的advisor,使用canApply(…)判断该advisor是否能使用,能则加入到列表中。关键的就是canApply(…)函数。

canApply的逻辑是将当前类的所有public方法和继承的父类的方法都取出来遍历,如果该方法的路径满足当前advice的切入点那么就判断能够使用。

AOP原理总结_第4张图片

至此我们找到了所有能够在当前类上使用的advisor,下一步就是根据这些advisor为目标类创建代理类了。

创建代理类

在流程中创建代理的方法为

 Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);

createProxy函数主要目的是生成一个ProxyFactory,然后跟据设置配置ProxyFactory使用JDK或者cglib,再将beanname和对应的advisors设置在proxyfactory中。配置好后调用proxyfactory.getProxy函数得到目标类的代理类。

getProxy的内容如下,先创建一个AopProxy,然后得到代理类

createAopProxy().getProxy(classLoader);

createAopProxy()根据配置选择创建jdk或者cglib动态代理

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  if (!NativeDetector.inNativeImage() &&
      (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
    Class<?> targetClass = config.getTargetClass();
    if (targetClass == null) {
      throw new AopConfigException("TargetSource cannot determine target class: " +
          "Either an interface or a target is required for proxy creation.");
    }
    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
      return new JdkDynamicAopProxy(config);
    }
    return new ObjenesisCglibAopProxy(config);
  }
  else {
    return new JdkDynamicAopProxy(config);
  }
}

得到AOP对象之后调用getProxy得到目标类动态代理(这里以JDK为例):

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}

关键的地方在于Proxy.newProxyInstance,这个函数接受 类加载器, 目标类的接口集合, InvocationHandler的实现类。JDK动态代理的实现机制就是创建一个新的代理类继承了Proxy,并实现类目标类接口。代理类继承了Proxy其中会有一个InvocationHandler类型的属性h,当调用代理类方法的时候实际上就是调用InvocationHandler中的invoke方法,invoke方法中会根据配置执行先后逻辑并执行目标类的方法。

关键的地方在于invoke函数的实现,是如何将advisor织入到切入点的。

@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    ····

    try {
        // 首先判断是否是equal方法,hashcode方法,包装类等,如果是则不拦截直接执行
        // 执行的是equal方法
        ····· 

        // 获取拦截链,该拦截链前面我们已经获取了并排序好
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        // 如果拦截链为空则直接执行原方法
        if (chain.isEmpty()) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 如果存在拦截链则根据拦截链创建一个MethodInvocation调用拦截链的方法
            MethodInvocation invocation =
                    new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 调用拦截链的关键就是proceed
            retVal = invocation.proceed();
        }
		····
    }

下面看一下proceed是如何根据递归遍历所有的拦截链并执行原方法的。

AOP原理总结_第5张图片

根据注释我们可以看到进入proceed时会先判断是否是最后一个拦截器了,如果是则执行目标方法,否则执行该拦截器的interceptor.invoke方法,在这个invoke方法中会递归的调用proceed方法,不同的拦截器有不同的实现方式例如

MethodBeforeAdviceInterceptor#invoke(…)

AOP原理总结_第6张图片

AspectJAfterAdvice#invoke(…)

AOP原理总结_第7张图片

可以想象成先从第一个拦截器遍历到最后一个拦截器,然后再回到第一个这样,从入口到底部再回到入口的回溯过程。

至此我们探析了invoke的实现过程和jdk创建代理类的原理,并得到了目标类的代理。AOP的总体过程堂堂完结!!

总结:如果用自己的话来总结AOP代理的流程,我想我会这么说:AOP的实现依赖于IOC,刚开始IOC进行BeanDefination注册的时候会将AOP切面类的信息包括切入点,通知类型,通知方法等信息解析到BeanDefination中。然后通过开启AOP代理引入关键的类AspectJAnnotationAutoProxyCreator,将其注册到IOC中。该类利用IOC创建Bean的生命周期,实现两个生命周期函数postProcessBeforeInstantiation和postProcessAfterInitialization,在创建Bean实例的前后执行这两个函数。其中postProcessBeforeInstantiation是首先执行的,里面有一个关键的函数buildAspectJAdvisors,用来寻找IOC所有的advice并封装成advisor,大概的逻辑是遍历所有类的所有方法如果是advice则封装(用到了双重锁的方式确保返回单例)。当Bean实例化完成之后(没有实例化前无法生成代理类的,因为不知道目标类的字节码),开始执行另一个生命周期函数postProcessAfterInitialization,在这个函数中首先会过滤advisor得到当前类能使用的advisor,大致流程是遍历所有的advisor,如果该advisor的切入点表达式满足该类的任意一个方法(父类中的方法和接口中的方法)就判断为能使用。得到可以使用的advisor下一步就是生成代理类了,按照配置判断使用jdk或者cglib代理。如果使用jdk代理会得到一个实现了InvocationHandler中invoke方法的Proxy对象,在invoke方法中会将advisors配置为一个能递归调用的拦截链,递归方法为Process(),该方法会获取当前的advisor,判断当advisor能否拦截当前要执行的方法,如果可以则进入到advisor中执行相应的逻辑,并再次递归执行process处理下一个advisor,如此就能够对每一个方法遍历了所有能够使用advisor。proxy会将使用newProxyInstance将自身作为InvocationHandler传入得到目标类的代理类,该代理类是继承了proxy并实现了目标类接口,重写了所有目标类中的接口函数,在重写的函数中实际上就是调用InvocationHandler的invoke函数执行拦截器和目标函数(InvocationHandler作为参数传入了newProxyInstance中,会作为生成的代理类的属性值)。

你可能感兴趣的:(java,spring,boot)