時間:2017年08月28日星期一
說明:本文部份內容均來自慕課網。@慕課網:http://www.imooc.com
教學源碼:https://github.com/zccodere/s...
學習源碼:https://github.com/zccodere/s...java
學習本課程基礎node
面向對象的設計思惟 瞭解多態的概念 瞭解反射機制
課程目標git
代理模式基本概念及分類 瞭解代理模式開發中應用場景 掌握代理模式實現方式 理解JDK動態代理實現
代理模式定義github
爲其餘對象提供一種代理以控制對這個對象的訪問 代理對象起到中介做用,可去掉功能服務或增長額外的服務
常見的幾種代理模式apache
遠程代理:相似於客戶端服務器這種模式,列一個爲不一樣地理對象提供局域網表明對象 虛擬代理:根據須要將資源消耗很大的對象進行延遲,真正須要的時候進行建立 保護代理:控制對象的訪問權限 智能代理:提供對目標對象額外的服務
代理模式示意圖數組
智能引用代理服務器
靜態代理 動態代理
靜態代理定義dom
代理和被代理對象在代理以前是肯定的。他們都實現相同的接口或者繼承相同的抽象類
靜態代理類圖編輯器
代碼編寫ide
1.編寫Moveable接口
package com.myimooc.designpattern.c3proxy.car; /** * @describe 可行駛的接口 * @author zc * @version 1.0 2017-08-28 */ public interface Moveable { /** * 行駛的方法 */ void move(); }
2.編寫Car類
package com.myimooc.designpattern.c3proxy.car; import java.util.Random; /** * @describe 一輛車實現可行駛的接口 * @author zc * @version 1.0 2017-08-28 */ public class Car implements Moveable { @Override public void move() { // 記錄汽車行駛的時間 // long starttime = System.currentTimeMillis(); // System.out.println("汽車開始行駛..."); // 實現開車 try { System.out.println("汽車行駛中..."); Thread.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // long endtime = System.currentTimeMillis(); // System.out.println("汽車結束行駛...汽車行駛時間:"+(endtime - starttime) + "毫秒"); } }
3.編寫Car2類
package com.myimooc.designpattern.c3proxy.car; /** * @describe 繼承的方式實現靜態代理 * @author zc * @version 1.0 2017-08-28 */ public class Car2 extends Car { @Override public void move() { long starttime = System.currentTimeMillis(); System.out.println("汽車開始行駛..."); super.move(); long endtime = System.currentTimeMillis(); System.out.println("汽車結束行駛...汽車行駛時間:"+(endtime - starttime) + "毫秒"); } }
4.編寫Car3類
package com.myimooc.designpattern.c3proxy.car; /** * @describe 聚合的方式實現靜態代理 * @author zc * @version 1.0 2017-08-28 */ public class Car3 implements Moveable { public Car3(Car car) { super(); this.car = car; } private Car car; @Override public void move() { long starttime = System.currentTimeMillis(); System.out.println("汽車開始行駛..."); car.move(); long endtime = System.currentTimeMillis(); System.out.println("汽車結束行駛...汽車行駛時間:"+(endtime - starttime) + "毫秒"); } }
5.編寫Client類
package com.myimooc.designpattern.c3proxy.car; /** * @describe 測試類 * @author zc * @version 1.0 2017-08-28 */ public class Client { public static void main(String[] args) { // test1(); test2(); } // 2-2 聚合與繼承 代理功能疊加測試方法 public static void test2(){ Car car = new Car(); CarLogProxy clp = new CarLogProxy(car); CarTimeProxy ctp = new CarTimeProxy(clp); ctp.move(); } // 2-1 靜態代理測試方法 public static void test1(){ // Car car = new Car(); // car.move(); // 使用繼承的方式 // Moveable m = new Car2(); // m.move(); // 使用聚合方式 Car car = new Car(); Moveable m = new Car3(car); m.move(); } }
場景分析
代理類功能疊加
1.記錄日誌 2.記錄時間 3.權限功能
使用繼承方式
使用繼承方式來實現代理功能的疊加,代理類會無限的膨脹下去,因此這種方式不推薦使用。
使用聚合方式,經過代碼演示
代碼編寫
1.複製Car3命名爲CarTimeProxy
package com.myimooc.designpattern.c3proxy.car; /** * @describe 汽車行駛時間的代理 * @author zc * @version 1.0 2017-08-28 */ public class CarTimeProxy implements Moveable { // 由於代理類和被代理類都是實現相同的接口,因此構造方法傳遞的對象也能夠是Moveable對象 public CarTimeProxy(Moveable m) { super(); this.m = m; } private Moveable m; @Override public void move() { long starttime = System.currentTimeMillis(); System.out.println("汽車開始行駛..."); m.move(); long endtime = System.currentTimeMillis(); System.out.println("汽車結束行駛...汽車行駛時間:"+(endtime - starttime) + "毫秒"); } }
2.編寫CarLogProxy類
package com.myimooc.designpattern.c3proxy.car; /** * @describe 汽車日誌功能的代理 * @author zc * @version 1.0 2017-08-28 */ public class CarLogProxy implements Moveable { // 由於代理類和被代理類都是實現相同的接口,因此構造方法傳遞的對象也能夠是Moveable對象 public CarLogProxy(Moveable m) { super(); this.m = m; } private Moveable m; @Override public void move() { System.out.println("日誌開始"); m.move(); System.out.println("日誌結束"); } }
3.編寫Client類
package com.myimooc.designpattern.c3proxy.car; /** * @describe 測試類 * @author zc * @version 1.0 2017-08-28 */ public class Client { public static void main(String[] args) { // test1(); test2(); } // 2-2 聚合與繼承 代理功能疊加測試方法 public static void test2(){ Car car = new Car(); CarLogProxy clp = new CarLogProxy(car); CarTimeProxy ctp = new CarTimeProxy(clp); ctp.move(); } // 2-1 靜態代理測試方法 public static void test1(){ // Car car = new Car(); // car.move(); // 使用繼承的方式 // Moveable m = new Car2(); // m.move(); // 使用聚合方式 Car car = new Car(); Moveable m = new Car3(car); m.move(); } }
場景分析
有沒有方法動態產生代理,實現對不一樣類,不一樣方法的代理呢
JDK動態代理類圖
Java動態代理類位於java.lang.reflect包下,通常主要涉及到如下兩個類
Interface InvocationHandler:該接口中僅定義了一個方法 public Object invoke(Object obj,Method method,Object[] args) 在實際使用時,第一參數obj通常是指代理類,method是被代理的方法,args爲該方法的參數數組。 這個抽象方法在代理類中動態實現。 Proxy:該類即爲動態代理類 static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h) 返回代理類的一個實例,返回後的代理類能夠當作被代理類使用 (可以使用被代理類的在接口中聲明過的方法)
所謂Dynamic Proxy是這樣一種class
它是在運行時生成的class 該class須要實現一組interface 使用動態代理類時,必須實現InvocationHandler接口
動態代理實現步驟
1.建立一個實現InvocationHandler接口的類,它必須實現invoke方法 2.建立被代理的類以及接口 3.調用Proxy的靜態方法,建立一個代理類 newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h) 4.經過代理調用方法
代碼編寫
1.編寫TimeHandler類
package com.myimooc.designpattern.c3proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * @describe 對時間上的處理-使用JDK動態代理 * @author zc * @version 1.0 2017-08-28 */ public class TimeHandler implements InvocationHandler { public TimeHandler(Object target) { super(); this.target = target; } private Object target; /** * 參數: * proxy 被代理對象 * method 被代理對象方法 * args 方法的參數 * 返回值: * Object 方法的返回值 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long starttime = System.currentTimeMillis(); System.out.println("汽車開始行駛..."); method.invoke(target); long endtime = System.currentTimeMillis(); System.out.println("汽車結束行駛...汽車行駛時間:"+(endtime - starttime) + "毫秒"); return null; } }
2.編寫Test類
package com.myimooc.designpattern.c3proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import com.myimooc.designpattern.c3proxy.car.Car; import com.myimooc.designpattern.c3proxy.car.Moveable; /** * @describe JDK動態代理測試類 * @author zc * @version 1.0 2017-08-28 */ public class Test { public static void main(String[] args) { Car car = new Car(); InvocationHandler h = new TimeHandler(car); Class<?> cls = car.getClass(); // 使用Proxy類newProxyInstance方法動態建立代理類 /** * loader 類加載器 * interfaces 實現接口 * h InvocationHandler */ Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h); m.move(); } }
JDK動態代理與CGLIB動態代理區別
JDK動態代理 1.只能代理實現了接口的類 2.沒有實現接口的類不能實現JDK的動態代理 CGLIB動態代理 1.針對類來實現代理的 2.對指定目標類產生一個子類,經過方法攔截技術攔截全部父類方法的調用 3.由於是使用繼承的方式,因此不能對final修飾的類來進行代理
代碼編寫
1.添加相關依賴
<!-- https://mvnrepository.com/artifact/cglib/cglib-nodep --> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.2.5</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency>
2.編寫CglibProxy類
package com.myimooc.designpattern.c3proxy.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * @describe 代理類 * @author zc * @version 1.0 2017-08-28 */ public class CglibProxy implements MethodInterceptor { private Enhancer enhance = new Enhancer(); @SuppressWarnings("rawtypes") public Object getProxy(Class clazz){ // 設置建立子類的類 enhance.setSuperclass(clazz); enhance.setCallback(this); return enhance.create(); } /** * 攔截全部目標類方法的調用 * 參數: * obj 目標類的實例 * method 目標方法的反射對象 * args 方法的參數 * proxy 代理類的實例 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("日誌開始..."); // 代理類調用父類的方法 proxy.invokeSuper(obj, args); System.out.println("日誌結束..."); return null; } }
3.編寫Train類
package com.myimooc.designpattern.c3proxy.cglib; /** * @describe 火車 * @author zc * @version 1.0 2017-08-28 */ public class Train { public void move(){ System.out.println("火車行駛中"); } }
4.編寫Client類
package com.myimooc.designpattern.c3proxy.cglib; /** * @describe cglib代理測試類 * @author zc * @version 1.0 2017-08-28 */ public class Client { public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); Train t = (Train)proxy.getProxy(Train.class); t.move(); } }
動態代理實現思路
實現功能:經過Proxy的newProxyInstance返回代理對象 1.聲明一段源碼(動態產生代理) 2.編譯編碼(JDK Compiler API),產生新的類(代理類) 3.將這個類load到內存當中,產生一個新的對象(代理對象) 4.return 代理對象
代碼編寫
1.編寫InvocationHandler類
package com.myimooc.designpattern.c3proxy.simulationjdk; import java.lang.reflect.Method; /** * @describe 模擬JDK動態代理-業務處理類 * @author zc * @version 1.0 2017-08-28 */ public interface InvocationHandler { public void invoke(Object obj,Method method); }
2.編寫Proxy類
package com.myimooc.designpattern.c3proxy.simulationjdk; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import org.apache.commons.io.FileUtils; /** * @describe 模擬JDK動態代理-代理類 * @author zc * @version 1.0 2017-08-28 */ @SuppressWarnings({"rawtypes","unchecked"}) public class Proxy { public static Object newProxyInstance( Class infce,InvocationHandler h)throws Exception{ String rt = "\r\n"; String methodStr = ""; for(Method m : infce.getMethods()){ methodStr += " @Override" + rt + " public void "+ m.getName() +"(){" + rt + " try{" + rt + " Method md = " +infce.getName()+".class.getMethod(\""+m.getName()+"\");" + rt + " h.invoke(this,md);" + rt + " }catch(Exception e){e.printStackTrace();}" + rt + " }" + rt; } String str = "package com.myimooc.designpattern.c3proxy.simulationjdk; " + rt + "import com.myimooc.designpattern.c3proxy.simulationjdk.InvocationHandler;" + rt + "import java.lang.reflect.Method;" + rt + "public class $Proxy0 implements "+ infce.getName() +" { " + rt + " public $Proxy0(InvocationHandler h) {" + rt + " this.h = h;" + rt + " }" + rt + " private InvocationHandler h;" + rt + methodStr + rt + "}"; // 產生代理類的java文件 String filename = System.getProperty("user.dir") + "/target/classes/com/myimooc/designpattern/c3proxy/simulationjdk/$Proxy0.java"; File file = new File(filename); FileUtils.writeStringToFile(file, str,"UTF-8"); // 編譯-拿到編輯器 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 文件管理者 StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); // 獲取文件 Iterable units = fileMgr.getJavaFileObjects(filename); // 編譯任務 CompilationTask task = compiler.getTask(null, fileMgr, null, null, null, units); // 進行編譯 task.call(); fileMgr.close(); // load到內存 ClassLoader cl = ClassLoader.getSystemClassLoader(); Class c = cl.loadClass("com.myimooc.designpattern.c3proxy.simulationjdk.$Proxy0"); Constructor ctr = c.getConstructor(InvocationHandler.class); return ctr.newInstance(h); } }
3.編寫TimeHandler類
package com.myimooc.designpattern.c3proxy.simulationjdk; import java.lang.reflect.Method; /** * @describe 模擬JDK動態代理-時間業務邏輯處理 * @author zc * @version 1.0 2017-08-28 */ public class TimeHandler implements InvocationHandler{ private Object target; public TimeHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object obj, Method method) { try { long starttime = System.currentTimeMillis(); System.out.println("汽車開始行駛..."); method.invoke(target); long endtime = System.currentTimeMillis(); System.out.println("汽車結束行駛...汽車行駛時間:"+(endtime - starttime) + "毫秒"); } catch (Exception e) { e.printStackTrace(); } } }
4.編寫Client類
package com.myimooc.designpattern.c3proxy.simulationjdk; import com.myimooc.designpattern.c3proxy.car.Car; import com.myimooc.designpattern.c3proxy.car.Moveable; /** * @describe 模擬JDK動態代理-測試類 * @author zc * @version 1.0 2017-08-28 */ public class Client { public static void main(String[] args) throws Exception { Car car = new Car(); InvocationHandler h = new TimeHandler(car); Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class,h); m.move(); } }
總結
代理模式概念、分類及應用場景 場景代理模式 靜態代理(繼承、聚合) JDK動態代理實現日誌處理功能 模擬JDK動態代理實現
代理模式-動態代理