写点什么

Spring 系列之 AOP 工作过程详解二

作者:王威07325
  • 2023-05-29
    上海
  • 本文字数:11083 字

    阅读完需:约 36 分钟

引言

还是一星期一更的经典栏目,如约而至



上回我们说到了 AOP 的 xml 解析过程,今天这篇文章,我们来讲解“代理子类生成”

入口

代理子类的生成的核心类 AspectJAwareAdvisorAutoProxyCreator,这个类是在解析“proxy-target-class”这个属性的时候进行注册,也就是 ConfigBeanDefinitionParser 的 configureAutoProxyCreator 方法中注册:


public static void registerAspectJAutoProxyCreatorIfNecessary(      ParserContext parserContext, Element sourceElement) {    //也就是在这里进行的AspectJAwareAdvisorAutoProxyCreator类的注册工作    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(        parserContext.getRegistry(), parserContext.extractSource(sourceElement));    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);    registerComponentIfNecessary(beanDefinition, parserContext);  }
复制代码


现在已经知道代理子类的核心的注入到 spring 中的过程,那么这个类的真正工作过程在哪呢?



看 AspectJAwareAdvisorAutoProxyCreator 的类图,我们可以知道,这个类实现了 SmartInstantiationAwareBeanPostProcessor,所以很容易想到入口应该位于此接口及其父接口(BeanPostProcessor)的相关方法中。实际上确实是这样的。

postProcessBeforeInstantiation(前置处理回调)

  1. 调用时机


不知道各位还是否记得之前在 spring 的 IOC 系列中 Bean 创建的过程中的调用时机,如果不记得自己回去再看一下(默认大家都记得😄)


AbstractAutowireCapableBeanFactory#createBean:


//// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {    return bean;}Object beanInstance = doCreateBean(beanName, mbdToUse, args);
复制代码


可以看出,调用发生在 Bean 实例的创建之前。


  1. 工作源码


AbstractAutoProxyCreator#postProcessBeforeInstantiation


@Override  public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {    Object cacheKey = getCacheKey(beanClass, beanName);
if (beanName == null || !this.targetSourcedBeans.contains(beanName)) { if (this.advisedBeans.containsKey(cacheKey)) { return null; } 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. if (beanName != null) { TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { 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; }
复制代码


这个方法逻辑有点复杂,我们还是分部分去讲解:


是否代理


Spring 首先会对当前的 beanClass 进行检查(是否应该/可以对其进行代理)。


不应该代理的类分为两种情况:


  • 用于实现 AOP 的 Spring 基础类,此种情况在 isInfrastructureClass 方法中完成检测(单词 Infrastructure 正是基础设施的意思)。

  • 子类定义的应该跳过的类,默认 AbstractAutoProxyCreator 的实现直接返回 false,即都不应该跳过


对应代码:


if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {      if (this.advisedBeans.containsKey(cacheKey)) {        return null;      }      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {        this.advisedBeans.put(cacheKey, Boolean.FALSE);        return null;      }    }
复制代码


其中 AbstractAutoProxyCreator.isInfrastructureClass()方法返回给定的 bean 类是否代表一个不应该被代理的基础设施类。默认实现将 Advices、Advisors 和 AopInfrastructureBeans 视为基础设施类。


protected boolean isInfrastructureClass(Class<?> beanClass) {    boolean retVal = Advice.class.isAssignableFrom(beanClass) ||        Pointcut.class.isAssignableFrom(beanClass) ||        Advisor.class.isAssignableFrom(beanClass) ||        AopInfrastructureBean.class.isAssignableFrom(beanClass);    if (retVal && logger.isTraceEnabled()) {      logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");    }    return retVal;  }
复制代码


AbstractAutoProxyCreator#shouldSkip()如果给定的 bean 不应被此后处理器考虑自动代理,则子类应覆盖此方法以返回 true 。有时我们需要能够避免这种情况发生,如果它会导致循环引用。此实现返回 false


protected boolean shouldSkip(Class<?> beanClass, String beanName) {    return false;  }
复制代码


那么此方法跳过的是谁呢?其实就是我们通过 aop:aspect 标签配置的切面,即:


<bean id="aopAdvice" class="base.aop.AopDemoAdvice" /><aop:config>    <aop:aspect ref="aopAdvice">    </aop:aspect></aop:config>
复制代码


里的 aopAdvice。


这体现在 AspectJAwareAdvisorAutoProxyCreator 重写其父类的 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) {        if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {          return true;        }      }    }    return super.shouldSkip(beanClass, beanName);  }
复制代码


从前面的 aop:aspect 标签的解析过程,Spring 对于 aop:config 的解析其实是把 aop:before/after 等标签解析成为了 AspectJPointcutAdvisor 类型的 BeanDefinition,而 aopAdvice 以 AbstractAspectJAdvice 的类型保存在其中。


我们不难得出: Spring 跳过的是适用于当前 bean 的 Advisor 的 Advice/Aspect 对象


到这里,我们可以将 AOP 的逻辑归纳一下,如下图所示:



Advisor 寻找


关键便是 findCandidateAdvisors 方法,最终调用 BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans:


    public List<Advisor> findAdvisorBeans() {    // Determine list of advisor bean names, if not cached already.    String[] advisorNames = null;    synchronized (this) {      //结果缓存      advisorNames = this.cachedAdvisorBeanNames;      if (advisorNames == null) {        //去容器中寻找,不要在这里初始化 FactoryBeans:我们需要让所有常规 bean 保持未初始化状态,以便让自动代理创建者应用到它们        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(            this.beanFactory, Advisor.class, true, false);        this.cachedAdvisorBeanNames = advisorNames;      }    }    if (advisorNames.length == 0) {      return new LinkedList<Advisor>();    }
List<Advisor> advisors = new LinkedList<Advisor>(); for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isDebugEnabled()) { logger.debug("Skipping currently created advisor '" + name + "'"); } } else { try { advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException) { BeanCreationException bce = (BeanCreationException) rootCause; if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) { if (logger.isDebugEnabled()) { logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } // Ignore: indicates a reference back to the bean we're trying to advise. // We want to find advisors other than the currently created bean itself. continue; } } throw ex; } } } } return advisors; }
复制代码


可以看出,首先是从容器中获取到所有的 Advisor 示例,然后调用 isEligibleBean 方法逐一判断 Advisor 是否适用于当前 bean


适用性检测


确定具有给定名称的方面 bean 是否符合条件。默认实现总是返回 true 。


protected boolean isEligibleAdvisorBean(String beanName) {    return true;}
复制代码


而 AbstractAdvisorAutoProxyCreator 的子类 AspectJAwareAdvisorAutoProxyCreator 并没有覆盖此方法,所以此处会对容器中所有的 Advisor 的 Advice 进行跳过。


检测结果缓存因为 postProcessBeforeInstantiation 方法会在每个 bean 初始化之前被调用,所以没有必要每次都真的进行基础类检测和跳过类检测,Spring 使用了 advisedBeans 作为缓存用以提高性能,advisedBeans 是通过 ConcurrentHashMap 实现的一个容器,初始容量为 256。


private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<Object, Boolean>(256);
复制代码


TargetSource 从源码中可以看出,对于自定义的 TargetSource,Spring 会立即执行代理子类的创建。Spring 的代理其实是针对 TargetSource 的


//如果我们有自定义 TargetSource,请在此处创建代理。//抑制目标 bean 的不必要的默认实例化://TargetSource 将以自定义方式处理目标实例if (beanName != null) {      //为 bean 实例创建目标源。如果设置,则使用任何 TargetSourceCreators。如果不应使用自定义 TargetSource,则返回null 。      //此实现使用“customTargetSourceCreators”属性。子类可以重写此方法以使用不同的机制      TargetSource targetSource = getCustomTargetSource(beanClass, beanName);      if (targetSource != null) {        this.targetSourcedBeans.add(beanName);        //返回是否要代理给定的 bean、要应用的附加建议(例如 AOP 联盟拦截器)和顾问        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);        //为给定的 bean 创建一个 AOP 代理        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);        this.proxyTypes.put(cacheKey, proxy.getClass());        return proxy;      }    }
复制代码


到这里,beanpostprocessor 中的前置处理回调就讲解完毕,那么我们来看看后置处理的逻辑是怎么样的:

postProcessAfterInitialization(后置处理回调)

AbstractAutoProxyCreator#postProcessAfterInitialization:


@Override  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {    if (bean != null) {      Object cacheKey = getCacheKey(bean.getClass(), beanName);      if (!this.earlyProxyReferences.contains(cacheKey)) {        return wrapIfNecessary(bean, beanName, cacheKey);      }    }    return bean;  }
复制代码


wrapIfNecessary:


如有必要,包装给定的 bean,即如果它有资格被代理


protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {    //自定义TargetSource,已经进行过代理子类生成    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {      return bean;    }    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {      return bean;    }    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {      this.advisedBeans.put(cacheKey, Boolean.FALSE);      return bean;    }
// Create proxy if we have advice. 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 方法,这里进行的便是去容器中寻找适用于当前 bean 的 Advisor,最终调用的是


AbstractAdvisorAutoProxyCreator.findEligibleAdvisors:


protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {    //findCandidateAdvisors这个方法之前已经说过了,查找出所有的增强    List<Advisor> candidateAdvisors = findCandidateAdvisors();    //搜索给定的候选顾问以查找可以应用于指定 bean 的所有顾问    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);    //给定迄今为止获得的排序顾问,子类可以覆盖以注册其他顾问的扩展挂钩。默认实现为空。通常用于添加顾问,这些顾问公开一些后来的顾问所需的上下文信息    extendAdvisors(eligibleAdvisors);    if (!eligibleAdvisors.isEmpty()) {    //对所有的增强进行排序      eligibleAdvisors = sortAdvisors(eligibleAdvisors);    }    return eligibleAdvisors;  }
复制代码


适用性判断 findAdvisorsThatCanApply 最终调用 AopUtils.findAdvisorsThatCanApply:


public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {    if (candidateAdvisors.isEmpty()) {      return candidateAdvisors;    }    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();    for (Advisor candidate : candidateAdvisors) {      if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {        eligibleAdvisors.add(candidate);      }    }    boolean hasIntroductions = !eligibleAdvisors.isEmpty();    for (Advisor candidate : candidateAdvisors) {      if (candidate instanceof IntroductionAdvisor) {        // already processed        continue;      }      if (canApply(candidate, clazz, hasIntroductions)) {        eligibleAdvisors.add(candidate);      }    }    return eligibleAdvisors;  }
复制代码


canApply:IntroductionAdvisor 在 Advisor 链中总是位于非 IntroductionAdvisor 前面


public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {    if (advisor instanceof IntroductionAdvisor) {      return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);    }    else if (advisor instanceof PointcutAdvisor) {      PointcutAdvisor pca = (PointcutAdvisor) advisor;      return canApply(pca.getPointcut(), targetClass, hasIntroductions);    }    else {      // It doesn't have a pointcut so we assume it applies.      return true;    }  }
复制代码


很明显,对于引入 Advisor 与其它 Advisor 是两种不同的判断方式。引入引入的概念在下面 aop:scoped-proxy 中有提到。因为引入的目的在于动态地向一个类添加另一种功能(接口),所以只要判断给定的类是否是要引入到的类即可其它 AopUtils.canApply:


public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {    Assert.notNull(pc, "Pointcut must not be null");    //是否Pointcut可以匹配当前类    if (!pc.getClassFilter().matches(targetClass)) {      return false;    }    //是否Pointcut可以匹配所有方法    MethodMatcher methodMatcher = pc.getMethodMatcher();    if (methodMatcher == MethodMatcher.TRUE) {      // No need to iterate the methods if we're matching any method anyway...      return true;    }
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; }
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); classes.add(targetClass); for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { if ((introductionAwareMethodMatcher != null && introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) || methodMatcher.matches(method, targetClass)) { return true; } } }
return false; }
复制代码


Advisor 扩展 AbstractAdvisorAutoProxyCreator.extendAdvisors 允许子类向 Advisor 链表中添加自己的 Advisor。最终调用 AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary 方法。


方法中的逻辑:如果 Advisor 链表中的 Advisor 含有 AspectJ Advice,那么将会把一个 ExposeInvocationInterceptor 添加到链表的表头,目的在于将 MethodInvocation 以 ThreadLocal 的方式暴露给后面所有的 Advisor


public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {    // Don't add advisors to an empty list; may indicate that proxying is just not required    if (!advisors.isEmpty()) {      boolean foundAspectJAdvice = false;      for (Advisor advisor : advisors) {        // Be careful not to get the Advice without a guard, as        // this might eagerly instantiate a non-singleton AspectJ aspect        if (isAspectJAdvice(advisor)) {          foundAspectJAdvice = true;        }      }      if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {        advisors.add(0, ExposeInvocationInterceptor.ADVISOR);        return true;      }    }    return false;  }
复制代码


排序即 sortAdvisors 方法,用于对实现了 Ordered 接口的 Advisor 进行排序。


protected List<Advisor> sortAdvisors(List<Advisor> advisors) {    AnnotationAwareOrderComparator.sort(advisors);    return advisors;  }
复制代码


创建这里省去了一些非关键的内容,这里的入口是在 wrapIfNecessary 方法中。AbstractAutoProxyCreator#createProxy


protected Object createProxy(      Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) { AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass); }
ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } //将interceptor适配为Advisor Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); }
return proxyFactory.getProxy(getProxyClassLoader()); }
复制代码


spring 通过两种方式去创建代理类,一种是 jdk 动态代理(需要实现接口),一种是 cglib 动态代理(生成一个代理子类),如何决定用那种方式去创建动态代理,则是由 DefaultAopProxyFactory.createAopProxy 方法决定。


public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {    if (config.isOptimize() || config.isProxyTargetClass() ||            hasNoUserSuppliedProxyInterfaces(config)) {        Class<?> targetClass = config.getTargetClass();        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {            return new JdkDynamicAopProxy(config);        }        return new ObjenesisCglibAopProxy(config);    } else {        return new JdkDynamicAopProxy(config);    }}
复制代码


逻辑很明显,如果指定了(proxy-target-classs 设为 true)使用 Cglib,那么就会使用 Cglib 的方式,如果没有指定(或为 false),那么先回检测被代理类是否实现了自己的接口,如果实现了,那么就采用 JDK 动态代理的方式。


jdk 动态代理实现:JdkDynamicAopProxy#getProxy:


@Overridepublic Object getProxy(ClassLoader classLoader) {    //找到可以用来进行代理的接口    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);    //用来代理的接口中是否定义了equals或者是hashCode方法?    //结果保存在内部equalsDefined和hashCodeDefined两个成员变量中    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}
复制代码


cglib 动态代理实现


@Override  public Object getProxy(ClassLoader classLoader) {    if (logger.isDebugEnabled()) {      logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());    }
try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass; if (ClassUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } }
// Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer... Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance. return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException ex) { throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } }
复制代码


这里对两种代理的实现没有去非常详细的说明,如果有感兴趣的小伙伴可以自己去跟一下代码。


好了,终于在清明假期的最后一天把这篇文章赶出来了,整个假期在忙着本地和云服务器的项目集成 elk 日志框架(mac 和 linux 会过程细节有些许不同),期间遇到了很多的坑,下个星期可能会加一篇文章来描述我在集成 elk 日志框架的时候碰到的一些问题和如何集成 elk 的过程描述一下,最后看到 elk 成功跑起来的时候,那一瞬间的感动,真的无法言语。



发布于: 刚刚阅读数: 3
用户头像

王威07325

关注

还未添加个人签名 2021-12-29 加入

还未添加个人简介

评论

发布
暂无评论
Spring系列之AOP工作过程详解二_spring_王威07325_InfoQ写作社区