Java动态代理与Spring AOP中的Cglib动态代理详解

在Java编程中,动态代理是一种在运行时动态创建代理类及其对象的技术。通过动态代理,我们可以在不修改原有类代码的情况下,为这些类添加新的行为或功能。Java提供了两种主要的动态代理机制:基于接口的Java动态代理和基于类的Cglib动态代理。在Spring AOP(面向切面编程)中,Cglib动态代理扮演着重要角色。本文将详细讨论这两种动态代理的实现。

一、Java动态代理

Java动态代理主要依赖于java.lang.reflect包中的Proxy类和InvocationHandler接口。它要求被代理的对象必须实现一个或多个接口,因为动态代理生成的代理类也是这些接口的实现类。

1.InvocationHandler接口

InvocationHandler接口定义了一个invoke方法,该方法在代理实例上的方法被调用时被调用。
public interface InvocationHandler {  public Object invoke(Object proxy, Method method, Object[] args)  throws Throwable;  
}

其中,proxy是代理实例,method是被调用的方法,args是方法的参数。

2.代理实例

使用Proxy.newProxyInstance方法可以创建动态代理实例:

public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

其中,loader是类加载器,interfaces是代理类要实现的接口列表,h是调用处理器。

3.示例

假设我们有一个接口HelloService和一个实现类HelloServiceImpl:

public interface HelloService {  void sayHello();  
}  public class HelloServiceImpl implements HelloService {  @Override  public void sayHello() {  System.out.println("Hello, world!");  }  
}
现在,我们想要为HelloService添加日志功能,但不修改HelloServiceImpl的代码。我们可以创建一个动态代理:
public class LogInvocationHandler implements InvocationHandler {  private Object target;  public LogInvocationHandler(Object target) {  this.target = target;  }  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  System.out.println("Before method: " + method.getName());  Object result = method.invoke(target, args);  System.out.println("After method: " + method.getName());  return result;  }  
}  public class DynamicProxyDemo {  public static void main(String[] args) {  HelloService helloService = new HelloServiceImpl();  LogInvocationHandler handler = new LogInvocationHandler(helloService);  HelloService proxy = (HelloService) Proxy.newProxyInstance(  helloService.getClass().getClassLoader(),  helloService.getClass().getInterfaces(),  handler  );  proxy.sayHello(); // 调用代理实例的方法  }  
}

在这个例子中,LogInvocationHandler实现了InvocationHandler接口,并在invoke方法中添加了日志功能。当我们调用proxy.sayHello()时,实际上是调用了LogInvocationHandler的invoke方法,从而实现了对原有方法的增强。

二、Cglib动态代理

Cglib是一个强大的高性能的代码生成库,它可以在运行时扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的拦截。
Cglib动态代理通过继承被代理类来创建代理对象,因此它不需要被代理类实现任何接口。这使得Cglib动态代理比Java动态代理更加灵活,因为Java中的类不一定需要实现接口

1.创建代理实例

使用Cglib创建代理实例通常涉及Enhancer类:

Enhancer enhancer = new Enhancer();  
enhancer.setSuperclass(targetClass); // 设置要代理的目标类  
enhancer.setCallback(callback); // 设置回调,用于处理代理方法调用  
Object proxy = enhancer.create(); // 创建代理对象

其中,targetClass是要被代理的类,callback是实现了MethodInterceptor接口的回调对象,用于拦截和增强方法调用。

2.示例

假设我们有一个没有实现接口的类HelloServiceWithoutInterface:

public class HelloServiceWithoutInterface {  public void sayHello() {  System.out.println("Hello, world(无接口)!");}
}

现在,我们想要为这个类添加日志功能,可以使用Cglib动态代理来实现:

import net.sf.cglib.proxy.Enhancer;  
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  
import java.lang.reflect.Method;  public class CglibLogInterceptor implements MethodInterceptor {  private Object target;  public CglibLogInterceptor(Object target) {  this.target = target;  }  @Override  public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {  System.out.println("Before method (Cglib): " + method.getName());  Object result = proxy.invokeSuper(obj, args); // 使用proxy.invokeSuper来调用父类(即目标类)的方法  System.out.println("After method (Cglib): " + method.getName());  return result;  }  
}  public class CglibDynamicProxyDemo {  public static void main(String[] args) {  HelloServiceWithoutInterface helloService = new HelloServiceWithoutInterface();  CglibLogInterceptor interceptor = new CglibLogInterceptor(helloService);  Enhancer enhancer = new Enhancer();  enhancer.setSuperclass(HelloServiceWithoutInterface.class);  enhancer.setCallback(interceptor);  HelloServiceWithoutInterface proxy = (HelloServiceWithoutInterface) enhancer.create();  proxy.sayHello(); // 调用代理实例的方法  }  
}

在这个例子中,我们创建了一个CglibLogInterceptor类,它实现了MethodInterceptor接口,并在intercept方法中添加了日志功能。然后,我们使用Cglib的Enhancer类来创建HelloServiceWithoutInterface类的代理对象。当我们调用proxy.sayHello()时,实际上是调用了CglibLogInterceptor的intercept方法,从而实现了对原有方法的增强。

三、Spring AOP中的Cglib动态代理

在Spring AOP中,Cglib动态代理被广泛应用于那些没有实现接口的类。Spring AOP通过配置切面(Aspect)、连接点(Joinpoint)、通知(Advice)等概念,实现了对目标方法的拦截和增强。当目标对象没有实现接口时,Spring AOP会自动使用Cglib来创建代理对象。这使得Spring AOP能够灵活地处理各种情况,无论是基于接口的代理还是基于类的代理。
总结:
Java动态代理和Cglib动态代理都是强大的技术,它们允许我们在运行时动态地创建代理对象,并为这些对象添加新的行为或功能。Java动态代理基于接口,而Cglib动态代理基于类,这使得它们各自适用于不同的场景。在Spring AOP中,这两种动态代理技术都得到了广泛应用,为面向切面编程提供了强大的支持。通过理解和掌握这些技术,我们可以更加灵活地设计和实现复杂的Java应用程序。

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

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

相关文章

密钥派生算法介绍 及 PBKDF2(过时)<Bcrypt(开始淘汰)<Scrypt< Argon2(含Argon2d、Argon2i、Argon2id)简介

密钥派生算法介绍 https://blog.csdn.net/xcxhzjl/article/details/127297263 一、定义 密钥派生函数(Key Derivation Function)就是从一个密码产生出一个或多个密钥&#xff0c;具体就是从一个master key&#xff0c;password或者passphrase派生出一个或多个密钥&#xff0…

HCIP-Datacom-ARST必选题库_23_SNMP【1道题】

一、单选 1.某中型规模园区网络通过SNMP协议管理网络,该园区对于网络安SNMP哪个版本进行管理? 所有版本均可以实现 BSNMPV1 SNMPV2C SNMPV3

都2024 年了,可以卸载的VS Code 插件

在 VS Code 中&#xff0c;庞大的插件市场提供了丰富多样的扩展功能&#xff0c;以增强编码体验和效率。然而&#xff0c;如果你安装了很多插件&#xff0c;就可能会导致&#xff1a; 性能下降&#xff1a;过多的插件可能导致 VS Code 的启动速度变慢&#xff0c;特别是在启动或…

[2021最新]大数据平台CDH存储组件kudu之启用HA高可用(添加多个master)

今天在做kudu高可用的时候没有参考官网&#xff0c;直接按照常规方式&#xff08;添加角色—>编辑属性—>启动&#xff09;结果发现报错&#xff1f;然后参考了一下文档之后发现这玩意儿还有点玄学&#xff0c;做一下记录。 1.添加两个master。kudu master有leader和foll…

薪酬调整流程:规范流程并确保公平合理

薪酬调整是企业人力资源管理中不可或缺的一环&#xff0c;它直接关系到员工的切身利益和企业的发展。为了确保薪酬调整的公平性和合理性&#xff0c;制定一套规范的薪酬调整流程至关重要。本文将详细阐述薪酬调整流程的各个环节&#xff0c;以及如何确保流程的合理性和公平性。…

用云手机运营TikTok有什么好处?

在数字化浪潮的推动下&#xff0c;社交媒体平台正重塑商业推广与品牌建设的面貌。TikTok&#xff0c;这款全球热门的短视频应用&#xff0c;已经吸引了亿万用户的瞩目。对于出海电商和品牌推广而言&#xff0c;借助云手机运营TikTok&#xff0c;能够解锁更多潜在可能&#xff0…

【Linux开发 第十二篇】搭建JavaEE环境

搭建开发环境 搭建javaEE环境 搭建javaEE环境 在Linux下开发JavaEE需要安装软件包&#xff1a; 安装jdk 安装步骤&#xff1a; 在opt目录下创建jdk目录通过xftp上床到jdk目录中进入到jdk目录中&#xff0c;解压jdk压缩包在/usr/local下创建java目录将解压完成的jdk文件移动…

时间搜索

时间搜索 vue2antd <a-form-item label"订单开始时间:"><a-range-picker v-model"date" valueFormat"YYYY-MM-DD" change"onChange" /></a-form-item> onChange (date) {if (date.length > 0) {this.queryParam.…

li 与 li 之间有看不见的空白间隔是什么原因引起的?如何解决?

浏览器会把inline内联元素间的空白字符&#xff08;空格、换行、Tab等&#xff09;渲染成一个空格。 为了美观&#xff0c;通常是一个放在一行&#xff0c;这导致换行后产生换行字符&#xff0c;它变成一个空格&#xff0c;占用了一个字符的宽度。 解决办法&#xff1a; &…

【MySQL | 第六篇】数据库三大范式

文章目录 6.数据库设计三大范式6.1第一范式6.2第二范式6.3第三范式6.4反范式设计 6.数据库设计三大范式 6.1第一范式 第一范式&#xff08;1NF&#xff09;&#xff1a;确保每列的原子性(强调的是列的原子性&#xff0c;即列不能够再分成其他几列)。实际上&#xff0c;第一范式…

MATLAB初学者入门(10)—— 粒子群算法

粒子群优化&#xff08;Particle Swarm Optimization, PSO&#xff09;是一种基于群体协作的优化技术&#xff0c;它由社会行为模型&#xff08;如鸟群觅食行为&#xff09;启发而来。PSO 通过模拟一群粒子&#xff08;候选解&#xff09;在解空间中的移动来寻找最优解。每个粒…

react学习(一)之初始化一个react项目

React 是一个用于构建用户界面&#xff08;UI&#xff09;的 JavaScript 库&#xff0c;用户界面由按钮、文本和图像等小单元内容构建而成。React 帮助你把它们组合成可重用、可嵌套的 组件。从 web 端网站到移动端应用&#xff0c;屏幕上的所有内容都可以被分解成组件&#xf…

在React Router 6中使用useRouteLoaderData钩子获取自定义路由信息

在 React Router 6 中怎么像vueRouter一样&#xff0c;可以在配置路由的时候&#xff0c;定义路由的元信息(附加信息)&#xff1f;答案是可以的。稍有些复杂。核心是通过为每个路由定义了一个 loader 函数,用于返回自定义的路由信息&#xff0c;然后通过useRouteLoaderData 钩子…

Java使用百度翻译接口开发app

到网站&#xff1a;百度翻译开放平台下载Java版本的demo 使用demo中的三个文件&#xff0c;main不用 默认返回的result内容是这样的: {"from":"en","to":"zh","trans_result":[{"src":"Sure! Heres a sh…

虚拟机扩容方法

概述 我的虚拟机开始的内存是40G,接下来要扩成60GB 扩容步骤 步骤1 步骤2 步骤3 修改扩容后的磁盘大小&#xff0c;修改后的值只可以比原来的大&#xff0c;修改完成后点击扩展&#xff0c;等待扩展完成 步骤4 虽然外面扩展成功&#xff0c;但是新增的磁盘空间虚拟机内部还…

GHO文件安装到Vmware的两种姿势

1、使用 Ghost11.5.1.2269 将gho转换为vmdk文件(虚拟机硬盘)&#xff0c;Vmware新建虚拟机自定义配置&#xff0c;然后添加已有的虚拟硬盘文件。 注意ghost的版本&#xff0c;如果你是用Ghost11.5备份的gho文件&#xff0c;再用Ghost12把gho文件转换为vmdk&#xff0c;则vmdk文…

Sound Siphon for Mac:音频处理与录制工具

Sound Siphon for Mac是一款专为Mac用户设计的音频处理与录制工具&#xff0c;以其出色的性能、丰富的功能和简便的操作而备受赞誉。 Sound Siphon for Mac v3.6.8激活版下载 该软件支持多种音频格式&#xff0c;包括MP3、WAV、AAC、FLAC等&#xff0c;用户可以轻松导入各种音频…

openwrt 挂载 usb摄像头 video-uvc mjpg-streamer

安装 # 安装vidoe uvc opkg install kmod-video-core kmod-video-uvc# 插入USB摄像头检测支持情况 dmesg|grep usb# 下载camwww # https://sourceforge.net/projects/mjpg-streamer/ # 解压后将 www 文件修改为camwww, 然后将camwww上传到路由器/www/目录下# 安装camwww 实现推…

小程序中Java后台调用getAccessToken接口、msg_sec_check接口检测文本安全、小程序前端访问后端接口的方法

序言:书接上回 小程序中Java后台调用接口(getAccessToken)获取调用凭据,调用接口(msgSecCheck)检测文本内容是否安全–最终版 小程序服务端后台 Java代码编写,框架采用SSM架构搭建 第一步:添加需要的pom.xml依赖配置 <!-- 小程序文本安全检测接口所需的依赖 --&g…

vue源码解析patch.js

前言&#xff1a;在vue2.0 中&#xff0c;VNode转换成真正的DOM是通过patch(oldVNode,VNode,hydrating)方法实现的。 源码目录&#xff1a;/vue/src/core/vdom/patch.ts // oldVnode 一个真实的DOM或者一个Vnode对象// vnode 一个待替换的Vnode对象// hydrating 是否支持服务端…