java静态代理和动态代理
本文动态代理部分内容大量引自:http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/
一、代理概念
为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
图1:代理模式
从图中可以看出,代理接口(Subject)、代理类(ProxySubject)、委托类(RealSubject)形成一个“品”字结构。
根据代理类的生成时间不同可以将代理分为静态代理和动态代理两种。
下面以一个模拟需求说明静态代理和动态代理:委托类要处理一项耗时较长的任务,客户类需要打印出执行任务消耗的时间。解决这个问题需要记录任务执行前时间和任务执行后时间,两个时间差就是任务执行消耗的时间。
二、静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
清单1:代理接口
/** * 代理接口。处理给定名字的任务。 */public interface Subject { /** * 执行给定名字的任务。 * @param taskName 任务名 */ public void dealTask(String taskName); } /** * 真正执行任务的类,实现了代理接口。 */public class RealSubject implements Subject { /** * 执行给定名字的任务。这里打印出任务名,并休眠500ms模拟任务执行了很长时间 * @param taskName */ @Override public void dealTask(String taskName) { System.out.println("正在执行任务:"+taskName); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } }} /** * 代理类,实现了代理接口。 */public class ProxySubject implements Subject { //代理类持有一个委托类的对象引用 private Subject delegate; public ProxySubject(Subject delegate) { this.delegate = delegate; } /** * 将请求分派给委托类执行,记录任务执行前后的时间,时间差即为任务的处理时间 * * @param taskName */ @Override public void dealTask(String taskName) { long stime = System.currentTimeMillis(); //将请求分派给委托类处理 delegate.dealTask(taskName); long ftime = System.currentTimeMillis(); System.out.println("执行任务耗时"+(ftime - stime)+"毫秒"); }}public class SubjectStaticFactory { //客户类调用此工厂方法获得代理对象。 //对客户类来说,其并不知道返回的是代理类对象还是委托类对象。 public static Subject getInstance(){ return new ProxySubject(new RealSubject()); }} public class Client1 { public static void main(String[] args) { Subject proxy = SubjectStaticFactory.getInstance(); proxy.dealTask("DBQueryTask"); } }// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器static InvocationHandler getInvocationHandler(Object proxy) // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象static Class getProxyClass(ClassLoader loader, Class[] interfaces) // 方法 3:该方法用于判断指定类对象是否是一个动态代理类static boolean isProxyClass(Class cl) // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行Object invoke(Object proxy, Method method, Object[] args)
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); // 通过反射从生成的类对象获得构造函数对象Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); // 通过构造函数对象创建动态代理类实例Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler }); // InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 直接创建动态代理类实例Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler ); /** * 动态代理类对应的调用处理程序类 */public class SubjectInvocationHandler implements InvocationHandler { //代理类持有一个委托类的对象引用 private Object delegate; public SubjectInvocationHandler(Object delegate) { this.delegate = delegate; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long stime = System.currentTimeMillis(); //利用反射机制将请求分派给委托类处理。Method的invoke返回Object对象作为方法执行结果。 //因为示例程序没有返回值,所以这里忽略了返回值处理 method.invoke(delegate, args); long ftime = System.currentTimeMillis(); System.out.println("执行任务耗时"+(ftime - stime)+"毫秒"); return null; }} /** * 生成动态代理对象的工厂. */public class DynProxyFactory { //客户类调用此工厂方法获得代理对象。 //对客户类来说,其并不知道返回的是代理类对象还是委托类对象。 public static Subject getInstance(){ Subject delegate = new RealSubject(); InvocationHandler handler = new SubjectInvocationHandler(delegate); Subject proxy = null; proxy = (Subject)Proxy.newProxyInstance( delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), handler); return proxy; }}public class Client { public static void main(String[] args) { Subject proxy = DynProxyFactory.getInstance(); proxy.dealTask("DBQueryTask"); } }