大型Java進階專題(六)設計模式之代理模式

代理模式

前言

又開始個人專題了,又停滯了一段時間了,加油繼續吧。都知道 SpringAOP 是用代理模式實現,究竟是怎麼實現的?咱們來一探究竟,而且本身仿真手寫還原部分細節。

代理模式的應用

在生活中,咱們常常見到這樣的場景,如:租房中介、售票黃牛、婚介、經紀人、快遞、 事務代理、非侵入式日誌監聽等,這些都是代理模式的實際體現。代理模式(Proxy Pattern)的定義也很是簡單,是指爲其餘對象提供一種代理,以控制對這個對象的訪問。 代理對象在客服端和目標對象之間起到中介做用,代理模式屬於結構型設計模式。使用 代理模式主要有兩個目的:一保護目標對象,二加強目標象。下面咱們來看一下代理 模式的類結構圖:

Subject 是頂層接口,RealSubject 是真實對象(被代理對象),Proxy 是代理對象,代 理對象持有被代理對象的引用,客戶端調用代理對象方法,同時也調用被代理對象的方 法,可是在代理對象先後增長一些處理。在代碼中,咱們想到代理,就會理解爲是代碼 加強,其實就是在本來邏輯先後增長一些邏輯,而調用者無感知。代理模式屬於結構型 模式,有靜態代理和動態代理。

靜態代理

舉個例子:人到了適婚年齡,父母老是火燒眉毛但願早點抱孫子。而如今社會的人在各 種壓力之下,都選擇晚婚晚育。因而着急的父母就開始處處爲本身的子女相親,比子女 本身還着急。這個相親的過程,就是一種咱們人人都有份的代理。來看代碼實現: 

/**
 * 人不少行爲,要談戀愛
 */
public interface Person {
    void findLove();
}


/**
 * 兒子須要找對象
 */
public class Son implements Person {

    @Override
    public void findLove() {
        System.out.println("工做沒時間!");
    }
}

/**
 * 父親代理兒子 先幫物色對象
 */
public class Father{

    private Son son;

    //代理對象持有 被代理對象的應用 但沒辦法擴展
    private Father(Son son) {
        this.son = son;
    }

    private void findLove() {
        //before
        System.out.println("父母幫物色對象");
        son.findLove();
        //after
        System.out.println("雙方贊成交往!");
    }

    //測試代碼
    public static void main(String[] args) {
        Son son = new Son();
        Father father = new Father(son);
        father.findLove();
    }
}

動態代理

動態代理和靜態對比基本思路是一致的,只不過動態代理功能更增強大,隨着業務的擴 展適應性更強。若是還以找對象爲例,使用動態代理至關因而可以適應複雜的業務場景。 不只僅只是父親給兒子找對象,若是找對象這項業務發展成了一個產業,進而出現了媒 婆、婚介所等這樣的形式。那麼,此時用靜態代理成本就更大了,須要一個更加通用的 解決方案,要知足任何單身人士找對象的需求。咱們升級一下代碼,先來看 JDK 實現方式:

JDK 實現方式

建立媒婆(婚介)JDKMeipo 類:java

package com.study;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class JdkMeipo implements InvocationHandler {
  	//持有被代理對象的引用
    Object target;

    public Object getInstance(Object target){
        this.target =target;
        Class<?> aClass = target.getClass();
        return Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
    }
  
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object object = method.invoke(this.target,args);
        after();
        return object;
    }

    private void before() {
        System.out.println("我是婚介,幫你物色對象");
    }

    private void after() {
        System.out.println("已找到,若是合適就開始");
    }
}

建立單身客戶spring

package com.study;

public class Customer implements Person{

    @Override
    public void findLove() {
        System.out.println("我要找白富美");
    }
}

測試代碼設計模式

package com.study;

public class DemoTest {
    public static void main(String[] args) {
        Person person = (Person) new JdkMeipo().getInstance(new Customer());
        person.findLove();
    }
}

執行效果
框架

Jdk代理的原理

不只知其然,還得知其因此然。既然 JDK Proxy 功能如此強大,那麼它是如何實現的呢? 咱們如今來探究一下原理。 咱們都知道 JDK Proxy 採用字節重組,從新生的對象來替代原始的對象以達到動態代理 的目的。JDK Proxy 生成對象的步驟以下:ide

1.拿到代理對象的應用,並獲取它的全部接口,反射獲取。測試

2.經過JDK proxy 類從新生成一個新的類,同時新的類要實現被代理類全部實現的接口。this

3.動態生成Java代碼,把新加的業務邏輯方法由必定的邏輯代碼去調用(在代碼中體現)。設計

4.編譯從新生成Java代碼.class代理

5.再從新加載的JVM中
以上這個過程就叫字節重組。日誌

CGLib實現方式

簡單看一下 CGLib 代理的使用,仍是以媒婆爲例,建立 CglibMeipo 類:

package com.study;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGlibMeipo implements MethodInterceptor {
    Object target;
    public Object getInstance(Class<?> aClass){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(aClass);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object invokeSuper = methodProxy.invokeSuper(o, objects);
        after();
        return invokeSuper;
    }

    private void after() {
        System.out.println("已找到,若是合適就開始");
    }

    private void before() {
        System.out.println("我是婚介,幫你物色對象");
    }
}

測試調用

package com.study;

public class DemoTest {
    public static void main(String[] args) {
        //Person person = (Person) new JdkMeipo().getInstance(new Customer());
        Person person = (Person) new CGlibMeipo().getInstance(Customer.class);
        person.findLove();
    }
}

執行結果:(CGLib代理的對象是不須要實現任何接口的,他是經過動態繼承目標對象實現的動態代理。)

CGLib 動態代理執行代理方法效率之因此比 JDK 的高是由於 Cglib 採用了 FastClass 機 制,它的原理簡單來講就是:爲代理類和被代理類各生成一個 Class,這個 Class 會爲代 理類或被代理類的方法分配一個 index(int 類型)。這個 index 當作一個入參,FastClass 就能夠直接定位要調用的方法直接進行調用,這樣省去了反射調用,因此調用效率比 JDK 動態代理經過反射調用高。

CGLib 和 JDK 動態代理對比

1.JDK 動態代理是實現了被代理對象的接口,CGLib 是繼承了被代理對象。

2.JDK 和 CGLib 都是在運行期生成字節碼,JDK 是直接寫 Class 字節碼,CGLib 使用 ASM 框架寫 Class 字節碼,Cglib 代理實現更復雜,生成代理類比 JDK 效率低。

3.JDK 調用代理方法,是經過反射機制調用,CGLib 是經過 FastClass 機制直接調用方法, CGLib 執行效率更高。

相關文章
相關標籤/搜索