1. 程式人生 > 其它 >05-07-設計模式 代理模式

05-07-設計模式 代理模式

基本介紹

  1. 代理模式:為一個物件提供一個替身,以控制對這個物件的訪問。即通過代理物件訪問目標物件.這樣做的好處是:可以在目標物件實現的基礎上,增強額外的功能操作,即擴充套件目標物件的功能。
  2. 被代理的物件可以是遠端物件、建立開銷大的物件或需要安全控制的物件
  3. 代理模式有不同的形式,主要有三種靜態代理、動態代理(JDK代理、介面代理)和Cglib代理(可以在記憶體動態的建立物件,而不需要實現介面,他是屬於動態代理的範疇)。
  4. 代理模式示意圖

靜態代理

基本介紹

靜態代理在使用時,需要定義介面或者父類,被代理物件(即目標物件)與代理物件一起實現相同的介面或者是繼承相同父類

應用例項

具體要求

  1. 定義一個介面: ITeacherDao
  2. 目標物件TeacherDao實現ITeacherDao
  3. 使用靜態代理方式, 就需要在代理物件TeacherDaoProxy中也實現ITeacherDao
  4. 呼叫的時候通過代理物件 TeacherDaoProxy的方法來呼叫目標物件
  5. 特別提醒: 代理物件與目標物件要實現相同的介面, 然後通過呼叫相同的方法來呼叫目標物件的方法

類圖

程式碼實現

package com.flower.proxy;

/**
 * 靜態代理
 */
public class StaticProxy {
    public static void main(String[] args) {
        new TeacherDaoProxy(new TeacherDao()).teach();
    }
}

/**
 * 介面
 */
interface ITeacher{
    void teach();
}

/**
 * 實現類
 */
class TeacherDao implements ITeacher{
    @Override
    public void teach() {
        System.out.println("老師授課");
    }
}

/**
 * 代理物件
 */
class TeacherDaoProxy implements ITeacher{

    /**
     * 要代理的目標物件
     */
    private ITeacher target;

    public TeacherDaoProxy(ITeacher target) {
        this.target = target;
    }

    /**
     * 看到這裡就可以想到Spring的切面了吧, 沒錯就是代理模式, 前置增強, 後置增強等
     */
    @Override
    public void teach() {
        System.out.println("開始代理..");
        target.teach();
        System.out.println("代理結束");
    }
}

靜態代理的優缺點

  • 優點:
    • 在不修改目標物件的功能前提下,能通過代理物件對目標功能擴充套件
  • 缺點:
    • 因為代理物件需要與目標物件實現一樣的介面,所以會有很多代理類
    • 一旦介面增加方法,目標物件與代理物件都要維護

動態代理

基本介紹

  1. 代理物件,不需要實現介面,但是目標物件要實現介面,否則不能用動態代理
  2. 代理物件的生成,是利用JDK的API,動態的在記憶體中構建代理物件
  3. 動態代理也叫做:JDK代理、介面代理

JDK 動態代理API

  1. 代理類所在包:java.lang.reflect.Proxy
  2. JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個引數,完整的寫法是:staticObjectnewProxyInstance(ClassLoaderloader,Class<?>[]interfaces,InvocationHandlerh)

應用例項

將前面的靜態代理改成動態代理

類圖

程式碼實現

package com.flower.proxy.jdk.dynamic;

import java.lang.reflect.Proxy;

public class JdkDynamicProxy {
    public static void main(String[] args) {
        ITeacher iTeacher = (ITeacher) new TeacherProxyFactory(new Teacher()).getProxyInstance();
        iTeacher.teach();
    }
}
interface ITeacher{
    void teach();
}
class Teacher implements ITeacher{
    @Override
    public void teach() {
        System.out.println("老師授課");
    }
}
class TeacherProxyFactory {

    // 目標物件
    private Object target;

    public TeacherProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * @return 代理物件
     */
    public Object getProxyInstance(){
        /*
         * 類載入器
         * 目標物件介面陣列
         * 增強類
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),((proxy, method, args) -> {
            System.out.println("開始代理");
            Object invoke = method.invoke(target, args);
            System.out.println("代理結束");
            return invoke;
        }));
    }
}

Cglib動態代理

基本介紹

  1. 靜態代理和JDK代理模式都要求目標物件是實現一個介面,但是有時候目標物件只是一個單獨的物件,並沒有實現任何的介面,這個時候可使用目標物件子類來實現代理-這就是Cglib代理
  2. Cglib代理也叫作子類代理,它是在記憶體中構建一個子類物件從而實現對目標物件功能擴充套件,有些書也將Cglib代理歸屬到動態代理。
  3. Cglib是一個強大的高效能的程式碼生成包,它可以在執行期擴充套件java類與實現java介面.它廣泛的被許多AOP的框架使用,例如SpringAOP,實現方法攔截
  4. 在AOP程式設計中如何選擇代理模式:
    1. 1.目標物件需要實現介面,用JDK代理
    2. 2.目標物件不需要實現介面,用Cglib代理
  1. Cglib包的底層是通過使用位元組碼處理框架ASM來轉換位元組碼並生成新的類

Cglib代理模式實現步驟

引入Jar檔案, 我是Maven專案直接引入座標

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>

在記憶體中動態構建子類,注意代理的類不能為final,否則報錯

目標物件的方法如果為final/static,那麼就不會被攔截,即不會執行目標物件額外的業務方法.

應用例項

將前面的靜態代理,改為CGLIB代理

類圖

程式碼實現

package com.flower.proxy.cglib.dynamic;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

public class CglibDynamicProxy {
    public static void main(String[] args) {
        TeacherDao teacherDao = (TeacherDao) new CglibProxyFactory(new TeacherDao()).getNewInstance();
        teacherDao.teach();
    }
}
class TeacherDao {
    public void teach(){
        System.out.println("老師授課");
    }
}
class CglibProxyFactory{

    private Object target;

    public CglibProxyFactory(Object target) {
        this.target = target;
    }

    public Object getNewInstance(){
        // 工具類
        Enhancer enhancer = new Enhancer();
        // 設定代理類的父類為目標類
        enhancer.setSuperclass(target.getClass());
        // 設定回撥函式
        enhancer.setCallback((MethodInterceptor) (o, method, args, methodProxy) -> {
            System.out.println("開始代理");
            Object invoke = method.invoke(target, args);
            System.out.println("代理結束");
            return invoke;
        });
        return enhancer.create();
    }

}

幾種特殊的代理模式

模式

介紹

防火牆代理

內網通過代理穿透防火牆

快取代理

比如:當請求圖片檔案等資源時, 先從快取代理取, 如果取到資源ok,如果取不到資源,再到公網或者資料庫取,然後快取

遠端代理

遠端物件的本地代表,通過它可以把遠端物件當本地物件呼叫,遠端代理通過網路和真正的遠端物件溝通訊息(我咋感覺Fegin客戶端就是這種呢)

同步代理

主要使用在多執行緒程式設計中, 完成多執行緒間同步工作