老生常谈设计模式之动态代理
一、动态代理概念
动态代理分为JDK动态代理和cglib动态代理两种方式。
jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。
总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。
还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。
由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
二、JDK动态代理
以下代码使用代理模式实现一个大小写字符转换的功能。
定义接口和实现类:
ISomeService接口:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk; /** * 接口类 * * @author Root */ public interface ISomeService { String doFirst(); void doSecond(); }
SomeServiceImpl实现类:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk; /** * 实现类 * * @author Root */ public class SomeServiceImpl implements ISomeService { @Override public String doFirst() { System.out.println("执行doFirst()..."); String result = "abcde"; return result; } @Override public void doSecond() { System.out.println("执行doSecond()..."); } }
JDK动态代理类:
package com.ietree.basicskill.designpattern.dynamicproxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { final ISomeService target = new SomeServiceImpl(); // 使用JDK的Proxy动态代理,要求目标类和代理类必须实现相同的接口,因为其底层的执行原理与静态代理的相同 ISomeService service = (ISomeService) Proxy.newProxyInstance( // 目标类的类加载器 target.getClass().getClassLoader(), // 目标类所实现的所有接口 target.getClass().getInterfaces(), new InvocationHandler() { // proxy:代理对象 // method:目标方法 // args:目标方法的参数列表 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 调用目标方法 Object result = method.invoke(target, args); if (result != null) { result = ((String) result).toUpperCase(); } return result; } }); String result = service.doFirst(); System.out.println(result); service.doSecond(); } }
三、cglib动态代理
Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:
CGLIB的核心类:
net.sf.cglib.proxy.Enhancer ? 主要的增强类
net.sf.cglib.proxy.MethodInterceptor ? 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy ? JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。
net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;
第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。
以下程序实现了大小写转换的功能:
实现类SomeService:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib; /** * 实现类 * * @author Root */ public class SomeService { public String doFirst() { System.out.println("执行doFirst()..."); String result = "abcde"; return result; } public void doSecond() { System.out.println("执行doSecond()..."); } }
代理类MyCglibFactory:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class MyCglibFactory implements MethodInterceptor { private SomeService target; public MyCglibFactory() { super(); target = new SomeService(); } public SomeService myCglibCreator() { // 创建增强器对象 Enhancer enhancer = new Enhancer(); // 指定目标类,即父类 enhancer.setSuperclass(SomeService.class); // 设置回调接口对象 enhancer.setCallback(this); return (SomeService) enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 调用目标方法 Object result = method.invoke(target, args); if (result != null) { result = ((String) result).toUpperCase(); } return result; } }
测试:
package com.ietree.basicskill.designpattern.dynamicproxy.cglib; public class Main { public static void main(String[] args) { SomeService service = new MyCglibFactory().myCglibCreator(); String result = service.doFirst(); System.out.println("result = " + result); service.doSecond(); } }
运行结果:
执行doFirst()... result = ABCDE 执行doSecond()...
以上这篇老生常谈设计模式之动态代理就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。
- 打包自己的aar库
- 揭开RecyclerView庐山真面目
- Python解析psiBlast输出的JSON文件结果
- 经验分享:社会工程学数据库搭建TIPS
- 过时但仍值得学习的选项卡TabHost
- 你所不知道的渗透测试:应用虚拟化的攻防
- C++中const小结
- 很多人不知道还有这个——搜索框组件SearchView
- 免费主题暗藏后门,波及WordPress等知名CMS系统
- 揭秘:针对PoS机的恶意软件工具箱
- 屏幕宽高不够,滚动视图ScrollView来凑
- 结合中间人攻击,Pidgin鸡肋漏洞变废为宝
- 日历视图CalendarView和定时器Chronometer
- 不用Linux也可以的强大文本处理方法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 百万级类别的分类模型的拆分训练
- 关于Spring定义的preDestroy修饰的方法不执行,有以下两种原因,总有一款适合你
- CSP201912-2-回收站选址题目解析-Java ,
- Spring boot框架快速入门
- SpringBoot 跨域问题:Access to XMLHttpRequest at ‘***‘ from origin ‘***‘ has been blocked by CORS policy
- 火车购票-CSP201609-2-Java
- 从后端开发人员的视角:最浅显的理解 Vue
- Sublime怎么默认显示文件路径
- Git 删除已提交的文件
- Sublime 怎么装SQL语法检测器
- Vue 设置环境变量和模式
- 你真的掌握了Python基本语法了吗?
- Mybatis-Generator 代码生成器的使用
- 提高数据分析工作效率-Sublime如何设置默认打开文件格式
- 使用 freemarker 制作代码生成器