91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

詳解JAVA動態代理

發布時間:2020-08-26 03:52:04 來源:腳本之家 閱讀:157 作者:奉強的個人博客 欄目:編程語言

文檔更新說明

2018年09月24日 v1.0 初稿

代理在生活中很常見,比如說婚介網站,其實就是找對象的代理;還有社保代理、人事代理;還有找黃牛搶票,其實也是一種代理;而這些代理,在JAVA中也是有對應實現的。

1、為什么要動態代理

動態代理的作用其實就是在不修改原代碼的前提下,對已有的方法進行增強。
關鍵點:

不修改原來已有的代碼(滿足設計模式的要求)
對已有方法進行增強

2、舉個栗子

我們用一個很簡單的例子來說明:Hello類,有一個introduction方法。

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

3、實現方式

JAVA中,實現動態代理有兩種方式,一種是JDK提供的,一種是第三方庫CgLib提供的。特點如下:

JDK動態代理:被代理的目標類需要實現接口
CgLib方式:可以對任意類實現動態代理

3.1、JDK動態代理

JDK動態代理需要實現接口,然后通過對接口方法的增強來實現動態代理

所以要使用JDK動態代理的話,我們首先要創建一個接口,并且被代理的方法要在這個接口里面

3.1.1、創建一個接口

我們創建一個接口如下:

Personal.java

public interface Personal {

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

}

3.1.2、實現接口

創建接口實現類,并且完成introduction方法

PersonalImpl.java

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

3.1.3、創建代理類

JDK代理的關鍵就是這個代理類了,需要實現InvocationHandler

在代理類中,所有方法的調用都好分發到invoke方法中。我們在invoke方法完成對方法的增強即可

JDKProxyFactory.java

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

public class JDKProxyFactory<T> implements InvocationHandler {

  /**
   * 目標對象
   */
  private T target;

  /**
   * 構造函數傳入目標對象
   *
   * @param target 目標對象
   */
  public JDKProxyFactory(T target) {
    this.target = target;
  }

  /**
   * 獲取代理對象
   *
   * @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 {
    // 對方法增強
    System.out.println("大家好!");
    // 調用原方法
    Object result = method.invoke(target, args);
    // 方法增強
    System.out.println("再見!");
    return result;
  }
}

就這樣,JDK動態代理的代碼就完成了,接下來寫一份測試代碼

3.1.4、編寫測試代碼

為了方便測試,我們編寫一個test方法

同時為了查看class文件,還添加了一個generatorClass方法,這個方法可以將動態代理生成的.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() {
    // 生成目標對象
    Personal personal = new PersonalImpl();
    // 獲取代理對象
    JDKProxyFactory<Personal> proxyFactory = new JDKProxyFactory<>(personal);
    Personal proxy = proxyFactory.getProxy();

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

    // 調用代理對象
    proxy.introduction();
  }

  /**
   * 將對象的class字節碼輸出到文件
   *
   * @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、查看運行結果

可以看到,運行test方法之后,控制臺打印出如下:

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

我們在introduction方法前和后都成功增加了功能,讓這個程序員的自我介紹瞬間變得更加有禮貌了。

3.1.6、探探動態代理的秘密

動態代理的代碼并不多,那么JDK底層是怎么幫我們實現的呢?

在測試的時候我們將動態生成的代理類的class字節碼輸出到了文件,我們可以反編譯看看。

結果有點長,就不全部貼出來了,不過我們可以看到,里面有一個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);
    }
  }

原來,生成的代理對象里面,引用了我們的InvocationHandler,然后在將introduction方法里面調用了InvocationHandlerintroduction,而InvocationHandler是由我們編寫的代理類,在這里我們增加了sayHellosayGoodBye操作,然后還調用了原對象的introduction方法,就這樣完成了動態代理。

3.2、CgLib動態代理

CgLib動態

3.2.1、創建被代理對象

由于CgLib不需要實現接口,所以我們不需要創建接口文件了(當然,你要有接口也沒有問題)

直接創建目標類,實現introduction方法

PersonalImpl.java

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

3.2.2、創建代理類

同樣,我們也需要創建代理類,并且在這里實現增強的邏輯,這次我們不是實現InvocationHandler接口了,而是實現CgLib提供的接口MethodInterceptor,都是類似的,MethodInterceptor中,全部方法調用都會交給intercept處理,我們在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 {

  /**
   * 獲取代理對象
   *
   * @param tClass 被代理的目標對象
   * @return 代理對象
   */
  public T getProxyByCgLib(Class<T> tClass) {
    // 創建增強器
    Enhancer enhancer = new Enhancer();

    // 設置需要增強的類的類對象
    enhancer.setSuperclass(tClass);

    // 設置回調函數
    enhancer.setCallback(this);

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

   /**
   * 代理類方法調用回調
   * 
   * @param obj 這是代理對象,也就是[目標對象]的子類
   * @param method [目標對象]的方法
   * @param args 參數
   * @param proxy 代理對象的方法
   * @return 返回結果,返回給調用者
   * @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、編寫測試代碼

在剛才的測試方法中,我們添加一個cglib的測試方法:

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

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

  // 調用代理對象
  proxy.introduction();
}

3.2.4、查看運行結果

運行測試用例,可以看到跟JDK的實現一樣的效果

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

3.2.5、探探動態代理的秘密

JDK的測試一樣,我們也來看看生成的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);
  }
}

可以發現,與JDK的動態代理并沒有區別。

4、如何選擇

既然有兩種實現方式,那么到底應該怎么選擇呢?

就兩個原則:

目標類有接口實現的,JDKCgLib都可以選擇,你開心就好
目標類沒有實現任何接口,那只能用CgLib

5、后記

其實在第一次看到動態代理的時候,我就想不明白,我們都把目標類new出來了,為什么還要將目標類丟給代理類呢?為什么不直接調用目標類對應的方法呢?

后來才發現,原來我沒搞清楚動態代理的使用場景,場景很清晰,就是:

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

以上所述是小編給大家介紹的JAVA動態代理詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

五寨县| 宁陵县| 库尔勒市| 微山县| 恩平市| 岗巴县| 攀枝花市| 陆丰市| 福建省| 阿合奇县| 宁南县| 启东市| 阿勒泰市| 金寨县| 仪陇县| 土默特右旗| 察哈| 灌云县| 金溪县| 石嘴山市| 淮南市| 营山县| 仁怀市| 宾川县| 邵东县| 汝州市| 南京市| 汕尾市| 鹤峰县| 尚志市| 临泽县| 墨江| 平乐县| 津市市| 万盛区| 芜湖县| 商河县| 普陀区| 望城县| 棋牌| 灵川县|