慕課網_《模式的祕密之代理模式》學習總結

時間:2017年08月28日星期一
說明:本文部份內容均來自慕課網。@慕課網:http://www.imooc.com
教學源碼:https://github.com/zccodere/s...
學習源碼:https://github.com/zccodere/s...java

第一章:代理模式

1-1 概念介紹

學習本課程基礎node

面向對象的設計思惟
瞭解多態的概念
瞭解反射機制

課程目標git

代理模式基本概念及分類
瞭解代理模式開發中應用場景
掌握代理模式實現方式
理解JDK動態代理實現

代理模式定義github

爲其餘對象提供一種代理以控制對這個對象的訪問
代理對象起到中介做用,可去掉功能服務或增長額外的服務

常見的幾種代理模式apache

遠程代理:相似於客戶端服務器這種模式,列一個爲不一樣地理對象提供局域網表明對象
虛擬代理:根據須要將資源消耗很大的對象進行延遲,真正須要的時候進行建立
保護代理:控制對象的訪問權限
智能代理:提供對目標對象額外的服務

代理模式示意圖數組

clipboard.png

第二章:經常使用代理模式

2-1 靜態代理

智能引用代理服務器

靜態代理
動態代理

靜態代理定義dom

代理和被代理對象在代理以前是肯定的。他們都實現相同的接口或者繼承相同的抽象類

靜態代理類圖編輯器

clipboard.png

代碼編寫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();
    }
    
}

2-2 聚合與繼承

場景分析

clipboard.png

代理類功能疊加

1.記錄日誌
2.記錄時間
3.權限功能

使用繼承方式

clipboard.png

使用繼承方式來實現代理功能的疊加,代理類會無限的膨脹下去,因此這種方式不推薦使用。

使用聚合方式,經過代碼演示

代碼編寫

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();
    }
    
}

2-3 JDK動態代理

場景分析

clipboard.png

有沒有方法動態產生代理,實現對不一樣類,不一樣方法的代理呢

JDK動態代理類圖

clipboard.png

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();
        
    }
    
}

2-4 使用cglib

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();
    }
}

第三章:模擬JDK動態代理

3-1 實現動態代理

動態代理實現思路

實現功能:經過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();
    }
}

第四章:總結

4-1 課程總結

總結

代理模式概念、分類及應用場景
場景代理模式
靜態代理(繼承、聚合)
JDK動態代理實現日誌處理功能
模擬JDK動態代理實現

代理模式-動態代理

clipboard.png

clipboard.png

相關文章
相關標籤/搜索