创建代理
通过前面的流程,获取到了所有对应 bean
的增强器后,可以开始代理的创建。
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { ProxyFactory proxyFactory = new ProxyFactory(); // 拷贝,获取当前类中的相关属性 proxyFactory.copyFrom(this); // 决定对于给定 bean 是否应该使用 targetClass 而不是他的接口代理 if (!proxyFactory.isProxyTargetClass()) { // 检查 proxyTargetClass 设置以及 preserveTargetClass 属性 if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { // 添加代理接口 evaluateProxyInterfaces(beanClass, proxyFactory); } } // 这一步中,主要将拦截器封装为增强器 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); proxyFactory.addAdvisors(advisors); proxyFactory.setTargetSource(targetSource); // 定制代理 customizeProxyFactory(proxyFactory); // 用来控制代理工厂被配置之后,是否含允许修改通知 // 缺省值为 false,不允许修改代理的配置 proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } // 生成代理,委托给了 ProxyFactory 去处理。 return proxyFactory.getProxy(getProxyClassLoader()); }
对于代理类的创建和处理, Spring
委托给了 ProxyFactory
去处理,在上面贴出的函数主要是对 ProxyFactory
的初始化操作,进而对真正的创建代理做准备,主要流程如下:
- 获取当前类的属性
- 添加代理接口
- 封装 Advisor 并加入到 ProxyFactory
- 设置要代理的类
- 为子类提供定制的函数 customizeProxyFactory,子类通过该方法对 ProxyFactory 进行进一步的封装
- 进行获取代理操作
比较关键的是第三个步骤和第六个步骤,其中在第三个步骤中,进行的是拦截器包装,详细代码流程请查 [注释 8.9 为给定的bean创建AOP代理] 和 [注释 8.10 包装拦截器,封装成 Advisor]。
接着,完成了所有增强器的封装过程,到了解析的最后一步,进行代理的创建和获取。
public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
创建代理 createAopProxy()
定位到创建代理的代码:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (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); } }
从上面代码中能看出,根据了几个关键属性,判断创建的是哪种类型的 AopProxy
,一种是 JDK 动态代理,另一种是 CGLIB 动态代理。
前面提到过的 proxy-target-class
属性和 targetClass
属性,在这里判断了应该创建哪一个代理。
获取代理 getProxy()
观察图片以及前面分析,可以知道有两种代理方式:[JDK 动态代理] 和 [CGLIB 动态代理]
同时先说下动态代理的含义:抽象类在编译期间是未确定具体实现子类,在运行时才生成最终对象。
JDK 动态代理
JDK
代理是默认推荐的代理方式,使用的是 Proxy
+ InvocationHandler
。
可以通过以下方式实现:定义一个接口、实现类,和一个处理器继承于 InvocationHandler
,然后重载处理器中的 invoke
方法,对代理对象进行增强。
JdkDynamicAopProxy.java
public Object getProxy(@Nullable ClassLoader classLoader) { // 注释 8.11 JDK 动态代理 if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
获取代理的核心步骤在 Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)
,第三个参数是 JdkDynamicAopProxy
本身,而且它实现了 InvocationHandler
接口,重载了 invoke
方法。
org.springframework.aop.framework.JdkDynamicAopProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 注释 8.12 jdk 动态代理重载的 invoke 方法 MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Object target = null; try { Object retVal; 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); // Get the interception chain for this method. // 获取此方法的拦截链 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // 检查我们是否有任何切面逻辑。如果我们不这样做,我们可以回退直接反射调用目标,并避免创建 MethodInvocation。 if (chain.isEmpty()) { Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // 将拦截器封装在 ReflectiveMethodInvocation,便于使用 proceed 执行拦截器 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 执行拦截器链 retVal = invocation.proceed(); } ... return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } }
创建 JDK
代理过程中,主要的工作时创建了一个拦截器链,并使用 ReflectiveMethodInvocation
类进行封装,封装之后,逐一调用它的 proceed
方法, 用来实现在目标方法的前置增强和后置增强。
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
public Object proceed() throws Throwable { // 执行完所有增强器后执行切点方法 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } // 获取下一个要执行的拦截器 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // 动态匹配 InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // 匹配失败,跳过拦截器,直接返回 return proceed(); } } else { return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
具体代码和注释请定位到该方法查看。关于 JDK
动态代理,深入学习的话也可以单独拎出来,所以推荐看这篇资料 小豹子带你看源码:JDK 动态代理,进行了和学习
CGLIB 动态代理
CGLIB[Code Generation LIB]
是一个强大的高性能的代码生成包。它广泛应用于许多 AOP
框架。
再次推荐参考资料一,这位老哥将 CGLIB
代理, 详细介绍了 CGLIB
在什么场景使用,以及被它增强后代码处理顺序,Cglib及其基本使用。
希望看完这篇文章,能过了解到 CGLIB
代码生成包具体是如何对类进行增强。
代理增强结果
通过前面一系列步骤,解析标签、属性、增强方法,到最后获取 CGLIB
代理,通过代理创建 bean
来看下最后被代理的 bean
内部:
从图中可以看到,最终创建的是被修饰后的 bean
,内部很明显是 CGGLIB
代理生成的代码,我们在不修改业务代码的情况下,实现了方法增强。
静态 AOP
既然有动态代理,那么也会有静态代理。
使用静态 AOP
的时候,需要用到 LTW
(Load-Time Weaving 加载时织入),指的是在虚拟机载入字节码文件时动态织入 AspectJ
切面。
AOP
的静态代理主要是在虚拟机启动时通过改变目标对象字节码的方式来完成对目标对象的增强,它与动态代理相比具有更高的效率,因为在动态代理调用的过程中,还需要一个动态创建代理类并代理目标对象的步骤,而静态代理则是在启动时便完成了字节码增减,当系统再次调用目标类时,与调动正常的类并无区别,所以在效率上会相对高些。
关于静态 AOP
的使用和学习,可以参考这篇文章:从代理机制到Spring AOP
总结
动态 AOP
使用起来很简单,对于如何实现,总结起来就两点:
- 动态解析 `AOP` 标签
- 创建 `AOP` 代理
但在 Spring
底层实现逻辑却是复杂到不行,从 Spring
框架中可以看到这是良好的代码设计思路,顶层入口尽量简单,使用者很容易就能掌握该功能,复杂实现逻辑都被隐藏了。
写这一篇 AOP
学习总结,花了将近一周,先看了一遍书籍, 下班后花了一晚,将大致流程理了一遍,第二天晚上走读代码,发现有些地方还存在疑惑,例如 JDK
和 cglib
动态代理是怎么回事,翻阅查询资料,弄懂后又过了一天。
将代码注释加上,分析动态代理每一个步骤做的事情,结合之前学的后处理器 BeanPostProcessor
知识和自定义标签解析知识一起又梳理一遍。零零散散,终于整理完成。
在静态 AOP
知识点,按照我的理解,越往系统底层深入,它的执行效率越高,所以减少了动态创建代理类和代理目标对象的步骤,静态代理的速度会得到提升。同时由于接近底层后,代码编写的复杂度同样会增加,所以我在权衡高频率使用场景(动态代理),本次学习没有详细去了解,留下这个坑,以后有机会再填吧~
由于个人技术有限,如果有理解不到位或者错误的地方,请留下评论,我会根据朋友们的建议进行修正
Gitee 地址 https://gitee.com/vip-augus/spring-analysis-note.git