Spring-ProxyFactory

ProxyFactory选择cglib或jdk动态代理原理

ProxyFactory在生成代理对象之前需要决定是使用JDK动态代理还是CGLIB技术:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {// 如果ProxyFactory的isOptimize为true,Spring认为cglib比jdk动态代理要快// 或者isProxyTargetClass为true,// 或者被代理对象没有实现接口,// 或者只实现了SpringProxy这个接口// 那么则利用Cglib进行动态代理,但如果被代理类是接口,或者被代理类已经是进行过JDK动态代理而生成的代理类了则只能进行JDK动态代理// 其他情况都会进行JDK动态代理,比如被代理类实现了除SpringProxy接口之外的其他接口// 是不是在GraalVM虚拟机上运行if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {// config就是ProxyFactory对象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.");}// targetClass是接口,直接使用Jdk动态代理if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}// 使用Cglibreturn new ObjenesisCglibAopProxy(config);}else {// 使用Jdk动态代理return new JdkDynamicAopProxy(config);}}/*** Determine whether the supplied {@link AdvisedSupport} has only the* {@link org.springframework.aop.SpringProxy} interface specified* (or no proxy interfaces specified at all).*/private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {Class<?>[] ifcs = config.getProxiedInterfaces();return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));}
}

代理对象创建过程

JdkDynamicAopProxy

/*** Construct a new JdkDynamicAopProxy for the given AOP configuration.* @param config the AOP configuration as AdvisedSupport object* @throws AopConfigException if the config is invalid. We try to throw an informative* exception in this case, rather than let a mysterious failure happen later.*/
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {Assert.notNull(config, "AdvisedSupport must not be null");if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {throw new AopConfigException("No advisors and no TargetSource specified");}this.advised = config;// 设置JDK动态代理所要代理的接口this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
}// 1、在构造JdkDynamicAopProxy对象时,会先拿到被代理对象自己所实现的接口,
// 并且额外的增加SpringProxy、Advised、DecoratingProxy三个接口,组合成一个Class[],
// 并赋值给proxiedInterfaces属性
static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {// 被代理对象自己所实现的接口Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();// 如果被代理对象没有实现接口,则判断被代理类是不是接口,或者被代理类是不是已经经过JDK动态代理之后的类从而获取想对应的接口if (specifiedInterfaces.length == 0) {// No user-specified interfaces: check whether target class is an interface.Class<?> targetClass = advised.getTargetClass();if (targetClass != null) {if (targetClass.isInterface()) {advised.setInterfaces(targetClass);}else if (Proxy.isProxyClass(targetClass)) {advised.setInterfaces(targetClass.getInterfaces());}specifiedInterfaces = advised.getProxiedInterfaces();}}// 添加三个Spring内置接口:SpringProxy、Advised、DecoratingProxyList<Class<?>> proxiedInterfaces = new ArrayList<>(specifiedInterfaces.length + 3);for (Class<?> ifc : specifiedInterfaces) {// Only non-sealed interfaces are actually eligible for JDK proxying (on JDK 17)if (isSealedMethod == null || Boolean.FALSE.equals(ReflectionUtils.invokeMethod(isSealedMethod, ifc))) {proxiedInterfaces.add(ifc);}}if (!advised.isInterfaceProxied(SpringProxy.class)) {proxiedInterfaces.add(SpringProxy.class);}if (!advised.isOpaque() && !advised.isInterfaceProxied(Advised.class)) {proxiedInterfaces.add(Advised.class);}if (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class)) {proxiedInterfaces.add(DecoratingProxy.class);}return ClassUtils.toClassArray(proxiedInterfaces);
}// 2、检查这些接口中是否定义了equals()、hashcode()方法
private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {// 判断被代理的接口中是否定义了equals()、hashCode()方法,// 如果在接口中手动定义了这两个方法,则也会进行代理// 否则这两个方法是不会走代理逻辑的for (Class<?> proxiedInterface : proxiedInterfaces) {Method[] methods = proxiedInterface.getDeclaredMethods();for (Method method : methods) {if (AopUtils.isEqualsMethod(method)) {this.equalsDefined = true;}if (AopUtils.isHashCodeMethod(method)) {this.hashCodeDefined = true;}if (this.equalsDefined && this.hashCodeDefined) {return;}}}
}// 3、得到代理对象,JdkDynamicAopProxy作为InvocationHandler
// 代理对象在执行某个方法时,会进入到JdkDynamicAopProxy的invoke()方法中
public Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());}// this实现了InvocationHandlerreturn Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}

ObjenesisCglibAopProxy

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating CGLIB proxy: " + 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;// 0、如果被代理类本身就已经是Cglib所生成的代理类了if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {// 获取真正的被代理类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...// 1、创建Enhancer对象Enhancer enhancer = createEnhancer();if (classLoader != null) {enhancer.setClassLoader(classLoader);if (classLoader instanceof SmartClassLoader &&((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {enhancer.setUseCache(false);}}// 2、被代理类,代理类的父类。proxySuperClass就是ProxyFactory.setTarget()所设置的对象的类enhancer.setSuperclass(proxySuperClass);// 3、代理类额外要实现的接口。通过ProxyFactory.addInterface()所添加的接口,// 以及SpringProxy、Advised、DecoratingProxy接口enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));// 获取和被代理类所匹配的AdvisorCallback[] 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 aboveenhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));enhancer.setCallbackTypes(types);// Generate the proxy class and create a proxy instance.// 4、设置Enhancer的Callbacks为DynamicAdvisedInterceptor// 代理对象在执行某个方法时,会进入到DynamicAdvisedInterceptor的intercept()方法中return createProxyClassAndInstance(enhancer, callbacks);}catch (CodeGenerationException | 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() failedthrow new AopConfigException("Unexpected AOP exception", ex);}
}

代理对象执行过程

1、在使用ProxyFactory创建代理对象之前,需要往ProxyFactory先添加Advisor

2、代理对象在执行某个方法时,会把ProxyFactory中的Advisor拿出来和当前正在执行的方法进行匹配筛选

3、把和方法所匹配的Advisor适配成MethodInterceptor

4、把和当前方法匹配的MethodInterceptor链,以及被代理对象、代理对象、代理类、当前Method对象、方法参数封装为MethodInvocation对象

5、调用MethodInvocation的proceed()方法,开始执行各个MethodInterceptor以及被代理对象的对应方法

6、按顺序调用每个MethodInterceptor的invoke()方法,并且会把MethodInvocation对象传入invoke()方法

7、直到执行完最后一个MethodInterceptor,就会调用invokeJoinpoint()方法,从而执行被代理对象的当前方法

各注解对应的MethodInterceptor

@Before对应的是AspectJMethodBeforeAdvice,在进行动态代理时会把AspectJMethodBeforeAdvice转成MethodBeforeAdviceInterceptor

  • 先执行advice对应的方法

  • 再执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法

@After对应的是AspectJAfterAdvice,直接实现了MethodInterceptor

  • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法

  • 再执行advice对应的方法

@Around对应的是AspectJAroundAdvice,直接实现了MethodInterceptor

  • 直接执行advice对应的方法,由@Around自己决定要不要继续往后面调用

@AfterThrowing对应的是AspectJAfterThrowingAdvice,直接实现了MethodInterceptor

  • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法

  • 如果上面抛了Throwable,那么则会执行advice对应的方法

@AfterReturning对应的是AspectJAfterReturningAdvice,在进行动态代理时会把AspectJAfterReturningAdvice转成AfterReturningAdviceInterceptor

  • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法

  • 执行上面的方法后得到最终的方法的返回值

  • 再执行advice对应的方法

AbstractAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator的父类是AbstractAdvisorAutoProxyCreator。 AbstractAdvisorAutoProxyCreator非常强大以及重要,只要Spring容器中存在这个类型的Bean,就相当于开启了AOP,AbstractAdvisorAutoProxyCreator实际上就是一个BeanPostProcessor,所以在创建某个Bean时,就会进入到它对应的生命周期方法中。

当某个Bean初始化后,会调用wrapIfNecessary()方法进行AOP:AbstractAdvisorAutoProxyCreator会找到所有的Advisor,然后判断当前这个Bean是否存在某个Advisor与之匹配(根据Pointcut),如果匹配就表示当前这个Bean有对应的切面逻辑,需要进行AOP,需要产生一个代理对象。

@EnableAspectJAutoProxy

主要作用:往Spring容器中添加了一个AnnotationAwareAspectJAutoProxyCreator类型的Bean

AspectJAwareAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator,重写了findCandidateAdvisors()方法。

AbstractAdvisorAutoProxyCreator只能找到所有Advisor类型的Bean对象。

AspectJAwareAdvisorAutoProxyCreator除了可以找到所有Advisor类型的Bean对象,还能把@Aspect注解所标注的Bean中的@Before等注解及方法进行解析,并生成对应的Advisor对象。

简单理解:@EnableAspectJAutoProxy注解就是往Spring容器中添加了一个AbstractAdvisorAutoProxyCreator类型的Bean,从而开启了AOP,并且还会解析@Before等注解生成Advisor。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/170000.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

golang Copier 数据复制

Copier I am a copier, I copy everything from one to another Copier是golang实现的&#xff0c;实现不同数据结构之间数据复制的工具包 github地址 使用方法 以User和Employee之间相互复制为例 使用的版本为 v0.3.5 入门 package mainimport ("fmt""git…

头歌答案--爬虫实战

目录 urllib 爬虫 第1关&#xff1a;urllib基础 任务描述 第2关&#xff1a;urllib进阶 任务描述 requests 爬虫 第1关&#xff1a;requests 基础 任务描述 第2关&#xff1a;requests 进阶 任务描述 网页数据解析 第1关&#xff1a;XPath解析网页 任务描述 第…

hive和spark-sql中 日期和时间相关函数 测试对比

测试版本&#xff1a; hive 2.3.4 spark 3.1.1 hadoop 2.7.7 1、增加月份 add_months(timestamp date, int months)add_months(timestamp date, bigint months)Return type: timestampusage:add_months(now(),1) 2、增加日期 adddate(timestamp startdate, int days)…

【Redis系列】Redis的核心命令(上)

哈喽&#xff0c;大家好&#xff0c;我是小浪。那么上篇博客教会了大家如何在Linux上安装Redis&#xff0c;那么本篇博客就要正式开始学习Redis啦&#xff0c;跟着俺的随笔往下看~ 1、启动Redis 那么如何启动Redis呢&#xff1f;最常用的是以下这个命令&#xff1a; redis-cl…

【阿里云】函数计算 X 通义千问快速部署

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

【Android】Android apk 逆向编译

链接&#xff1a;https://pan.baidu.com/s/14r5s9EJwQgeLK5cCb1Gq1Q 提取码&#xff1a;qdqt 解压jadx 在 lib 文件内找到 jadx-gui-1.4.7.jar 打开cmd 执行 &#xff1a;java -jar jadx-gui-1.4.7.jar示列&#xff1a;

生成式AI - Knowledge Graph Prompting:一种基于大模型的多文档问答方法

大型语言模型&#xff08;LLM&#xff09;已经彻底改变了自然语言处理&#xff08;NLP&#xff09;任务。它们改变了我们与文本数据交互和处理的方式。这些强大的AI模型&#xff0c;如OpenAI的GPT-4&#xff0c;改变了理解、生成人类类似文本的方式&#xff0c;导致各种行业出现…

[.NET]启明星电子文档管理系统edoc v33.0

启明星电子文档库是一个简单、实用的企业文档在线存储工具。系统采用ASP.NETMSSQL2008 Express开发&#xff0c;所有文档数据都以二进制方式存储在数据库里方便备份。 系统的特点包括&#xff1a; &#xff08;1&#xff09;支持文档在线预览&#xff0c;可以在线预览word&…

【2011年数据结构真题】

41题 41题解答&#xff1a; &#xff08;1&#xff09;图 G 的邻接矩阵 A 如下所示&#xff1a; 由题意得&#xff0c;A为上三角矩阵&#xff0c;在上三角矩阵A[6][6]中&#xff0c;第1行至第5行主对角线上方的元素个数分别为5, 4, 3, 2, 1 用 “ 平移” 的思想&#xff0c;…

Halcon WPF 开发学习笔记(4):Halcon 锚点坐标打印

文章目录 专栏前言锚点二次开发添加回调函数辅助Model类 下集预告 专栏 Halcon开发 博客专栏 WPF/HALCON机器视觉合集 前言 Halcon控件C#开发是我们必须掌握的&#xff0c;因为只是单纯的引用脚本灵活性过低&#xff0c;我们要拥有Halcon辅助开发的能力 锚点开发是我们常用的…

SolidWorks绘制花瓶教程

这个花瓶是我学习solidworks画图以来用时最长的一个图形了&#xff0c;特此记录一下&#xff0c;用了我足足两个早晨才把他给画出来&#xff0c;我这是跟着哔站里的隔壁老王学习的&#xff0c;下面是视频地址&#xff1a;点击我一下看视频教程 下面是我的绘图过程&#xff0c;…

Anolis 8.6 安装 Drawio

Anolis 8.6 安装 Drawio 22.1.0 一.RPM版&#xff08;不建议&#xff09;二.WAR 包部署 一.RPM版&#xff08;不建议&#xff09; Draw RPM 包下载链接 RPM 包直接基于Linux图形化能力部署&#xff0c;服务器类型的Linux系统启动RPM包安装的Draw可能比较复杂 系统版本 ## 1.…