Java动态代理
2016-06-17 17:43
507 查看
1、简介
在java常用的设计模式中有一种模式叫:代理模式----通过一个代理类负责处理被代理类(委托类)的调用,如果你了解或使用过Spring的切面(AOP),可以更好的理解何为代理,通俗的讲就是在一个类的方法执行的前后加入操作(如进入和完成方法打印日志),实际上Spring正是使用java的动态代理来实现切面的;一般代理类和委托类实现同样的接口,代理类通过调用委托类对象的方法真正的实现服务,并且可以在实现的前后增加需要的处理。
从代理创建时期区分,java的代理可以分为两种:
静态代理:代理类在程序运行前就已经存在;
动态代理:代理类在程序的运行期间才创建;
2、静态代理
静态代理方式更好理解,我们先简单实用测试代码说明,以手机生产为例:一般手机厂商都不是通过自己整机生产手机,而是由代工厂代理生产;
可以看出,静态代理的优点可以解耦调用方和委托类,在不修改委托类的源码情况下增加处理,但是静态代理的代理类代码是必须在程序编译运行前编写好。
3、动态代理
简单介绍完静态代理,下面重点介绍java动态代理:
顾名思义,动态代理的代理类是在程序运行中动态创建的,也就是说代理类并不是由我们通过代码定义的,
下面我们通过以下需求来详细说明:
对所有被调用的外部类的方法全部在方法开始前打印入参日志,方法执行完成后打印返回结果日志
先按照静态代理的方式实现:
通过这样的静态代理方式,实现简单,也容易理解,但是对于这个增加日志打印的需求,这种实现方式明显不好,每个外部接口都要实现一个代理类,接口中的每个方法都要增加日记打印语句,这样的方式重复工作量大,繁琐,谁都不愿意这样实现;
通过动态代理方式就可以高效的实现上面的需求:
动态代理类的字节码在程序运行中由java的反射机制生成,具有更好的扩展性;java.lang.reflect包下Proxy类和InvocationHandler接口提供实现动态代理的方式:
invoke参数:
proxy : 生成的代理类实例对象
method : 实际要调用的方法
args : 方法调用时的参数
InvocationHandler 接口可以理解为“调用处理器”,但是具体的实现有程序员决定;
Proxy类,是实际完成代理的操作类,由这个类的方法动态生成代理对象,使用如下方法:
loader:类加载器
interfaces:需要代理的类的全部接口
handler:上面InvocationHandler的实例
下面我们通过动态代理来实现上面的需求(打印日志):
动态代理方式能可以更加高效的实现需求,不需要每个类都写一个代理类;
通过以上的示例代码可以看出,java的动态代理是必须基于接口实现的,当然这也非常符合java编程的规范;
在java常用的设计模式中有一种模式叫:代理模式----通过一个代理类负责处理被代理类(委托类)的调用,如果你了解或使用过Spring的切面(AOP),可以更好的理解何为代理,通俗的讲就是在一个类的方法执行的前后加入操作(如进入和完成方法打印日志),实际上Spring正是使用java的动态代理来实现切面的;一般代理类和委托类实现同样的接口,代理类通过调用委托类对象的方法真正的实现服务,并且可以在实现的前后增加需要的处理。
从代理创建时期区分,java的代理可以分为两种:
静态代理:代理类在程序运行前就已经存在;
动态代理:代理类在程序的运行期间才创建;
2、静态代理
静态代理方式更好理解,我们先简单实用测试代码说明,以手机生产为例:一般手机厂商都不是通过自己整机生产手机,而是由代工厂代理生产;
/** * 手机生产接口 * @author hwz * */ public interface PhoneBuilder { public void buildAPhone(); }
/** * 手机生产厂商 * @author hwz * */ public class PhoneVendor implements PhoneBuilder{ @Override public void buildAPhone() { //设计和生产手机 System.out.println("design and build a phone"); } }
/** * 代理工厂类 * @author hwz * */ public class PhoneBuilderProxyFactory implements PhoneBuilder { private PhoneBuilder phoneBuilder;//代理类一把通过聚合持有委托类的一个实例 public PhoneBuilderProxyFactory(PhoneBuilder phoneBuilder) { this.phoneBuilder = phoneBuilder; } @Override public void buildAPhone() { phoneBuilder.buildAPhone(); //组装手机 System.out.println("phone assemble and produce..."); } }
/** * 静态代理测试 * @author hwz * */ public class StaticProxyTest { public static void main(String[] args) { PhoneVendor realBuilder = new PhoneVendor(); PhoneBuilderProxyFactory proxyBuilder = new PhoneBuilderProxyFactory(realBuilder); proxyBuilder.buildAPhone(); } }
可以看出,静态代理的优点可以解耦调用方和委托类,在不修改委托类的源码情况下增加处理,但是静态代理的代理类代码是必须在程序编译运行前编写好。
3、动态代理
简单介绍完静态代理,下面重点介绍java动态代理:
顾名思义,动态代理的代理类是在程序运行中动态创建的,也就是说代理类并不是由我们通过代码定义的,
下面我们通过以下需求来详细说明:
对所有被调用的外部类的方法全部在方法开始前打印入参日志,方法执行完成后打印返回结果日志
先按照静态代理的方式实现:
/** * 一个简单的外部接口 * @author hwz */ public interface OneOuterInterface { /** * 方法一:翻转字符串 * @param str * @return */ public String methodOne(String str); /** * 方法二:计算两个数的乘积 * @param a * @param b * @return */ public int methodSecond(int a, int b); }
/** * 简单的外部类 * @author hwz * */ public class OneOuterClass implements OneOuterInterface { @Override public String methodOne(String str) { return new StringBuilder(str).reverse().toString(); } @Override public int methodSecond(int a, int b) { return a*b; } }
/** * 代理类 * @author hwz * */ public class InnerProxyClass implements OneOuterInterface { private OneOuterInterface outer; public InnerProxyClass(OneOuterInterface outer) { this.outer = outer; } @Override public String methodOne(String str) { System.out.println("Enter methodOne,str=" + str); String result = outer.methodOne(str); System.out.println("Leave methodOne,result=" + result); return result; } @Override public int methodSecond(int a, int b) { System.out.println("Enter methodSecond,a=" + a + ",b=" + b); int result = outer.methodSecond(a, b); System.out.println("Leave methodSecond,result=" + result); return result; } }
/** * 测试代码 * @author hwz * */ public class ProxyOuterClassTest { public static void main(String[] args) { InnerProxyClass proxy = new InnerProxyClass(new OneOuterClass()); proxy.methodOne("abcdef"); proxy.methodSecond(2, 3); } }
通过这样的静态代理方式,实现简单,也容易理解,但是对于这个增加日志打印的需求,这种实现方式明显不好,每个外部接口都要实现一个代理类,接口中的每个方法都要增加日记打印语句,这样的方式重复工作量大,繁琐,谁都不愿意这样实现;
通过动态代理方式就可以高效的实现上面的需求:
动态代理类的字节码在程序运行中由java的反射机制生成,具有更好的扩展性;java.lang.reflect包下Proxy类和InvocationHandler接口提供实现动态代理的方式:
public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args); }
invoke参数:
proxy : 生成的代理类实例对象
method : 实际要调用的方法
args : 方法调用时的参数
InvocationHandler 接口可以理解为“调用处理器”,但是具体的实现有程序员决定;
Proxy类,是实际完成代理的操作类,由这个类的方法动态生成代理对象,使用如下方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)
loader:类加载器
interfaces:需要代理的类的全部接口
handler:上面InvocationHandler的实例
下面我们通过动态代理来实现上面的需求(打印日志):
/** * 代理处理器 * @author hwz * */ public class OutInterfaceInvokeHandler implements InvocationHandler { private Object proxyObj; public Object proxy(Object proxyObj) { this.proxyObj = proxyObj; return Proxy.newProxyInstance(proxyObj.getClass().getClassLoader(), proxyObj.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { StringBuilder builder = new StringBuilder(); if (args != null) { for (int i=0; i<args.length; ++i) { builder.append(",arg" + i + "=" + args[i].toString()); } } System.out.println("Enter " + method.toString() + ",args:" + builder.toString()); //此处proxy是生成代理类的实例 Object result = method.invoke(proxyObj, args); System.out.println("Leave " + method + ",result=" + result.toString()); return result; } }
/** * 测试代码 * @author hwz * */ public class ProxyOuterClassTest { public static void main(String[] args) { OneOuterInterface outer = new OneOuterClass(); OutInterfaceInvokeHandler handler = new OutInterfaceInvokeHandler(); OneOuterInterface proxy = (OneOuterInterface) handler.proxy(outer); proxy.methodOne("abcdef"); proxy.methodSecond(2, 4); } }注意:InvocationHandler.invoke方法的第一个入参是动态生成代理类的实例对象引用,直接使用这个proxy调用方法会发生无限循环调用
动态代理方式能可以更加高效的实现需求,不需要每个类都写一个代理类;
通过以上的示例代码可以看出,java的动态代理是必须基于接口实现的,当然这也非常符合java编程的规范;
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树