Spring进阶 - Spring AOP实现原理(二)AOP代理的创建

上一篇文章熟悉后,创建代理对象的源码5分钟能看完。

上一篇文章我们通过阅读源码学会切面实现原理,将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor。本文在此基础上继续介绍,代理(cglib代理和JDK代理)的创建过程。

引言

AOP 切面实现后才是代理的创建,先快速回顾切面实现原理。

阶段一 : 标签解析

  1. Spring 启动,IOC 容器从加载标签 XML 标签开始,XML 标签声明了多个自定义实例和 AOP 切面实例,IOC 容器加载所有的 XML 。
  2. 在 AopNamespaceHandler 中有一个 init()方法,我们发现每一个 aop 标签都有一个解析器,所以当你在 xml 使用 aop 这些标签,会被解析成 AOP 代理。
  3. config 配置标签的解析,初始化 ConfigBeanDefinitionParser 类,执行内部 parse 方法。
  4. aspect-autoproxy 标签解析,初始一个解析类,内部有 parse 方法,但是一直调用下去会发现 AOP的创建工作是交给 AnnotationAwareAspectJAutoProxyCreator 类完成

阶段二:**postProcessBeforeInstantiation** 方法

  1. 方法内有两个重要的判断,判断是否给这个实例加 aop 的代理能力。

    1. 是否是 AOP 基础类?是基础类就不加 aop 能力。
    2. 是否跳过?跳过就不加 aop能力
  2. 如果实例是基础类,且要被跳过,那么它是 aop 切面类,把切面转成 Advisor 对象,内部会调用一个 getAdvisor() 的方法来转换。

  3. 在 getAdvisor() 方法又会调用 getPointcut() 方法获取表达式切点。最后转成出来一个 advisor 对象。

  4. advisor 对象是 advice 的包装类,所以我们要获取 advice 信息,封装到 advisor 内部。

大费周章得到的 advisor 是什么?

advisor 约等于通知(Advice)和 切入点(Pointcut),一个 advisor 是 AOP 设计思想的两个术语的具体实现。一个切面类有多个通知,就有多个切点表达式,那么就有多个 advisor.

AOP 代理实现逻辑在 postProcessAfterInstantiation 方法内,通过这个方法看Spring AOP的代理(cglib或jdk)的创建过程。

文字描述阅读源码流程

  1. 一个Bean实例化之后会调用 postProcessAfterInitialization 方法
  2. wrapIfNecessary方法主要用于判断是否需要创建代理,如果Bean能够获取到advisor才需要创建代理
  3. 获取所有 advisor 对象,但只取够应用于beanClass的Advisor
  4. 如果有advisor,则说明需要增强,即需要创建代理对象。
  5. 最后执行 DefaultAopProxyFactory.createAopProxy() 创建代理对象。

代理的创建

一个Bean实例化之后会调用 postProcessAfterInitialization 方法,这个方法会判断这个 Bean 是否需要代理,如果要那么就会创建代理对象,接下来详细看源码实现。

java
复制代码
/** * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */ @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 如果不是提前暴露的代理,这个判断用来处理循环依赖 if (this.earlyProxyReferences.remove(cacheKey) != bean) { // 核心逻辑:如果需要,就包装(代理)这个Bean return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }

wrapIfNecessary方法主要用于判断是否需要创建代理,如果Bean能够获取到advisor才需要创建代理

java
复制代码
/** * Wrap the given bean if necessary, i.e. if it is eligible for being proxied. * @param bean the raw bean instance * @param beanName the name of the bean * @param cacheKey the cache key for metadata access * @return a proxy wrapping the bean, or the raw bean instance as-is */ 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

我们看下获取所有advisor的方法getAdvicesAndAdvisorsForBean

java
复制代码
@Override @Nullable protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }

通过findEligibleAdvisors方法获取advisor, 如果获取不到返回DO_NOT_PROXY(不需要创建代理),findEligibleAdvisors方法如下

java
复制代码
/** * Find all eligible Advisors for auto-proxying this class. * @param beanClass the clazz to find advisors for * @param beanName the name of the currently proxied bean * @return the empty List, not {@code null}, * if there are no pointcuts or interceptors * @see #findCandidateAdvisors * @see #sortAdvisors * @see #extendAdvisors */ protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 和上文一样,获取所有切面类的切面方法生成Advisor List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 找到这些Advisor中能够应用于beanClass的Advisor List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // 如果需要,交给子类拓展 extendAdvisors(eligibleAdvisors); // 对Advisor排序 if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }

这里内部有一个获取所有切面类的切面方法,生成Advisor

java
复制代码
/** * Find all candidate Advisors to use in auto-proxying. * @return the List of candidate Advisors */ protected List<Advisor> findCandidateAdvisors() { Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available"); return this.advisorRetrievalHelper.findAdvisorBeans(); }

找到这些Advisor中能够应用于beanClass的Advisor , 这一步代码长,但没啥好说的,就是返回符合要求的 advisor。

java
复制代码
/** * Determine the sublist of the {@code candidateAdvisors} list * that is applicable to the given class. * @param candidateAdvisors the Advisors to evaluate * @param clazz the target class * @return sublist of Advisors that can apply to an object of the given class * (may be the incoming List as-is) */ public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List<Advisor> eligibleAdvisors = new ArrayList<>(); for (Advisor candidate : candidateAdvisors) { // 通过Introduction实现的advice 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; } // 是否能够应用于clazz的Advice if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }

然后代码流程就走到了创建代理对象了。

创建代理的入口方法

获取所有advisor后,如果有advisor,则说明需要增强,即需要创建代理,创建代理的方法如下:

java
复制代码
/** * Create an AOP proxy for the given bean. * @param beanClass the class of the bean * @param beanName the name of the bean * @param specificInterceptors the set of interceptors that is * specific to this bean (may be empty, but not null) * @param targetSource the TargetSource for the proxy, * already pre-configured to access the bean * @return the AOP proxy for the bean * @see #buildAdvisors */ protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable 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()) { // Explicit handling of JDK proxy targets (for introduction advice scenarios) if (Proxy.isProxyClass(beanClass)) { // Must allow for introductions; can't just set interfaces to the proxy's interfaces only. for (Class<?> ifc : beanClass.getInterfaces()) { proxyFactory.addInterface(ifc); } } } else { // No proxyTargetClass flag enforced, let's apply our default checks... if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // Use original ClassLoader if bean class not locally loaded in overriding class loader ClassLoader classLoader = getProxyClassLoader(); if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) { classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader(); } return proxyFactory.getProxy(classLoader); }

这里最终是调用了 proxyFactory 对象,看一眼 proxyFactory 的继承图,没啥特殊的,看看有没有你熟悉的组件

img

最后一行的 getProxy() 方法源码:

java
复制代码
/** * Create a new proxy according to the settings in this factory. * <p>Can be called repeatedly. Effect will vary if we've added * or removed interfaces. Can add and remove interceptors. * <p>Uses the given class loader (if necessary for proxy creation). * @param classLoader the class loader to create the proxy with * (or {@code null} for the low-level proxy facility's default) * @return the proxy object */ public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }

这里的 createAopProxy()方法是先执行,它是真正的创建代理对象的方法,DefaultAopProxyFactory.createAopProxy。

依据条件创建代理(jdk或cglib)

DefaultAopProxyFactory.createAopProxy:

java
复制代码
@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."); } // 目标类本身是一个接口 || 目标类已经是一个JDK代理对象(这种情况发生在代理嵌套时) if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { // 返回 JDK 动态代理对象 return new JdkDynamicAopProxy(config); } // 否则返回 Cglib 动态代理 return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }

到这就明白了八股文里面的 Spring Boot 优先返回哪个代理对象的问题。

在 Spring Framework 6.x 我的项目截取出来的源码目前是这样的:

java
复制代码
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (NativeDetector.inNativeImage() || !config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) { return new JdkDynamicAopProxy(config); } else { 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."); } else { return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config)); } } }

第一个 if 有四个判断条件, if 成立则返回 JDK 代理,说明Spring 是优先使用 JDK 代理。条件含义是:

  • 检测当前应用是否运行在 GraalVM Native Image 环境中。GraalVM是一个将Java代码提前编译成本地可执行文件的技术。在这种环境下,CGLIB这种运行时生成字节码的技术会受到很大限制甚至不可用。因此,如果检测到是Native Image环境,此条件true , Spring会强制使用JDK动态代理。

  • 检查是否没有开启“优化”模式。优化配置如:**@EnableAspectJAutoProxy(proxyTargetClass = false, optimize = true)** ,那么此条件 true。它告诉Spring:“请尽可能地对代理进行优化”。

  • 检查是否没有强制要求代理目标类。对应配置项对应 **@EnableAspectJAutoProxy(proxyTargetClass = false)**。 表示没有强制要求,此条件 true,Spring会优先考虑使用JDK动态代理。

  • 是否没有任何用户提供的接口,如果有,那么此条件 true , 优先考虑 JDK动态代理。

如果第一个 if 不满足,进入else , else 中如果目标对象实现了接口,也是优先考虑 JDK 。

到这里我们就把 AOP 的切面实现原理和AOP的创建代理对象原理讲清楚了。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP