代理模式
Posted wuyx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理模式相关的知识,希望对你有一定的参考价值。
在开发过程中,尤其现在注解开发过程中代理其实运用的非常广泛,它可以让我们可以更关注业务,其他的比如日志,事务,以及aop这些公共的东西交给代理类去做。本文就来介绍代理设计模式。
一、代理模式概念
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。(来自百度百科的介绍)
我觉得,代理就是让我们可以单纯的去关注业务,而把公共的方法交给代理类去处理,如果业务发生变化,而像一些公共的方法是不需要发生变化,我们只需要去修改业务方法就可以。
二、代理模式的优缺点
- 优点
(1) .责任清晰,真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
(2) .代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
(3) .高扩展性 - 缺点
(1) .静态代理,如果代理对象多的话,则会增加代理类,加重了开发人员的工作量。
(2) .静态代理由于需要和目标对象实现相同的接口,一旦更改了接口,那么目标对象和代理对象都要同时做出调整,不方便管理。
三、代理模式的组成
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
四、静态代理
下面写一个静态代理的例子:
- 首先需要一个抽象角色(用于定义真实角色的动作)
/** * * @escription:此接口为代理模式的"抽象角色", 其中定义了真实角色与代理角色共同需要做的事情。 * 此接口也可以定义为抽象类。 * @author: Herrt灬凌夜 * @date: 2018年11月24日 下午12:24:24 */ public interface AbstractRole { /** * * @Title: businessMethod * @Description: 此方法为真实角色和代理角色需要实现的业务方法 * @param: * @return: void * @throws */ public void businessMethod(); }
- 然后是真实角色(来实现抽象角色的动作)
/** * @escription:(真实角色) * @author: Herrt灬凌夜 * @date: 2018年11月24日 下午12:47:07 */ public class RealRole implements AbstractRole { /** * <p>Title: businessMethod</p> * <p>Description: 真实角色所以执行的方法,只需要注重业务</p> * @see proxy.AbstractRole#businessMethod() */ public void businessMethod() { System.out.println("业务方法!"); } }
- 接下来是代理角色(来代理真实角色的动作,并且加上自己的一些动作)
/** * @escription:(代理角色) * @author: Herrt灬凌夜 * @date: 2018年11月24日 下午12:46:35 */ public class ProxyRole implements AbstractRole{ private RealRole realRole; /** * * @Title:ProxyRole * @Description:通过构造器初始化真实角色 * @param:@param realRole * @throws */ public ProxyRole(RealRole realRole) { super(); this.realRole = realRole; } /** * <p>Title: businessMethod</p> * <p>Description: 代理角色执行的方法,在这里处理非业务,并且公共的东西,比如日志,事务等等。</p> * @see proxy.AbstractRole#businessMethod() */ public void businessMethod() { prior(); realRole.businessMethod(); after(); } /** * * @Title: prior * @Description: 在businessMethod方法执行之前执行 * @param: * @return: void * @throws */ private void prior() { System.out.println("在真实角色执行方法之前执行"); } /** * * @Title: after * @Description: 在businessMethod方法执行之后执行 * @param: * @return: void * @throws */ private void after() { System.out.println("在真实角色执行方法之后执行"); } }
- 最后是使用者
/** * @escription:使用者,调用真实角色的方法 * @author: Herrt灬凌夜 * @date: 2018年11月24日 下午12:48:16 */ public class Client { public static void main(String[] args) { RealRole realRole = new RealRole(); ProxyRole proxyRole = new ProxyRole(realRole); proxyRole.businessMethod(); } }
执行结果如下:
五、基于接口动态代理(jdk动态代理)
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
- 首先还是创建抽象角色和真实角色,此处使用的还是上例中的AbstractRole和RealRole
- 创建动态代理类
1 /** 2 * @escription:log的动态代理类 3 * @author: Herrt灬凌夜 4 * @date: 2018年11月24日 下午2:44:26 5 */ 6 public class LogDynamicProxy implements InvocationHandler{ 7 8 private Object target; 9 10 /** 11 * @Title:LogDynamicProxy 12 * @Description: 将代理对象通过构造器传入 13 * @param:@param target 14 * @throws 15 */ 16 public LogDynamicProxy(Object target) { 17 super(); 18 this.target = target; 19 } 20 21 22 /** 23 * 获取代理类 24 * @return the target 25 */ 26 public Object getTarget() { 27 return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); 28 } 29 30 /** 31 * <p>Title: invoke</p> 32 * <p>Description: </p> 33 * @param proxy : 代理类 34 * @param method : 代理类的 调用处理程序的方法对象 35 * @param args : 参数 36 * @return 37 * @throws Throwable 38 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 39 */ 40 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 41 log(method.getName()); 42 return method.invoke(target, args); 43 } 44 45 private void log (String method) { 46 System.out.println("执行" + method + "方法"); 47 } 48 49 }
- 创建使用者
1 /** 2 * @escription:使用者,调用真实角色的方法 3 * @author: Herrt灬凌夜 4 * @date: 2018年11月24日 下午12:48:16 5 */ 6 public class Client { 7 8 public static void main(String[] args) { 9 RealRole realRole = new RealRole(); 10 LogDynamicProxy logProxy = new LogDynamicProxy(realRole); 11 AbstractRole abstractRole = (AbstractRole)logProxy.getTarget(); 12 abstractRole.businessMethod(); 13 } 14 }
下面对上述代码进行简单分析,首相我们在创建LogDynamicProxy时,调用其构造器,将真实角色传入在LogDynamicProxy的get方法中调用了Proxy类的newProxyInstance方法,这个方法是用于动态创建代理类的,其中有三个参数,第一个参数是代理类的类加载器ClassLoader,第二个是真实角色的接口数组,第三参数是代理类。我们简单看下其源码,就可以很容易的看到使用反射去创建代理类。
1 @CallerSensitive 2 public static Object newProxyInstance(ClassLoader loader, 3 Class<?>[] interfaces, 4 InvocationHandler h)throws IllegalArgumentException 5 { 6 //判断代理类是否为null 7 Objects.requireNonNull(h); 8 //克隆接口 9 final Class<?>[] intfs = interfaces.clone(); 10 //权限检查 11 final SecurityManager sm = System.getSecurityManager(); 12 if (sm != null) { 13 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 14 } 15 16 /* 17 * Look up or generate the designated proxy class. 18 * 生成代理类,如果有直接从缓存中取,如果没有则生成 19 */ 20 Class<?> cl = getProxyClass0(loader, intfs); 21 22 /* 23 * Invoke its constructor with the designated invocation handler. 24 */ 25 try { 26 if (sm != null) { 27 checkNewProxyPermission(Reflection.getCallerClass(), cl); 28 } 29 //动态获取构造 30 final Constructor<?> cons = cl.getConstructor(constructorParams); 31 final InvocationHandler ih = h; 32 if (!Modifier.isPublic(cl.getModifiers())) { 33 AccessController.doPrivileged(new PrivilegedAction<Void>() { 34 public Void run() { 35 cons.setAccessible(true); 36 return null; 37 } 38 }); 39 } 40 //返回代理对象 41 return cons.newInstance(new Object[]{h}); 42 } catch (IllegalAccessException|InstantiationException e) { 43 throw new InternalError(e.toString(), e); 44 } catch (InvocationTargetException e) { 45 Throwable t = e.getCause(); 46 if (t instanceof RuntimeException) { 47 throw (RuntimeException) t; 48 } else { 49 throw new InternalError(t.toString(), t); 50 } 51 } catch (NoSuchMethodException e) { 52 throw new InternalError(e.toString(), e); 53 } 54 }
最后使用代理类去调用真实角色的方法,在调用时其实是调用的代理类的invoke方法,而这个invoke方法是重写了InvocationHandler接口的方法,这个方法有3个参数,第一个为代理类,第二个则为方法,第三为参数,在这个方法中我们动态的调用真实角色的方法,并且在其中加入一些log。
所以说每个动态代理类,都可以去代理一类的业务,这样可以减轻程序员的代码量,而且可以更好的管理代理类。
六、基于类动态代理(cglib动态代理)
上面的jdk动态代理是实现接口的方式去实现动态代理,而接下来说的则是继承类去实现动态代理。
要想使用cglib的动态代理,首先,我们需要去导一个cglib-nodep的jar包,下面是maven的坐标
<!-- https://mvnrepository.com/artifact/cglib/cglib-nodep --> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.1_3</version> </dependency>
- 首先需要创建真实角色,这个还是使用上例中的RealRole类
- 创建cglib代理类
1 /** 2 * @escription:cglig代理类 3 * @author: Herrt灬凌夜 4 * @date: 2018年11月25日 下午12:29:14 5 */ 6 public class LogCglibProxy implements MethodInterceptor { 7 8 /** 9 * <p>Title: intercept</p> 10 * <p>Description: 在Client中调用通过动态代理获取到的真实对象调用方法时,其实调用的是此方法</p> 11 * @param obj 12 * @param method 13 * @param args 14 * @param proxy 15 * @return 16 * @throws Throwable 17 * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy) 18 */ 19 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 20 log(method.getName()); 21 return proxy.invokeSuper(obj, args); //动态调用真实角色的方法 22 } 23 24 /** 25 * @Title: getInstance 26 * @Description: 动态获取代理对象 27 * @param: @param target 真实对象 28 * @return 代理对象 29 * @throws 30 */ 31 public Object getInstance (Object target) { 32 Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类 33 enhancer.setSuperclass(target.getClass()); //给加强器指定父类,即真实角色 34 enhancer.setCallback(this); //设置回调,调用上面的 intercept 方法 35 return enhancer.create();//动态创建代理类,并返回 36 } 37 38 private void log (String method) { 39 System.out.println("执行" + method + "方法"); 40 } 41 }
- 创建使用者
1 /** 2 * @escription:使用者,调用真实角色的方法 3 * @author: Herrt灬凌夜 4 * @date: 2018年11月24日 下午12:48:16 5 */ 6 public class Client { 7 8 public static void main(String[] args) { 9 RealRole realRole = new RealRole(); 10 LogCglibProxy cglibProxy = new LogCglibProxy(); 11 RealRole cglibRealRole = (RealRole)cglibProxy.getInstance(realRole); 12 cglibRealRole.businessMethod(); 13 } 14 }
对上面代码进行简单分析,首先创建真实角色,代理类,调用其getInstance方法,动态创建代理类,其中代码的含义已经在代码中备注,所以在此不再赘述。而在使用代理类调用真实角色方法时,其实在调用intercept方法,这个方法是重写了MethodInterceptor 接口的方法,可以在其中加入log的一些方法。
-------------------- END ---------------------
最后附上作者的微信公众号地址和博客地址
公众号:wuyouxin_gzh
Herrt灬凌夜:https://www.cnblogs.com/wuyx/
以上是关于代理模式的主要内容,如果未能解决你的问题,请参考以下文章