淺析Java設(shè)計(jì)模式【3】——代理

1. 前情內(nèi)容

淺析Java設(shè)計(jì)模式【1】——觀察者

淺析Java設(shè)計(jì)模式【2】——適配器

淺析Java設(shè)計(jì)模式【3】——代理

2. 目錄

3. 概念

百度百科對(duì)它的定義是:對(duì)其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。

代理模式更多的是一種安全層面的考慮,保護(hù)被訪問(wèn)對(duì)象的安全,在訪問(wèn)者和被訪問(wèn)者之間起一種中介作用。在我們具體應(yīng)用中都會(huì)遇到需要記錄掌控方法的執(zhí)行前后的動(dòng)作。

從生成方式的不通又在代理中分為靜態(tài)代理、動(dòng)態(tài)代理以及Cglib代理。

4. 靜態(tài)代理

代理類(lèi)在程序運(yùn)行前就已經(jīng)定義好,其與目標(biāo)類(lèi)(被代理類(lèi))的關(guān)系在程序運(yùn)行前就已經(jīng)確立,屬于事前約定的范疇。這一點(diǎn)非常類(lèi)似于企業(yè)與企業(yè)法律顧問(wèn)間的關(guān)系,他們之間的代理關(guān)系,并不是在“官司“發(fā)生后才建立的,而是之前就確立好的一種關(guān)系。在這一點(diǎn)上動(dòng)態(tài)代理可以理解為官司已經(jīng)開(kāi)始,才臨時(shí)聘請(qǐng)熟諳法律的律師。


package xyz.wongs.weathertop.design.proxy;

/**
 * @ClassName Loginable
 * @Description 定義接口
 * @author WCNGS@QQ.COM
 * @Github <a>https://github.com/rothschil</a>
 * @date 2019/12/27 15:31
 * @Version 1.0.0
*/
public interface Loginable {
    void login();
}


package xyz.wongs.weathertop.design.proxy.stat;

import lombok.extern.slf4j.Slf4j;
import xyz.wongs.weathertop.design.proxy.Loginable;

import java.util.Random;

/**
 * @ClassName LoginService
 * @Description 登陸實(shí)現(xiàn)類(lèi)
 * @author WCNGS@QQ.COM
 * @Github <a>https://github.com/rothschil</a>
 * @date 2019/12/27 15:32
 * @Version 1.0.0
*/
@Slf4j
public class LoginService implements Loginable {

    @Override
    public void login(){
        try {
            Thread.sleep(new Random().nextInt(5000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.error("登陸成功");
    }
}

package xyz.wongs.weathertop.design.proxy.stat;

import lombok.extern.slf4j.Slf4j;
import xyz.wongs.weathertop.design.proxy.Loginable;

/**
 * @ClassName LoginServiceProxy
 * @Description 實(shí)際代理類(lèi)
 * @author WCNGS@QQ.COM
 * @Github <a>https://github.com/rothschil</a>
 * @date 2019/12/27 15:32
 * @Version 1.0.0
*/
@Slf4j
public class LoginServiceProxy implements Loginable {

    private Loginable loginable;

    public LoginServiceProxy() {
    }

    public LoginServiceProxy(Loginable loginable) {
        this.loginable = loginable;
    }

    @Override
    public void login() {
        long start = System.currentTimeMillis();
        log.error("start proxy : " + start);
        loginable.login();
        long end = System.currentTimeMillis();
        log.error("end proxy : " + end);
        log.error("spend all time : " + (end - start) + " ms.");
    }
}

靜態(tài)代理

上面的一個(gè)登陸例子中,我們通過(guò)代理對(duì)象來(lái)獲取我們想要的目標(biāo)(LoginService),外界并不知道目標(biāo)是如何實(shí)現(xiàn)的,無(wú)形之中保護(hù)了真正的意圖。

小結(jié)一下:
靜態(tài)代理對(duì)象(LoginServiceProxy)有兩個(gè)特征:

  • 它內(nèi)部包含著被代理對(duì)象(Loginable)實(shí)現(xiàn)接口的引用。

  • 它又實(shí)現(xiàn)了被代理對(duì)象(Loginable)實(shí)現(xiàn)的接口。

這兩個(gè)特征既是特點(diǎn)同時(shí)也帶有劣勢(shì),就是代理的對(duì)象必須提前知曉,當(dāng)被代理對(duì)象發(fā)生變更,相應(yīng)的代理對(duì)象也要跟著變更,還是有點(diǎn)不便捷。那有沒(méi)有一種手段可在運(yùn)行過(guò)程中動(dòng)態(tài)地產(chǎn)生一個(gè)代理對(duì)象,非但再也不用維護(hù)代理類(lèi),更提高整體的效率。答案是有的,這種手段就是下來(lái)的動(dòng)態(tài)代理。

5. 動(dòng)態(tài)代理

JAVA中的AOP切面就是基于動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的,但是它用到了兩種代理模式分別為jdk動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理,當(dāng)然這兩種代理各有優(yōu)劣,這一點(diǎn)我們?cè)谧詈髸?huì)統(tǒng)一總結(jié)。

5.1. JDK動(dòng)態(tài)代理

在靜態(tài)代理的模塊上做一下改造,沿用接口和接口實(shí)現(xiàn)部分,新增一個(gè)

5.1.1. 實(shí)現(xiàn)

package xyz.wongs.weathertop.design.proxy.dyc.jdk;

import lombok.extern.slf4j.Slf4j;
import xyz.wongs.weathertop.design.proxy.Loginable;

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

@Slf4j
public class LoginServiceDycProxy implements InvocationHandler {

    private Loginable loginable;

    public Object getInstall(Loginable loginable){
        this.loginable = loginable;
        return Proxy.newProxyInstance(loginable.getClass().getClassLoader(),loginable.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.error("Begin 執(zhí)行方法前的操作");

        if(method.getName().equalsIgnoreCase("login")){
            loginable.login();
        }
        log.error("End 執(zhí)行方法后的操作");
        return null;
    }
}


5.1.2. 演示結(jié)果

@Test
public void testDycProxy(){
    LoginServiceDycProxy lsdp = new LoginServiceDycProxy();
    Loginable loginable = (Loginable)lsdp.getInstall(new LoginService());
    loginable.login();
}

17:48:57.793 [main] ERROR xyz.wongs.weathertop.design.proxy.dyc.jdk.LoginServiceDycProxy - Begin 執(zhí)行方法前的操作
17:49:00.594 [main] ERROR xyz.wongs.weathertop.design.proxy.LoginService - 登陸成功
17:49:00.594 [main] ERROR xyz.wongs.weathertop.design.proxy.dyc.jdk.LoginServiceDycProxy - End 執(zhí)行方法后的操作

5.2. cglib代理

5.2.1. 引入依賴(lài)包

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

5.2.2. 代理類(lèi)

package xyz.wongs.weathertop.design.proxy.dyc.cglib;

import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

@Slf4j
public class LoginServiceDycProxyCglib implements MethodInterceptor {

    public Object getInstall(Object object){
        return Enhancer.create(object.getClass(), this);
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        log.error("Begin 執(zhí)行方法前的操作");
        methodProxy.invokeSuper(o,objects);
        log.error("End 執(zhí)行方法后的操作");
        return null;
    }
}

5.2.3. 測(cè)試


@Test
public void testDycProxyCglib(){
    LoginServiceDycProxyCglib lsdp = new LoginServiceDycProxyCglib();
    Loginable loginable = (Loginable)lsdp.getInstall(new LoginService());
    loginable.login();
}

5.2.4. 演示結(jié)果

18:00:53.993 [main] ERROR xyz.wongs.weathertop.design.proxy.dyc.cglib.LoginServiceDycProxyCglib - Begin 執(zhí)行方法前的操作
18:00:57.787 [main] ERROR xyz.wongs.weathertop.design.proxy.LoginService - 登陸成功
18:00:57.787 [main] ERROR xyz.wongs.weathertop.design.proxy.dyc.cglib.LoginServiceDycProxyCglib - End 執(zhí)行方法后的操作

5.3. jdk代理與cglib代理比較

  • 實(shí)現(xiàn)機(jī)制:jdk動(dòng)態(tài)代理是由java內(nèi)部的反射機(jī)制來(lái)實(shí)現(xiàn)的,cglib動(dòng)態(tài)代理底層則是借助asm來(lái)實(shí)現(xiàn)的;

  • 效率上:反射機(jī)制在類(lèi)的生成過(guò)程中比較高效,而asm機(jī)制在類(lèi)生成之后的執(zhí)行過(guò)程中比較高效,當(dāng)然也有可以通過(guò)將asm生成的類(lèi)接入緩存,這樣也可以解決asm生成類(lèi)過(guò)程低效問(wèn)題;

  • 應(yīng)用上: jdk動(dòng)態(tài)代理的應(yīng)用需要依賴(lài)目標(biāo)類(lèi)均基于統(tǒng)一的接口,而cglib則無(wú)限制;

綜上所述,我們?cè)趯?shí)際過(guò)程中基于第三方庫(kù)實(shí)現(xiàn)的動(dòng)態(tài)代理應(yīng)用在綜合效率上更有優(yōu)勢(shì)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、概述 ??代理模式我們接觸的就比較多了,所謂的代理模式就是,給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)原...
    騎著烏龜去看海閱讀 1,011評(píng)論 0 9
  • 動(dòng)態(tài)代理的意義在于生成一個(gè)占位(又稱(chēng)代理對(duì)象),來(lái)代理真實(shí)對(duì)象,從而控制真實(shí)對(duì)象的訪問(wèn)。 我們首先來(lái)談?wù)勈裁词谴?..
    Haozz_1994閱讀 376評(píng)論 0 2
  • 7.代理模式 7.1.課程目標(biāo) 1、掌握代理模式的應(yīng)用場(chǎng)景和實(shí)現(xiàn)原理。 2、了解靜態(tài)代理和動(dòng)態(tài)代理的區(qū)別。 3、了...
    我是阿喵醬閱讀 657評(píng)論 0 0
  • 前言 《設(shè)計(jì)模式自習(xí)室》系列,顧名思義,本系列文章帶你溫習(xí)常見(jiàn)的設(shè)計(jì)模式。主要內(nèi)容有: 該模式的介紹,包括:引子、...
    蠻三刀醬閱讀 650評(píng)論 0 0
  • 讀書(shū)改變的不是容貌的形,而是容貌的神韻。一個(gè)人神韻的改變,是由內(nèi)心而發(fā)出來(lái)的。 這些年,我讀了不少名家的作品,用心...
    西廊白衣閱讀 15,983評(píng)論 0 4

友情鏈接更多精彩內(nèi)容