代理模式简单说就是对当前已有类中的方法进行前/后置干预的的一种设计模式,目的是在不对现有业务类进行修改的状况下对指定的现有业务在类级别或方法级别进行修改干预。java
实现方式目前有两种,一种是静态代理,纯粹基于设计模式经过代码实现。另外一种是动态代理,须要经过JDK默认提供的功能和导入CGLIG功能包来加强实现。node
首先进行静态代理的实现。spring
package proxy.staticproxy;设计模式
import java.util.List;数组
import bean.PickDoc;
import bean.PickList;
import bean.PickTask;
import builder.IPickTask;springboot
静态代理的具体实现
public class StaticPickTaskProxy implements IPickTask {app
//被代理对象 private IPickTask pickTask; public StaticPickTaskProxy(IPickTask pickTask) { this.pickTask = pickTask; } @Override public List<PickList> getPickList(List<PickDoc> list) { System.out.println("前置处理"+pickTask.getClass().getName()+"对象调用前的操做"); pickTask.getPickList(list); System.out.println("后置处理"+pickTask.getClass().getName()+"对象调用前的操做"); return null; } @Override public List<PickTask> getPickTask(List<PickList> list) { // TODO Auto-generated method stub return null; }
}ide
//静态代理的实际调用
package proxy.staticproxy;测试
import java.util.ArrayList;
import java.util.List;ui
import bean.PickDoc;
import builder.IPickTask;
import builder.SinglePickTask;
import builder.UnionPickTask;
public class StaticPickTaskProxyMain {
public static void main (String[] args) { //使用代理类代替具体的业务类来进行操做 IPickTask pickTaskProxy = new StaticPickTaskProxy(new SinglePickTask()); //使用代理类代替具体的业务类来进行操做 IPickTask pickTaskProxy1 = new StaticPickTaskProxy(new UnionPickTask()); List<PickDoc> pickDocList = new ArrayList<PickDoc>(); //非合并拣货 pickTaskProxy.getPickList(pickDocList); //合并拣货 pickTaskProxy1.getPickList(pickDocList); }
}
静态代理比较简单,比较容易明白。这里要另外说的是若是前期对要开发的业务设计的好,那么能够必定程度上下降开码的开发量,同时提升可维护性。
好比一般描述静态代理是只能针对某个具体的类中的一个或多个方法来手工实现代理。但由于示例的两个业务实现类SinglePickTask和UnionPickTask都面向IPickTask接口进行实现(采用建造者模式)。这样使得我一个静态代理类能够对这一组业务实现类进行代理。 这就是现实中的好处
动态代理中的JDK默认实现
package proxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicJDKProxy implements InvocationHandler {
//被代理对象 private Object obj ; public DynamicJDKProxy(Object obj) { this.obj = obj; } //当经过代理类的对象发起对被重写的方法调用时,都会转化为对以下invoke方法的调用 @Override /** * proxy 代理类对象,要实现被代理方法的那个对象,而不是被代类对象,这点不要搞错 * proxy - the proxy instance that the method was invoked on * * 参数method是一个实例,它就是调用在代理实例上的接口方法。声明的 * 方法对象类是该方法声明的接口,这个接口是全部继承当前method的代理接口的父接口 * method - the Method instance corresponding to the interface method * invoked on the proxy instance.The declaringclass of the Method * object will be the interface that the method was declared in, which * may be a superinterface of theproxy interface that the proxy * class inherits the method through. * * 参数args是包含了代理方法调用中传输的对象数组参数。或者这个接口没有参数。 * 原始类型的参数被打包在合适的包装类中,如Integer或者Boolean. * args - an array of objects containing the values of thearguments * passed in the method invocation on the proxy instance,or null if * interface method takes no arguments.Arguments of primitive types * are wrapped in instances of the appropriate primitive wrapper class, * such as java.lang.Integer or java.lang.Boolean. */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("当前代理对象名称:"+obj.getClass().getSimpleName()); System.out.println("当前代理方法名称:"+method.getName()); if(args!=null) { for(Object obj: args) { System.out.println("参数对象:"+obj.getClass().getSimpleName()); } } Object returnObj =null; if(method.getName().equals("getPickList")) { System.out.println("前置处理"); returnObj = method.invoke(obj, args); System.out.println("后置处理"); } return returnObj; }
}
//实现调用测试代码
package proxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import bean.PickDoc;
import builder.IPickTask;
import builder.SinglePickTask;
import builder.UnionPickTask;
public class DynamicJDKProxyMain {
public static void main(String[] arg) { IPickTask singlePickTask = new SinglePickTask(); IPickTask unionPickTask = new UnionPickTask(); //定义拣货单据列表 List<PickDoc> pickDocList = new ArrayList<PickDoc>(); //要代理的对象 InvocationHandler singlePickTaskHandler = new DynamicJDKProxy(singlePickTask); InvocationHandler unionPickTaskHandler = new DynamicJDKProxy(unionPickTask); //获取类加载器 ClassLoader loader = singlePickTask.getClass().getClassLoader(); //获取类接口对象列表 Class[] interfaces = singlePickTask.getClass().getInterfaces(); /* * classloader,要代理哪一个类就用哪一个类的加载器来加载要新建立的代理类 * interfaces,代理类要实现哪些接口,与被代理类对象的一致 * handler, 方法分发调用的处理器 * loader - the class loader to define the proxy class * interfaces - the list of interfaces for the proxy classto implement * h - the invocation handler to dispatch method invocations to * 这里是对要被代理的对象动态生成一个新的代理类, * 因此作为一个新类,它须要有相应的类加载器,同时这个类是经过反射来构造的, * 因此它构造时的方法列表就来自于被被代理对象 */ IPickTask proxyPickTask = (IPickTask) Proxy.newProxyInstance(loader, interfaces, singlePickTaskHandler); proxyPickTask.getPickList(pickDocList); }
}
//运行结果
这里的注释已经比较多,应该比较容易理解。总之就是JDK自动构建了一个与静态代理实现方式同样的代理类,来代理当前的业务类。与静态代理相比的好处是不用必定要手工实现业务类对应接口的全部方法,尤为是在对基于多个接口实现的不一样业务类的代理的时候比较好。
CGLIB动态代理实现
首先,这个不是JDK默认带有的功能,须要单独下载JAR包或者运行在springboot的工程下,引入springboot工程中自带的相应实现。本例引用的是cglib-nodep-2.2.2.jar
代理类代码以下
package proxy.dynamicproxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class DynamicCGLibProxy implements MethodInterceptor {
private Object target; public DynamicCGLibProxy(Object target) { this.target = target; } public Object newInstance() { //在这里对被代理对象加强生成一个代理对象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("当前代理对象名称:"+obj.getClass().getSimpleName()); System.out.println("当前代理方法名称:"+method.getName()); if(args!=null) { for(Object arg: args) { System.out.println("参数对象:"+arg.getClass().getSimpleName()); } } Object returnObj =null; if(method.getName().equals("getPickList")) { System.out.println("前置处理"); returnObj = proxy.invokeSuper(obj, args); System.out.println("后置处理"); } return returnObj; }
}
调用演示代码以下
package proxy.dynamicproxy;
import java.util.ArrayList;
import java.util.List;
import bean.PickDoc;
import builder.IPickTask;
import builder.SinglePickTask;
public class DynamicCGLibProxyMain {
public static void main(String[] args) { List<PickDoc> pickDocList = new ArrayList<PickDoc>(); //SinglePickTask 实际业务类 //pickTaskProxy 生成的代理类象 IPickTask pickTaskProxy=(SinglePickTask)new DynamicCGLibProxy(new SinglePickTask()).newInstance(); pickTaskProxy.getPickList(pickDocList); }
}
//调用效果
CGLIB代理实现本质上和jdk是同样的,都是新生成一个代理类。区别是二者生成代理类中的方法来源不一样。JDK基于取到的对象接口列表在反射时生成相应的方法。而CGLIB经过对当前要被代理的对象生成一个子类对象来解决这个问题
spring中优先使用JDK自带的代理实现方式,当业务方法不基于接口实现时才使用CGLIB方式
另有同窗补充指正 spring boot2.x默认使用cglib作动态代理了
另外,在3种实现中均采用经过构造方法传入须要被代理的对象,我的认为这是个比较好的处理方式