Android 动态代理浅叙
来源:互联网 发布:音轨合成软件 编辑:程序博客网 时间:2024/06/07 01:22
前两篇讲了Android和Js的交互、NDK开发,今天准备写一篇关于动态代理的博客。记得在上家公司项目有个模块用到了动态代理,并且在它基础上进行了扩展运用,自己接触动态代理还是在最早学Java的时候,然后结果大家懂的…
虽然动态代理用起来比较简单,并不复杂,但我还是想写一篇关于它的文章,不为其它的,只为对自己一个交待^-^
在项目开发里,有时候可能会有这种情况,因为某些原因或者需求变动,需要在不改变某个类的某个方法的代码前提下,添加一些额外的逻辑代码进去。这种情况怎么实现?可能第一个想到的——代理,就是今天准备说的代理模式。
比如,现在要添加额外逻辑代码的类(也就是被代理类)的代码,(ps:为了方便演示,这里是创建的Java工程):
public class MyClass { //不可以更改coumpute(int a, int b)方法内部的代码逻辑 public void coumpute(int a, int b){ System.out.println("正在执行计算逻辑,计算参数,a:" + a + ",b:" + b); }}
这里为了简单起见,只是在compute方法里打印了一条日志,现在要求在不改变compute方法任何代码的前提下,在计算逻辑执行之前,执行一段其它代码。
使用动态代理实现,需要满足两个条件,一:代理类和被代理类必须实现同一个接口;二:需要使用到InvocationHandler接口,添加的额外逻辑,主要就是在InvocationHandler实现类的invoke方法中进行实现。
那我们进行第一步,定义一个接口,定义被代理类中的方法
public interface IMyInterface { void compute(int a, int b);}
很简单,就是定义了一个compute方法,然后让MyClass实现定义的接口。第二步,新建一个DynamicProxyHandler实现InvocationHandler,在其中实现添加额外逻辑代码。具体实现代码:
public class DynamicProxyHandler implements InvocationHandler{ //被代理对象 private Object beProxy; public DynamicProxyHandler(Object beProxy){ this.beProxy = beProxy; } //通过Proxy类,获取代理对象 public Object getProxy(){ return Proxy.newProxyInstance( //代理对象和被代理对象应该在同一类加载器 this.beProxy.getClass().getClassLoader(), //获取被代理对象实现的接口(代理类和被代理类必须实现同一接口) this.beProxy.getClass().getInterfaces(), this ); } //不需要手动调用,当代理对象某个方法被调用时,自动调用 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("执行添加的额外代码逻辑"); Object result = method.invoke(this.beProxy, args); return result; }
先通过构造传入被代理对象,然后通过Proxy.newProxyInstance(),获取到代理对象。代理对象和被代理对象必须实现同一接口,代理对象和被代理对象必须实现同一接口,重要的事情说三遍。然后我们要实现的添加,主要就在invoke方法中,动态代理所有的处理全部交给InvocationHandler,InvocationHandler所有被代理方法的处理,全部在invoke方法中。这里,我们也只是简单得在compute方法执行之前,打印了一条日志。
好,看代码应该已经实现了动态代理,我们新建一个MainCls类,(ps:因为我compute方法里有中文,所以在模块的build.gradle文件里,添加了tasks.withType(JavaCompile) {options.encoding = “UTF-8”},避免控制台输出乱码)
public class MainCls { public static void main(String[] args){ //获取代理对象 DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(new MyClass()); IMyInterface proxy = (IMyInterface) dynamicProxyHandler.getProxy(); //调用compute方法 proxy.compute(3, 5); }}
运行测试一下结果:
执行添加的额外代码逻辑正在执行计算逻辑,计算参数,a:3,b:5
输出了这两条log,说明动态代理成功。看到这里,可能有朋友会问,这只是一个方法的代理,如果有多个方法需要代理?
刚刚说过InvocationHandler所有被代理方法的处理,全部是交给了invoke方法处理,那我们直接根据invoke方法的method参数,进行方法名区分,就可以了。比如,MyCls多添加一个query()方法,要求获取到query()方法值后,进行一次额外运算之后,将最后运算结果返回。实现也比较简单,MyCls中添加了query()方法
//不可以更改query()方法内部的代码逻辑,查询到的值,进行一次额外运算逻辑,将最后运算结果返回 @Override public int query() { return 10; }
IMyInterface添加query()
void compute(int a, int b); int query();
DynamicProxyHandler更改invoke方法实现逻辑:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; String methodName = method.getName(); if("compute".equals(methodName)){ //在执行compute方法计算逻辑之前,添加一段额外代码逻辑 System.out.println("compute 执行添加的额外代码逻辑");//添加的额外代码逻辑 result = method.invoke(this.beProxy, args); } else if("query".equals(methodName)){ //获取query方法查询结果 result = method.invoke(this.beProxy, args); //进行额外计算,然后将结果返回 if(result instanceof Integer){ int r = (Integer)result; r*=1000; System.out.println("query 获取到二次计算后数据:" + r); return r; } } return result; }
然后MainCls添加query()方法的调用,运用一下测试结果:
compute 执行添加的额外代码逻辑正在执行计算逻辑,计算参数,a:3,b:5query 获取到二次计算后数据:10000查询到最新数据:10000
说明compute(…)和query()方法都达到要求,全部代理成功。动态代理,大致就是这样一个实现过程。不过每次实现的时候,都需要手动传入被代理的对象实例,这样看起来,好像low了一点。直接使用反射,可不可以呢?我们试一下
//使用反射 return Proxy.newProxyInstance( Class.forName("com.example.MyClass").newInstance().getClass().getClassLoader(), new Class<?>[]{Class.forName("com.example.IMyInterface")}, this);
运行一下,发现结果一致,说明成功。这样调用好像还显得代码有些多,干脆直接这样调用:
IMyInterface proxy = (IMyInterface)Proxy.newProxyInstance( MainCls.class.getClassLoader(),new Class<?>[]{Class.forName("com.example.IMyInterface")}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; String methodName = method.getName(); if("compute".equals(methodName)){ //在执行compute方法计算逻辑之前,添加一段额外代码逻辑 System.out.println("compute 执行添加的额外代码逻辑");//添加的额外代码逻辑 result = method.invoke(//被代理类实例 Class.forName("com.example.MyClass").newInstance(), args); } else if("query".equals(methodName)){ //获取query方法查询结果 result = method.invoke(Class.forName("com.example.MyClass").newInstance(), args); //进行额外计算,然后将结果返回 if(result instanceof Integer){ int r = (Integer)result; r*=1000; System.out.println("query 获取到二次计算后数据:" + r); return r; } } return result; } });
运行:
compute 执行添加的额外代码逻辑正在执行计算逻辑,计算参数,a:3,b:5query 获取到二次计算后数据:10000查询到最新数据:10000
输出结果一致,说明代理成功。(ps:这种调用比较粗暴,不太推荐)
代理模式分动态代理和静态代理,这里我们主要讲的动态代理,如果用静态代理,应该怎么实现?
新建StaticProxy类,实现IMyInterface
public class StaticProxyCls implements IMyInterface{ private IMyInterface beProxy; public StaticProxyCls(IMyInterface beProxy){ this.beProxy = beProxy; } @Override public void compute(int a, int b) { System.out.println("静态代理 执行添加的额外代码逻辑");//添加的额外代码逻辑 beProxy.compute(a,b); } @Override public int query() { int n = beProxy.query(); return n*1000; }}
在MainCls中调用,运行:
静态代理 执行添加的额外代码逻辑正在执行计算逻辑,计算参数,a:3,b:5静态代理 查询到最新数据:10000
说明代理成功。静态代理,相当于组合实现,小量的使用还好,如果大量的使用,缺点就非常明显。比如现在有1000个类需要代理,那就要需要创建1000个来实现对应接口的代理类?!!!
动态代理的原理,涉及到JVM编译机制,我理解得还比较浅显,所以不敢深入讲它的一个原理,只能浅面讲它的一个基本运用。
个人观点,难免有不正确错误的地方,欢迎大家不吝指出。如果需要转载,请注明出处,谢谢。
源码下载
- Android 动态代理浅叙
- android 代理模式 动态代理
- Android 之动态代理
- hook+android动态代理
- Android动态代理模式
- android动态代理机制
- Android 动态代理
- Android实用技巧-动态代理
- Android 反射,动态代理
- Android Java 动态代理应用
- android动态代理学习笔记
- Android动态代理机制浅析
- Android 动态代理以及利用动态代理实现 ServiceHook
- Android下静态代理和动态代理解析
- Android测试函数使用时间(动态代理)
- Android动态加载进阶 代理Activity模式
- Android动态加载进阶 代理Activity模式
- Android插件化开发-hook动态代理
- Java 自定义注解实现ORM对象关系映射
- 归一化与标准化
- 历届试题网络寻路(dfs)
- Python pandas
- [XJOI]黑白树
- Android 动态代理浅叙
- jquery中用width还是width(val)获取当前窗口的宽度
- JAVA中子父类的调用
- Codeforces Round #319 (Div. 2)B. Modulo Sum(数学,DP)
- Java注解初体验(简单ORM映射框架)
- Attack on Titans ZOJ 3747
- 关于Java的封装、继承、抽象类和接口
- css 的规律摸索之路(一)css之三角形的规律及变化
- 谓词