代理模式簡單說就是對當前已有類中的方法進行前/後置干預的的一種設計模式,目的是在不對現有業務類進行修改的狀況下對指定的現有業務在類級別或方法級別進行修改干預。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種實現中均採用經過構造方法傳入須要被代理的對象,我的認爲這是個比較好的處理方式