深入理解Java动态代理与静态代理

这篇文章主要介绍了深入理解Java动态代理与静态代理,静态代理,代理类和被代理的类实现了同样的接口,代理类同时持有被代理类的引用,动态代理的根据实现方式的不同可以分为JDK动态代理和CGlib动态代理

前言

学习 Spring 的过程中,不可避免要掌握代理模式。这篇文章总结一下代理模式。顾名思义,代理,就是你委托别人帮你办事,所以代理模式也有人称作委托模式的。比如领导要做什么事,可以委托他的秘书去帮忙做,这时就可以把秘书看做领导的代理。下面将以这个例子来讲解。
代理模式又分为静态代理和动态代理。

一、静态代理

静态代理的使用

静态代理,代理类和被代理的类实现了同样的接口,代理类同时持有被代理类的引用,这样,当我们需要调用被代理类的方法时,可以通过调用代理类的方法来做到。

举例:假设领导的工作是开会和给员工考评。

先定义一个接口:

package com.sharpcj;
public interface IWork {
    void meeting();
    int evaluate(String name);
}
然后定义领导类: package com.sharpcj; import java.util.Random; public class Leader implements IWork { @Override public void meeting() { System.out.println("领导早上要组织会议"); } @Override public int evaluate(String name) { int score = new Random(System.currentTimeMillis()).nextInt(20) + 80; System.out.println(String.format("领导给%s的考评为%s分", name, score)); return score; } } 秘书类: package com.sharpcj; public class Secretary implements IWork { private Leader mLeader; public Secretary(Leader mLeader) { this.mLeader = mLeader; } @Override public void meeting() { System.out.println("秘书先给老板准备材料"); mLeader.metting(); } @Override public int evaluate(String name) { return mLeader.evaluate(name); } } 测试类: package com.sharpcj; public class TestApp { public static void main(String[] args) { Leader leader = new Leader(); Secretary secretary = new Secretary(leader); secretary.meeting(); secretary.evaluate("Joy"); } } 执行结果: 这个代码很简单,注意在调用Secretary类的 meeting 方法时,我们调用了Leader类的 meeting 的方法,在此之前,我们还扩充了该方法。这时有的人可能有疑惑了,这看起来有点是装饰者模式了。这到底怎么回事? 与装饰者模式的区别 实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。 我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。 先看看两者的 UML 类图区别: 代理模式: 装饰者模式: 两者伪代码: 代理模式: Interface Subject { void doAction() } public class RealSubject implements Subject{ @Override public void doAction() {}; } public class Proxy implements Subject{ private RealSubject realSubject; public Proxy(RealSubject realSubject) { //关系在编译时确定 this.realSubject = realSubject; } @Override public void doAction() { …. realSubject.doAction(); …. } } 装饰者模式; Interface Component { void doAction() } public class ConcreteComponent implement Component { @Override public void doAction() {}; } public class Decorator implements Component { private Component component; public Decorator(Component component) { //关系在编译时确定 this.component = new component; } public void doAction() { …. component.doAction(); …. } } 其实代理模式和装饰者模式侧重点不一样,代理模式重点在于明确了被代理的类。如上例中,秘书很明确要代理的是的领导。而装饰者模式侧重于拓展类的方法,装饰类持有的实现Component接口的类的对象不是固定的,也就是说,装饰类可以根据在调用时传入的参数,装饰任意一个实现了 Component 接口的类。 二、动态代理 动态代理的根据实现方式的不同可以分为 JDK 动态代理和 CGlib 动态代理。 JDK 动态代理:利用反射机制生成一个实现代理接口的类,在调用具体方法前调用InvokeHandler来处理。CGlib 动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。 JDK 动态代理 还是以上面的例子为例:首先,定一个类实现 InvocationHandler 接口,并实现 invoke 方法: package com.sharpcj; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class WorkInvocationHandler implements InvocationHandler { private Object object; public WorkInvocationHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("object: " + object.getClass().getSimpleName()); System.out.println("proxy: " + proxy.getClass().getSimpleName()); if ("meeting".equals(method.getName())) { System.out.println("代理先准备会议材料..."); return method.invoke(object, args); } else if ("evaluate".equals(method.getName())) { if(args[0] instanceof String) { if ("James".equals(args[0])) { System.out.println("James 犯过错误,所以考评分数较低..."); return 70; } } return method.invoke(object, args); } return null; } } 然后通过 Proxy.newProxyInstance() 方法创建代理对象: package com.sharpcj; import java.lang.reflect.Proxy; public class TestApp { public static void main(String[] args) { /*Leader leader = new Leader(); Secretary secretary = new Secretary(leader); secretary.meeting(); secretary.evaluate("Joy");*/ Leader leader = new Leader(); IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(), new Class[]{IWork.class}, new WorkInvocationHandler(leader)); proxy.meeting(); proxy.evaluate("Joy"); proxy.evaluate("James"); } } 输出结果: 我们看到,通过 WorkInvocationHandler 类,我们同样可以代理 Leader 类的方法的实现,实际上我们实现的是任意的方法的实现,只是我们在创建代理对象的时候传入的是 Iwork 接口以及 Leader 类对象。 这里需要注意的是:在 InvocationHandler 接口的 invoke 方法中第一个参数 proxy, 并不是我们调用方法的对象,那这个参数是什么呢?代码中,我特别增加相应打印,打印出了 proxy 的类名,实际上 proxy 是代理对象本身,它的意义在于,我们可以在 invoke 方法中,返回该代理对象,然后进行连续调用。 看如下例子: package com.sharpcj.proxytest; public interface IWork { IWork work(String subject); } package com.sharpcj.proxytest; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class WorkInvocationHandler implements InvocationHandler { private Object object; public WorkInvocationHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("work".equals(method.getName())){ System.out.println("--- work: " + args[0]); return proxy; } return null; } } package com.sharpcj.proxytest; import java.lang.reflect.Proxy; public class TestApp { public static void main(String[] args) { IWork worker = (IWork) Proxy.newProxyInstance(IWork.class.getClassLoader(), new Class[]{IWork.class}, new WorkInvocationHandler(new IWork() { @Override public IWork work(String subject) { return null; } })); worker.work("AAA").work("BBB").work("CCC"); } } 结果如下: CGlib 动态代理实现 首先添加 cglib 依赖 build.gradle 文件: ... dependencies { // 引入 cglib 库 compile 'cglib:cglib:3.1' testCompile group: 'junit', name: 'junit', version: '4.12' } ... 前面说了,cglib 针对类进行代理,我们以上面的 Leader 类为例,先创建一个类实现 MethodInterceptor接口: package com.sharpcj; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class LeaderMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if ("meeting".equals(method.getName())) { System.out.println("代理先准备会议材料..."); return methodProxy.invokeSuper(o, objects); } else if ("evaluate".equals(method.getName())) { if(objects[0] instanceof String) { if ("James".equals(objects[0])) { System.out.println("James 犯过错误,所以考评分数较低..."); return 70; } } return methodProxy.invokeSuper(o, objects); } return null; } } 测试代码: package com.sharpcj; import net.sf.cglib.core.DebuggingClassWriter; import net.sf.cglib.proxy.Enhancer; import java.lang.reflect.Proxy; public class TestApp { public static void main(String[] args) { // System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\temp\code"); //保存生成的 class 文件 Enhancer enhancer = new Enhancer(); // 通过CGLIB动态代理获取代理对象的过程 enhancer.setSuperclass(Leader.class); // 设置enhancer对象的父类 enhancer.setCallback(new LeaderMethodInterceptor()); // 设置enhancer的回调对象 Leader proxy= (Leader)enhancer.create(); // 创建代理对象 // 通过代理对象调用目标方法 proxy.meeting(); proxy.evaluate("Joy"); proxy.evaluate("James"); } } 结果如下: MethodInterceptor 接口只有一个 intercept 方法,这个方法有4个参数: 1)obj表示增强的对象,即实现这个接口类的一个对象;2)method表示要被拦截的方法;3)args表示要被拦截方法的参数;4)proxy表示要触发父类的方法对象; 需要注意的是:实际调用是 methodProxy.invokeSuper(), 如果使用 invoke() 方法,则需要传入被代理的类对象,否则出现死循环,造成 stackOverflow 。 到此这篇关于深入理解Java动态代理与静态代理的文章就介绍到这了,更多相关Java动态静态代理内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!
版权声明:本站部分内容来源互联网,如果文章中所涉及的图片或者文字内容侵犯了您的权益,请联系我们,我们会在确认后第一时间进行删除!
沃梦达教程 本文标题为:深入理解Java动态代理与静态代理 复制本文链接 var clipboard = new Clipboard('.itemCopy'); clipboard.on('success', function(e) { if (e.trigger.disabled == false || e.trigger.disabled == undefined) { e.trigger.innerHTML = "<i class='iconfont icon-attachment'></i>链接复制成功"; e.trigger.disabled = true; setTimeout(function() { e.trigger.innerHTML = "<i class='iconfont icon-attachment'></i>复制本文链接"; e.trigger.disabled = false; }, 2000); } }); clipboard.on('error', function(e) { e.trigger.innerHTML = "链接复制失败"; }); 上一篇: Java中为什么ArrayList初始化容量大小为10 下一篇: Spring Security全新版本使用方式 基础教程推荐 学习HTML 学习jQuery 学习Laravel 学习CSS3 学习Vue.js 学习Bootstrap5 学习ThinkPHP 学习AJAX 猜你喜欢 Java文件管理操作的知识点整理 2023-05-19 java基础知识之FileInputStream流的使用 2023-08-11 springboot自定义starter方法及注解实例 2023-03-31 Java并发编程进阶之线程控制篇 2023-03-07 ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02 Java数据结构之对象比较详解 2023-03-07 Java实现线程插队的示例代码 2022-09-03 Java实现查找文件和替换文件内容 2023-04-06 JDK数组阻塞队列源码深入分析总结 2023-04-18 java实现多人聊天系统 2023-05-19
最新文章 2023-09-01 Java spring-data-mongo、shell 命令行获取 ( 2.x 升级到 3.x ) mongo db的运行状态的实践 2023-09-01 JAVA内存区域 2023-09-01 深入理解java虚拟机之自动内存管理机制(二) 2023-09-01 JAVA 音频转换AMR 转MP3,OS,Linux cent os 7 2023-09-01 Java Service Wrapper 发布Java程序为Windows服务 热门文章 java开源项目jeecgboot的超详细解析 2023-06-17 1502 Java项目实现定时任务的三种方法 2023-01-02 1053 MyBatisPlus-QueryWrapper多条件查询及修改方式 2023-02-04 929 Java实现图片文件上传 2023-01-29 673 swagger注解@ApiModelProperty失效情况的解决 2023-01-02 649 预留广告位,火热招商中! 编程基础 学习PHP 学习JAVA 学习ASP.NET 学习Python 热门标签 word dedecms 织梦 php 正则表达式 匹配 video 跨域问题 区块链 定时任务 资源映射 设计模式 代理模式 EasyExcel 运行原理 归并排序 排序算法 希尔排序 快速排序 冒泡排序 经典算法 Nacos 对象转换 Springboot mysql 远程连接 过滤器 监听器 多进程 图片上传 无刷新 wushuaxi wusu wus 加载 url编码 jdk Socket 循环语句
沃梦达教程网是一个专门为菜鸟打造的前端开发和软件编程的学习网站,可以为编程者和程序员提供海量技术文档,以便编程初学者快速入门,提升开发技术水平和工作效率。 编程基础 HTML/CSS JAVASCRIPT PHP JAVA ASP.NET Python 编程教程 编程基础编程技术编程问答实例代码 © 2023-2024 沃梦达教程 版权所有并保留所有权 网站地图 ICP备案号:粤ICP备14083021号 网站首页 HTML/CSS 菜单 Layui Yii2 网站首页HTML/CSS HTMLHTML5CSSCSS3SassLayuiBootstrap3Bootstrap4Bootstrap5AppMLFont AwesomeFoundation5 JAVASCRIPT JavaScriptjQueryAngularJSAngularJS2Vue.jsVue3ReactTypeScriptEasyUINode.jsAJAXJSONEchartsHighcharts PHP PHPLaravelCakePHPCodeIgniterZendSymfonyYii2PhalconThinkPHPSmarty JAVA JAVASpringHibernateStrutsPlayGWTJspMavenServlet ASP.NET ASP.NETC#Web PagesRazorMVCWeb Forms Python PythonPython 3Django Go GoDockerBeegoBuffaloEchoGinIrisRevel Ruby C C++ Perl Lua Rust Scala VB 移动端 AndroidSwiftionicKotlinHarmonyOS
.night .wmd-logo-nav-night{ background: url(https://www.womengda.cn/xwassets/images/flogo.png) no-repeat!important; background-size: 150px auto; } .night .wmd-app-logo2{ background: url(https://www.womengda.cn/xwassets/images/flogo.png) no-repeat!important; background-size: 92px auto!important; } #canvas { position: absolute; left: 0; top: 0; }