代理模式:html
代理模式是经常使用的java设计模式,他的特征是代理类与委托类有一样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及过后处理消息等。经过代理模式,能够延迟建立对象,限制访问某个对象,也就是说,提供一组方法给普通用户,特别方法给管理员用户。java
UML图:git
简单结构示意图:程序员
为了保持行为的一致性,代理类和委托类一般会实现相同的接口,因此在访问者看来二者没有丝毫的区别。github
按照代理的建立时期,代理类能够分为两种:设计模式
静态代理:dom
为了帮助理解代理模式,来看一下静态代理的示例代码(代码摘自这里):ide
Count.java 函数
1 /** 2 * 定义一个帐户接口 3 * @author Administrator 4 */ 5 public interface Count { 6 // 查看帐户方法 7 public void queryCount(); 8 // 修改帐户方法 9 public void updateCount(); 10 }
CountImpl.java 工具
1 /** 2 * 委托类(包含业务逻辑) 3 * @author Administrator 4 */ 5 public class CountImpl implements Count { 6 7 @Override 8 public void queryCount() { 9 System.out.println("查看帐户方法..."); 10 } 11 12 @Override 13 public void updateCount() { 14 System.out.println("修改帐户方法..."); 15 } 16 }
CountProxy.java
1 public class CountProxy implements Count { 2 private CountImpl countImpl; 3 /** 4 * 覆盖默认构造器 5 * @param countImpl 6 */ 7 public CountProxy(CountImpl countImpl) { 8 this.countImpl = countImpl; 9 } 10 11 @Override 12 public void queryCount() { 13 System.out.println("事务处理以前"); 14 // 调用委托类的方法; 15 countImpl.queryCount(); 16 System.out.println("事务处理以后"); 17 } 18 19 @Override 20 public void updateCount() { 21 System.out.println("事务处理以前"); 22 // 调用委托类的方法; 23 countImpl.updateCount(); 24 System.out.println("事务处理以后"); 25 } 26 }
TestCount.java
1 /** 2 *测试Count类 3 * @author Administrator 4 */ 5 public class TestCount { 6 public static void main(String[] args) { 7 CountImpl countImpl = new CountImpl(); 8 CountProxy countProxy = new CountProxy(countImpl); 9 countProxy.updateCount(); 10 countProxy.queryCount(); 11 } 12 }
以上静态代理的代码结合前面的结构图和UML图,相信不难理解代理模式的基本原理。
JDK动态代理
为了提升代理的灵活性和可扩展性,减小重复代码,咱们可使用JDK提供的动态代理。
// 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 });
实际使用过程更加简单,由于 Proxy 的静态方法 newProxyInstance 已经为咱们封装了步骤 2 到步骤 4 的过程:
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 InvocationHandler handler = new InvocationHandlerImpl(..); // 经过 Proxy 直接建立动态代理类实例 Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
JAVA示例代码:
public class TraceHandler implements InvocationHandler{ private Object target = null; public TraceHandler(Object t) { this.target = t; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.print(target); System.out.print("." + method.getName() + "("); if(args != null) { for(int i = 0; i < args.length; ++i) { System.out.print(args[i]); if(i < args.length-1) System.out.print(", "); } } System.out.println(")"); return method.invoke(target, args); } }
Test.java
public void test() { Object[] elements = new Object[1000]; for (int i = 0; i < elements.length; i++) { Integer val = i+1; TraceHandler handler = new TraceHandler(val); elements[i] = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler); } Integer key = new Random().nextInt(1000) + 1; int result = Arrays.binarySearch(elements, key); if (result > 0) { System.out.println(elements[result]); } }
Proxy 静态方法生成动态代理类一样须要经过类装载器来进行装载才能使用,它与普通类的惟一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。每次生成动态代理类对象时都须要指定一个类装载器对象。
动态生成的代理类自己的一些特色:
被代理的接口的特色:
异常处理的特色:
基于CGLIB的动态代理
因为JDK的动态代理依靠接口实现,若是有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。
CGlib概述:
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现加强,但由于采用的是继承,因此不能对final修饰的类进行代理。 很少说,直接上代码!
有一个Manager类:
public class Manager { public void query() { System.out.println("query..."); } public void insert() { System.out.println("insert..."); } public String update() { System.out.println("update...."); return "I'm update"; } public void delete() { System.out.println("delete...."); } }
咱们想要在这个类中的每一个方法前面和后面都打印一句话,这时候咱们就可使用代理了,让咱们来看一下这个代理能够怎么写:
public class AuthProxy implements MethodInterceptor{ @Override public Object intercept(Object arg0, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before..."); Object result = proxy.invokeSuper(arg0, args); System.out.println("After...."); return result; } }
如上,CGLIB实现的代理类必须实现MethodInterceptor接口,该接口中只有一个方法须要实现,即intercept方法。经过MethodProxy中的invokeSuper便可执行被代理类的方法。咱们继续往下看。
public static Manager getInstace(AuthProxy auth) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Manager.class); enhancer.setCallback(auth); return (Manager) enhancer.create(); }
经过以上代码咱们就能获得一个Manager的代理类,被AuthProxy代理。
public void AuthProxyTest() { AuthProxy auth = new AuthProxy(); Manager manager = ManagerFactory.getInstace(auth); manager.delete(); System.out.println(); manager.query(); System.out.println(); String result = manager.update(); System.out.println("result: " + result); }
下面是一个能够代理不一样类的代理生成工厂:
public class ManagerFactory2 { public static Manager getInstace(Class clasz, AuthProxy auth) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clasz); enhancer.setCallback(auth); return (Manager) enhancer.create(); } }
对这个工厂进行通用化扩展:
public class ManagerFactory { public static Manager getInstace(Class clasz, AuthProxyFilter filter, AuthProxy auth, Object...args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clasz); Callback[] callback = new Callback[args.length+1]; System.out.println("length: " + callback.length); callback[0] = auth; for (int i = 0; i < args.length; i++) { if (args[i] instanceof Callback) { callback[i+1] = (Callback) args[i]; }else { callback[i+1] = NoOp.INSTANCE; } } enhancer.setCallbacks(callback); enhancer.setCallbackFilter(filter); return (Manager) enhancer.create(); }
AuthProxyFilter.java
public class AuthProxyFilter implements CallbackFilter{ @Override public int accept(Method method) { if ("query".equals(method.getName())) { return 1; } return 0; } }
=====================华丽的分割线==================================
源码请猛戳{ 这里 }
===============================================================
参考资料:
http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html
http://www.blogjava.net/stone2083/archive/2008/03/16/186615.html