最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解JAVA動(dòng)態(tài)代理

 更新時(shí)間:2019年03月22日 10:50:48   作者:奉強(qiáng)的個(gè)人博客  
這篇文章主要介紹了JAVA動(dòng)態(tài)代理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

文檔更新說(shuō)明

2018年09月24日 v1.0 初稿

代理在生活中很常見,比如說(shuō)婚介網(wǎng)站,其實(shí)就是找對(duì)象的代理;還有社保代理、人事代理;還有找黃牛搶票,其實(shí)也是一種代理;而這些代理,在JAVA中也是有對(duì)應(yīng)實(shí)現(xiàn)的。

1、為什么要?jiǎng)討B(tài)代理

動(dòng)態(tài)代理的作用其實(shí)就是在不修改原代碼的前提下,對(duì)已有的方法進(jìn)行增強(qiáng)。
關(guān)鍵點(diǎn):

不修改原來(lái)已有的代碼(滿足設(shè)計(jì)模式的要求)
對(duì)已有方法進(jìn)行增強(qiáng)

2、舉個(gè)栗子

我們用一個(gè)很簡(jiǎn)單的例子來(lái)說(shuō)明:Hello類,有一個(gè)introduction方法。

現(xiàn)在我們的需求就是不修改Hello類的introduction方法,在introduction之前先sayHello,在introduction之后再sayGoodBye

3、實(shí)現(xiàn)方式

JAVA中,實(shí)現(xiàn)動(dòng)態(tài)代理有兩種方式,一種是JDK提供的,一種是第三方庫(kù)CgLib提供的。特點(diǎn)如下:

JDK動(dòng)態(tài)代理:被代理的目標(biāo)類需要實(shí)現(xiàn)接口
CgLib方式:可以對(duì)任意類實(shí)現(xiàn)動(dòng)態(tài)代理

3.1、JDK動(dòng)態(tài)代理

JDK動(dòng)態(tài)代理需要實(shí)現(xiàn)接口,然后通過(guò)對(duì)接口方法的增強(qiáng)來(lái)實(shí)現(xiàn)動(dòng)態(tài)代理

所以要使用JDK動(dòng)態(tài)代理的話,我們首先要?jiǎng)?chuàng)建一個(gè)接口,并且被代理的方法要在這個(gè)接口里面

3.1.1、創(chuàng)建一個(gè)接口

我們創(chuàng)建一個(gè)接口如下:

Personal.java

public interface Personal {

  /**
   * 被代理的方法
   */
  void introduction();

}

3.1.2、實(shí)現(xiàn)接口

創(chuàng)建接口實(shí)現(xiàn)類,并且完成introduction方法

PersonalImpl.java

public class PersonalImpl implements Personal {
  @Override
  public void introduction() {
    System.out.println("我是程序員!");
  }
}

3.1.3、創(chuàng)建代理類

JDK代理的關(guān)鍵就是這個(gè)代理類了,需要實(shí)現(xiàn)InvocationHandler

在代理類中,所有方法的調(diào)用都好分發(fā)到invoke方法中。我們?cè)?code>invoke方法完成對(duì)方法的增強(qiáng)即可

JDKProxyFactory.java

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

public class JDKProxyFactory<T> implements InvocationHandler {

  /**
   * 目標(biāo)對(duì)象
   */
  private T target;

  /**
   * 構(gòu)造函數(shù)傳入目標(biāo)對(duì)象
   *
   * @param target 目標(biāo)對(duì)象
   */
  public JDKProxyFactory(T target) {
    this.target = target;
  }

  /**
   * 獲取代理對(duì)象
   *
   * @return 獲取代理
   */
  public T getProxy() {
    return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 對(duì)方法增強(qiáng)
    System.out.println("大家好!");
    // 調(diào)用原方法
    Object result = method.invoke(target, args);
    // 方法增強(qiáng)
    System.out.println("再見!");
    return result;
  }
}

就這樣,JDK動(dòng)態(tài)代理的代碼就完成了,接下來(lái)寫一份測(cè)試代碼

3.1.4、編寫測(cè)試代碼

為了方便測(cè)試,我們編寫一個(gè)test方法

同時(shí)為了查看class文件,還添加了一個(gè)generatorClass方法,這個(gè)方法可以將動(dòng)態(tài)代理生成的.class輸出到文件

ProxyTest.java

import org.junit.Test;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

public class ProxyTest {

  @Test
  public void testJdkProxy() {
    // 生成目標(biāo)對(duì)象
    Personal personal = new PersonalImpl();
    // 獲取代理對(duì)象
    JDKProxyFactory<Personal> proxyFactory = new JDKProxyFactory<>(personal);
    Personal proxy = proxyFactory.getProxy();

    // 將proxy的class字節(jié)碼輸出到文件
    generatorClass(proxy);

    // 調(diào)用代理對(duì)象
    proxy.introduction();
  }

  /**
   * 將對(duì)象的class字節(jié)碼輸出到文件
   *
   * @param proxy 代理類
   */
  private void generatorClass(Object proxy) {
    FileOutputStream out = null;
    try {
      byte[] generateProxyClass = ProxyGenerator.generateProxyClass(proxy.getClass().getSimpleName(), new Class[]{proxy.getClass()});
      out = new FileOutputStream(proxy.getClass().getSimpleName() + ".class");
      out.write(generateProxyClass);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (out != null) {
        try {
          out.close();
        } catch (IOException e) {
          // TODO Auto-generated catch block
        }
      }
    }

  }

}

3.1.5、查看運(yùn)行結(jié)果

可以看到,運(yùn)行test方法之后,控制臺(tái)打印出如下:

大家好!
我是程序員!
再見!

我們?cè)?code>introduction方法前和后都成功增加了功能,讓這個(gè)程序員的自我介紹瞬間變得更加有禮貌了。

3.1.6、探探動(dòng)態(tài)代理的秘密

動(dòng)態(tài)代理的代碼并不多,那么JDK底層是怎么幫我們實(shí)現(xiàn)的呢?

在測(cè)試的時(shí)候我們將動(dòng)態(tài)生成的代理類的class字節(jié)碼輸出到了文件,我們可以反編譯看看。

結(jié)果有點(diǎn)長(zhǎng),就不全部貼出來(lái)了,不過(guò)我們可以看到,里面有一個(gè)introduction方法如下:

/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;

protected Proxy(InvocationHandler h) {
  Objects.requireNonNull(h);
  this.h = h;
}

public final void introduction() throws {
    try {
      super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw new UndeclaredThrowableException(var3);
    }
  }

原來(lái),生成的代理對(duì)象里面,引用了我們的InvocationHandler,然后在將introduction方法里面調(diào)用了InvocationHandlerintroduction,而InvocationHandler是由我們編寫的代理類,在這里我們?cè)黾恿?code>sayHello和sayGoodBye操作,然后還調(diào)用了原對(duì)象的introduction方法,就這樣完成了動(dòng)態(tài)代理。

3.2、CgLib動(dòng)態(tài)代理

CgLib動(dòng)態(tài)

3.2.1、創(chuàng)建被代理對(duì)象

由于CgLib不需要實(shí)現(xiàn)接口,所以我們不需要?jiǎng)?chuàng)建接口文件了(當(dāng)然,你要有接口也沒(méi)有問(wèn)題)

直接創(chuàng)建目標(biāo)類,實(shí)現(xiàn)introduction方法

PersonalImpl.java

public class PersonalImpl {
  public void introduction() {
    System.out.println("我是程序員!");
  }
}

3.2.2、創(chuàng)建代理類

同樣,我們也需要?jiǎng)?chuàng)建代理類,并且在這里實(shí)現(xiàn)增強(qiáng)的邏輯,這次我們不是實(shí)現(xiàn)InvocationHandler接口了,而是實(shí)現(xiàn)CgLib提供的接口MethodInterceptor,都是類似的,MethodInterceptor中,全部方法調(diào)用都會(huì)交給intercept處理,我們?cè)?code>intercept添加處理邏輯即可。

CgLibProxyFactory.java

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

import java.lang.reflect.Method;

public class CgLibProxyFactory<T> implements MethodInterceptor {

  /**
   * 獲取代理對(duì)象
   *
   * @param tClass 被代理的目標(biāo)對(duì)象
   * @return 代理對(duì)象
   */
  public T getProxyByCgLib(Class<T> tClass) {
    // 創(chuàng)建增強(qiáng)器
    Enhancer enhancer = new Enhancer();

    // 設(shè)置需要增強(qiáng)的類的類對(duì)象
    enhancer.setSuperclass(tClass);

    // 設(shè)置回調(diào)函數(shù)
    enhancer.setCallback(this);

    // 獲取增強(qiáng)之后的代理對(duì)象
    return (T) enhancer.create();
  }

   /**
   * 代理類方法調(diào)用回調(diào)
   * 
   * @param obj 這是代理對(duì)象,也就是[目標(biāo)對(duì)象]的子類
   * @param method [目標(biāo)對(duì)象]的方法
   * @param args 參數(shù)
   * @param proxy 代理對(duì)象的方法
   * @return 返回結(jié)果,返回給調(diào)用者
   * @throws Throwable
   */
  @Override
  public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

    System.out.println("大家好!");

    Object result = proxy.invokeSuper(obj, args);

    System.out.println("再見!");

    return result;
  }
}

3.2.3、編寫測(cè)試代碼

在剛才的測(cè)試方法中,我們添加一個(gè)cglib的測(cè)試方法:

@Test
public void testCgLibProxy() {
  // 生成被代理的目標(biāo)對(duì)象
  PersonalImpl personal = new PersonalImpl();
  
  // 獲取代理類
  CgLibProxyFactory<PersonalImpl> proxyFactory = new CgLibProxyFactory<>();
  PersonalImpl proxy = proxyFactory.getProxyByCgLib((Class<PersonalImpl>) personal.getClass());

  // 將proxy的class字節(jié)碼輸出到文件
  generatorClass(proxy);

  // 調(diào)用代理對(duì)象
  proxy.introduction();
}

3.2.4、查看運(yùn)行結(jié)果

運(yùn)行測(cè)試用例,可以看到跟JDK的實(shí)現(xiàn)一樣的效果

大家好!
我是程序員!
再見!

3.2.5、探探動(dòng)態(tài)代理的秘密

JDK的測(cè)試一樣,我們也來(lái)看看生成的class文件

public final void introduction() throws {
  try {
    super.h.invoke(this, m7, (Object[])null);
  } catch (RuntimeException | Error var2) {
    throw var2;
  } catch (Throwable var3) {
    throw new UndeclaredThrowableException(var3);
  }
}

可以發(fā)現(xiàn),與JDK的動(dòng)態(tài)代理并沒(méi)有區(qū)別。

4、如何選擇

既然有兩種實(shí)現(xiàn)方式,那么到底應(yīng)該怎么選擇呢?

就兩個(gè)原則:

目標(biāo)類有接口實(shí)現(xiàn)的,JDKCgLib都可以選擇,你開心就好
目標(biāo)類沒(méi)有實(shí)現(xiàn)任何接口,那只能用CgLib

5、后記

其實(shí)在第一次看到動(dòng)態(tài)代理的時(shí)候,我就想不明白,我們都把目標(biāo)類new出來(lái)了,為什么還要將目標(biāo)類丟給代理類呢?為什么不直接調(diào)用目標(biāo)類對(duì)應(yīng)的方法呢?

后來(lái)才發(fā)現(xiàn),原來(lái)我沒(méi)搞清楚動(dòng)態(tài)代理的使用場(chǎng)景,場(chǎng)景很清晰,就是:

不修改原來(lái)已有的代碼(滿足設(shè)計(jì)模式的要求)
對(duì)已有方法進(jìn)行增強(qiáng)
關(guān)鍵是增強(qiáng),代理類里面我們是可以添加很多處理邏輯的,從而實(shí)現(xiàn)增強(qiáng)效果。就像黃牛搶票比我們厲害些一樣。

以上所述是小編給大家介紹的JAVA動(dòng)態(tài)代理詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Java調(diào)整圖片大小的3種方式小結(jié)

    Java調(diào)整圖片大小的3種方式小結(jié)

    在軟件開發(fā)中處理圖像是一個(gè)常見任務(wù),特別是當(dāng)我們需要優(yōu)化圖像尺寸以適應(yīng)不同的應(yīng)用場(chǎng)景時(shí),這篇文章主要介紹了Java調(diào)整圖片大小的3種方式,需要的朋友可以參考下
    2024-09-09
  • Spring Security中successHandler無(wú)效問(wèn)題及解決

    Spring Security中successHandler無(wú)效問(wèn)題及解決

    這篇文章主要介紹了Spring Security中successHandler無(wú)效問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • IDEA一致卡在build時(shí)間過(guò)長(zhǎng)問(wèn)題解決

    IDEA一致卡在build時(shí)間過(guò)長(zhǎng)問(wèn)題解決

    有很多小伙伴在起項(xiàng)目的時(shí)候巨慢,特別影響開發(fā)效率,本文主要介紹了IDEA一致卡在build時(shí)間過(guò)長(zhǎng)問(wèn)題解決,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06
  • springboot post接口接受json時(shí),轉(zhuǎn)換為對(duì)象時(shí),屬性都為null的解決

    springboot post接口接受json時(shí),轉(zhuǎn)換為對(duì)象時(shí),屬性都為null的解決

    這篇文章主要介紹了springboot post接口接受json時(shí),轉(zhuǎn)換為對(duì)象時(shí),屬性都為null的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Spring boot自定義http反饋狀態(tài)碼詳解

    Spring boot自定義http反饋狀態(tài)碼詳解

    這篇文章主要給大家介紹了Spring boot自定義http反饋狀態(tài)碼的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。
    2017-06-06
  • 解決mybatis中的mapper命名問(wèn)題

    解決mybatis中的mapper命名問(wèn)題

    這篇文章主要介紹了解決mybatis中的mapper命名問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • springboot整合Nacos組件環(huán)境搭建和入門案例詳解(最新推薦)

    springboot整合Nacos組件環(huán)境搭建和入門案例詳解(最新推薦)

    本文介紹了Nacos的基礎(chǔ)概念、關(guān)鍵特性、專業(yè)術(shù)語(yǔ)和生態(tài)圈,如何在Windows環(huán)境下搭建Nacos單個(gè)服務(wù),以及如何整合SpringBoot2來(lái)使用Nacos進(jìn)行服務(wù)注冊(cè)和配置管理,感興趣的朋友一起看看吧
    2025-03-03
  • Java KindEditor粘貼圖片自動(dòng)上傳到服務(wù)器功能實(shí)現(xiàn)

    Java KindEditor粘貼圖片自動(dòng)上傳到服務(wù)器功能實(shí)現(xiàn)

    這篇文章主要介紹了Java KindEditor粘貼圖片自動(dòng)上傳到服務(wù)器功能實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • MybatisPlus查詢條件空字符串和NULL問(wèn)題背景分析

    MybatisPlus查詢條件空字符串和NULL問(wèn)題背景分析

    文章詳細(xì)分析了MybatisPlus在處理查詢條件時(shí),空字符串和NULL值的問(wèn)題,MP 3.3.0及以上版本提供了多種解決方法,包括在Bean屬性上使用注解、全局配置等,推薦使用全局配置的方式來(lái)解決這個(gè)問(wèn)題,以避免在SQL查詢中出現(xiàn)不必要的空字符串條件,感興趣的朋友跟隨小編一起看看吧
    2025-03-03
  • springboot整合JPA訪問(wèn)Mysql的實(shí)現(xiàn)方法

    springboot整合JPA訪問(wèn)Mysql的實(shí)現(xiàn)方法

    本文主要介紹了springboot整合JPA訪問(wèn)Mysql的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02

最新評(píng)論

呼和浩特市| 冕宁县| 昌都县| 洛扎县| 景宁| 桦南县| 呼图壁县| 廉江市| 浪卡子县| 随州市| 仁寿县| 盈江县| 南投市| 西乌| 泸定县| 屏山县| 建瓯市| 潍坊市| 昆山市| 平顺县| 上思县| 鄢陵县| 赣榆县| 平凉市| 威远县| 滁州市| 都匀市| 北宁市| 灵川县| 西丰县| 攀枝花市| 平江县| 佛学| 兴国县| 萨嘎县| 嘉义县| 正镶白旗| 垦利县| 孟连| 西乌珠穆沁旗| 宁海县|