代理是一种经常使用的设计模式,其目的就是为其余对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被为拖累执行后的后续处理。
为了保持行为的一致性,代理类和委托类一般会实现相同的接口,因此在访问者看来二者没有丝毫的区别。经过代理类这中间一层,能有效控制对委托类对象的直接访问,也能够很好地隐藏和保护委托类对象,同时也为实施不一样控制策略预留了空间,从而在设计上得到了更大的灵活性。java
更通俗的说,代理解决的问题当两个类须要通讯时,引入第三方代理类,将两个类的关系解耦,让咱们只了解代理类便可,并且代理的出现还可让咱们完成与另外一个类之间的关系的统一管理,可是切记,代理类和委托类要实现相同的接口,由于代理真正调用的仍是委托类的方法。程序员
按照代理的建立时期,代理类能够分为两种:
静态:由程序员建立代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态建立而成设计模式
下面是两个例子说明了静态代理和动态代理的使用方法:数组
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package Proxy; /** * Created by benjamin on 1/15/16. */ public interface UserManager { void addUser(String userId, String userName); void deleteUser(String userId); String findUser(String userId); void modifyUser(String userId, String userName); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package Proxy; /** * Created by benjamin on 1/15/16. */ public class UserManagerImpl implements UserManager { public void addUser(String userId, String userName) { System.out.println("UserManagerImpl.addUser: " + userId + ": " + userName); } public void deleteUser(String userId) { System.out.println("UserManagerImpl.deleteUser"); } public String findUser(String userId) { System.out.println("UserManagerImpl.findUser"); return "benjamin"; } public void modifyUser(String userId, String userName) { System.out.println("UserManagerImpl.modifyUser"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package Proxy; /** * Created by benjamin on 1/15/16. */ public class UserManagerProxy implements UserManager { private UserManager object; // 构造函数传入目标对象 public UserManagerProxy(UserManager object) { this.object = object; } public void addUser(String userId, String userName) { try { // 添加基本的日志打印功能 System.out.println("开始addUser"); object.addUser(userId, userName); System.out.println("成功addUser"); } catch (Exception e) { System.err.println("error addUser"); } } public void deleteUser(String userId) { object.deleteUser(userId); } public String findUser(String userId) { return object.findUser(userId); } public void modifyUser(String userId, String userName) { object.modifyUser(userId, userName); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package Proxy; /** * Created by benjamin on 1/15/16. */ public class Client { public static void main(String[] args) { UserManager userManager = new UserManagerProxy(new UserManagerImpl()); userManager.addUser("1111", "张三"); /** * 开始addUser UserManagerImpl.addUser: 1111: 张三 成功addUser */ } } |
相信有一点基础的同窗均可以看出静态代理的缺点:
1)代理类和委托类实现了相同的接口,代理类经过委托类实现了相同的方法。这样就出现了大量的代码重复。若是接口增长一个方法,除了全部实现类须要实现这个方法外,全部代理类也须要实现此方法。增长了代码维护的复杂度。
2)代理对象只服务于一种类型的对象,若是要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就没法胜任了。如上的代码只是为UserManager类的访问提供了代理,可是若是还要为其余类如Department类提供代理的话,就须要咱们再次添加代理Department的代理类。缓存
这样咱们必需要引入动态代理:
在上面的示例中,一个代理只能代理一种类型,并且是在编译器就已经肯定被代理的对象。而动态代理是在运行时,经过反射机制实现动态代理,而且可以代理各类类型的对象安全
原来的接口和实现类都不变,这里就再也不重复写了,只是代理类使用了动态代理来实现。服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
package Proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Created by benjamin on 1/15/16. */ public class UserManagerProxy implements InvocationHandler { // 目标对象 private Object targetObject; public Object newProxyInstance(Object targetObject) { this.targetObject = targetObject; // 第一个参数指定产生代理对象的类加载器,须要将其指定为和目标对象同一个类加载器 // 第二个参数要实现和目标对象同样的接口,因此只须要拿到目标对象的实现接口 // 第三个参数代表这些被拦截的方法在被拦截时须要执行哪一个InvocationHandler的invoke方法 // 根据传入的目标返回一个代理对象 return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this); } /** * 关联的这个实现类的方法被调用时将被执行 * @param proxy 代理 * @param method 原对象被调用的方法 * @param args 方法的参数 * @return 原方法的返回值 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object ret = null; try { System.out.println("start invoke-->"); // 调用目标方法 ret = method.invoke(targetObject, args); System.out.println("success invoke-->"); } catch (Exception e) { e.printStackTrace(); System.out.println("error-->"); throw e; } return ret; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package Proxy; /** * Created by benjamin on 1/15/16. */ public class Client { public static void main(String[] args) { UserManager userManager = (UserManager)new UserManagerProxy().newProxyInstance(new UserManagerImpl()); userManager.addUser("1111", "张三"); /** * start invoke--> UserManagerImpl.addUser: 1111: 张三 success invoke--> */ } } |
AOP(AspectOrientedProgramming):将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,经过对这些行为的分离,咱们但愿能够将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码—解耦。
正是由于在全部的类里,核心代码以前的操做和核心代码以后的操做都作的是一样的逻辑,所以咱们须要将它们提取出来,单独分析,设计和编码,这就是咱们的AOP思想。一句话说,AOP只是在对OOP的基础上进行进一步抽象,使咱们的类的职责更加单一。app
动态代理的优势:
动态代理与静态代理相比较,最大的好处是接口中声明的全部方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,咱们能够进行灵活处理,而不须要像静态代理那样每个方法进行中转。并且动态代理的应用使咱们的类职责更加单一,复用性更强框架
JDK自从1.3版本开始,就引入了动态代理,JDK的动态代理用起来很是简单,可是它有一个限制,就是使用动态代理的对象必须实现一个或多个接口 。若是想代理没有实现接口的类可使用CGLIB包。
CGLIB是一个强大的高性能的代码生成包。它被许多AOP的框架(例如Spring AOP)使用,为他们提供方法的interception(拦截)。Hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联。EasyMock经过使用模仿(moke)对象来测试java代码的包。它们都经过使用CGLIB来为那些没有接口的类建立模仿(moke)对象。
CGLIB包的底层是经过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,由于它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
下面经过一个实例来说解cglib:
socket
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.benjamin; /** * 定义一个HelloWorld类 * * @author benjamin * */ public class HelloWorld { public void sayHelloWorld() { System.out.println("HelloWorld!"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
package com.benjamin; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 经过Cglib实如今方法调用先后向控制台输出两句字符串 * * @author benjamin * */ public class CglibProxy implements MethodInterceptor { //要代理的原始对象 private Object obj; public Object createProxy(Object target) { this.obj = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.obj.getClass());// 设置代理目标 enhancer.setCallback(this);// 设置回调 enhancer.setClassLoader(target.getClass().getClassLoader()); return enhancer.create(); } /** * 在代理实例上处理方法调用并返回结果 * * @param proxy * 代理类 * @param method * 被代理的方法 * @param params * 该方法的参数数组 * @param methodProxy */ public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable { Object result = null; // 调用以前 doBefore(); // 调用原始对象的方法 result = methodProxy.invokeSuper(proxy, params); // 调用以后 doAfter(); return result; } private void doBefore() { System.out.println("before method invoke"); } private void doAfter() { System.out.println("after method invoke"); } } |
1 2 3 4 5 6 7 8 9 10 11 |
package com.benjamin; public class HelloWorldTest { public static void main(String[] args) { HelloWorld helloWorld=new HelloWorld(); CglibProxy cglibProxy=new CglibProxy(); HelloWorld hw=(HelloWorld)cglibProxy.createProxy(helloWorld); hw.sayHelloWorld(); } } |
运行结果为:
1 2 3 |
before method invoke HelloWorld! after method invoke |
远程代理分为服务端和客户端,都必须存在接口定义的文件。这样客户端只须要经过远程代理就能够拥有存在服务端方法同样的感受。
共同的代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
package remoteProxy; import java.io.Serializable; /** * Created by piqiu on 1/15/16. */ public class Call implements Serializable { private static final long serialVersionUID = 4924678505695074146L; private String className; private String methodName; private Class[] paramTypes; private Object[] params; private Object result; public Call() {} public Call(String className, String methodName, Class[] paramTypes, Object[] params) { this.className = className; this.methodName = methodName; this.paramTypes = paramTypes; this.params = params; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("{className=" + className).append(",methodName=" + methodName).append(",result=" + result).append("}"); return sb.toString(); } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Class[] getParamTypes() { return paramTypes; } public void setParamTypes(Class[] paramTypes) { this.paramTypes = paramTypes; } public Object[] getParams() { return params; } public void setParams(Object[] params) { this.params = params; } public Object getResult() { return result; } public void setResult(Object result) { this.result = result; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package remoteProxy; import java.rmi.RemoteException; import java.util.Date; /** * Created by benjamin on 1/15/16. */ public interface IHelloService { String echo(String msg) throws RemoteException; Date getTime() throws RemoteException; } |
服务端代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package remoteProxy.Server; import remoteProxy.IHelloService; import java.util.Date; /** * Created by benjamin on 1/15/16. */ public class HelloServiceImpl implements IHelloService { public String echo(String msg) { System.out.println("HelloServiceImpl.echo: " + msg); return msg; } public Date getTime() { System.out.println("HelloServiceImpl.getTime: " + new Date()); return new Date(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
package remoteProxy.Server; import remoteProxy.Call; import java.io.*; import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; /** * Created by benjamin on 1/15/16. */ public class SimpleServer { private Map remoteObjects = new HashMap(); /** 把一个远程对象放到缓存中 **/ public void register(String className, Object remoteObject) { remoteObjects.put(className, remoteObject); } public void service() throws Exception { ObjectInputStream ois = null; ObjectOutputStream oos = null; Socket socket = null; try { ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务器启动......"); while (true) { socket = serverSocket.accept(); ois = new ObjectInputStream(socket.getInputStream()); // 读取对象 oos = new ObjectOutputStream(socket.getOutputStream()); // 获得输出流 Call call = (Call)ois.readObject(); // 接收客户端发送的Call对象 call = invoke(call); // 执行完操做拿回结果 oos.writeObject(call); // 发送结果 } } finally { // 关闭流 if (ois != null) ois.close(); if (oos != null) oos.close(); if (socket != null) socket.close(); } } public Call invoke(Call call) { Object result = null; try { String className = call.getClassName(); String methodName = call.getMethodName(); Object[] params = call.getParams(); Class[] paramTypes = call.getParamTypes(); Class classType = Class.forName(className); Method method = classType.getMethod(methodName, paramTypes); Object remoteObject = remoteObjects.get(className); // 从缓存中读取相关的缓存对象 if (remoteObject == null) { throw new Exception(className + "的远程对象不存在"); } else { result = method.invoke(remoteObject, params); } } catch (Exception e) { result = e; } call.setResult(result); return call; } public static void main(String[] args) throws Exception { SimpleServer server = new SimpleServer(); server.register("remoteProxy.IHelloService", new HelloServiceImpl()); // 启动服务 server.service(); } } |
客户端代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
package remoteProxy.Client; import java.io.*; import java.net.Socket; /** * Created by Benjamin on 1/15/16. * Connector 类负责创建与远程服务器的链接,以及接收和发送Socket对象 */ public class Connector { private String host; private int port; private Socket skt; private InputStream is; private ObjectInputStream ois; private OutputStream os; private ObjectOutputStream oos; public Connector(String host, int port) throws Exception { this.host = host; this.port = port; connect(host, port); } /** 发送对象 **/ public void send(Object obj) throws IOException { oos.writeObject(obj); } /** 接收对象 **/ public Object receive() throws IOException, ClassNotFoundException { return ois.readObject(); } /** 创建与远程服务器的链接 **/ public void connect() throws Exception { connect(host, port); } public void connect(String host, int port) throws Exception { skt = new Socket(host, port); os = skt.getOutputStream(); oos = new ObjectOutputStream(os); is = skt.getInputStream(); ois = new ObjectInputStream(is); } public void close() { // 关闭链接 try { } finally { try { ois.close(); oos.close(); skt.close(); } catch (Exception e) { System.out.println("Connector.close: " + e); } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
package remoteProxy.Client; import remoteProxy.Call; import remoteProxy.IHelloService; import java.rmi.RemoteException; import java.util.Date; /** * Created by piqiu on 1/15/16. */ public class HelloServiceProxy implements IHelloService { private String host; private int port; public HelloServiceProxy(String host, int port) { this.host = host; this.port = port; } public String echo(String msg) throws RemoteException { Connector connector = null; try { connector = new Connector(host, port); Call call = new Call("remoteProxy.IHelloService", "echo", new Class[]{String.class}, new Object[]{msg}); connector.send(call); call = (Call)connector.receive(); Object result = call.getResult(); if (result instanceof Throwable) { throw new RemoteException("", (Throwable)result); } else { return (String)result; } } catch (Exception e) { throw new RemoteException("", e); } finally { if (connector != null) connector.close(); } } public Date getTime() throws RemoteException { Connector connector = null; try { connector = new Connector(host, port); Call call = new Call("remoteProxy.IHelloService", "getTime", new Class[]{}, new Object[]{}); connector.send(call); call = (Call)connector.receive(); Object result = call.getResult(); if (result instanceof Throwable) { throw new RemoteException("", (Throwable)result); } else { return (Date)result; } } catch (Exception e) { throw new RemoteException("", e); } finally { if (connector != null) connector.close(); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package remoteProxy.Client; import remoteProxy.Call; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.rmi.RemoteException; /** * Created by piqiu on 1/15/16. */ public class ProxyFactory { public static Object getProxy(final Class classType, final String host, final int port) { InvocationHandler handler = new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Connector connector = null; try { connector = new Connector(host, port); Call call = new Call(classType.getName(), method.getName(), method.getParameterTypes(), args); connector.send(call); call = (Call) connector.receive(); Object result = call.getResult(); if (result instanceof Throwable) throw new RemoteException("",(Throwable) result); // 把异常都转换为RemoteException else return result; } finally { if (connector != null) connector.close(); } } }; return Proxy.newProxyInstance(classType.getClassLoader(), new Class[]{classType}, handler); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package remoteProxy.Client; import remoteProxy.IHelloService; import java.rmi.RemoteException; /** * Created by piqiu on 1/15/16. */ public class SimpleClient { public static void main(String[] args) throws RemoteException { IHelloService helloService = new HelloServiceProxy("localhost", 8888); System.out.println(helloService.echo("hello")); System.out.println(helloService.getTime()); IHelloService helloService1 = (IHelloService)ProxyFactory.getProxy(IHelloService.class, "localhost", 8888); System.out.println(helloService1.echo("hello2")); System.out.println(helloService.getTime()); } } |
运行时候先运行服务端,再运行客户端。结果为:
服务端:
1 2 3 4 5 |
服务器启动...... HelloServiceImpl.echo: hello HelloServiceImpl.getTime: Fri Jan 15 14:32:52 CST 2016 HelloServiceImpl.echo: hello2 HelloServiceImpl.getTime: Fri Jan 15 14:32:52 CST 2016 |
客户端:
1 2 3 4 |
hello Fri Jan 15 14:32:52 CST 2016 hello2 Fri Jan 15 14:32:52 CST 2016 |
根据须要建立开销很大的对象。经过它来存放实例化须要很长时间的真实对象。
用来控制真是对象访问时的权限
当调用真实的对象时,代理处理另一些事。如计算真实对象的引用次数,这样当该对象没有引用时,能够自动释放它。或当第一次引用一个持久对象时,将它装入内存。或在访问一个实例对象前,检查是否已经锁定它,以确保其余对象不能改变它。他们都是经过代理在访问一个对象时附加一些内务处理。