一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - 詳解JAVA動態代理

詳解JAVA動態代理

2021-07-25 15:37奉強的個人博客 Java教程

這篇文章主要介紹了JAVA動態代理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

文檔更新說明

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

?
1
2
3
4
5
6
7
8
public interface personal {
 
  /**
   * 被代理的方法
   */
  void introduction();
 
}

3.1.2、實現接口

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

personalimpl.java

?
1
2
3
4
5
6
public class personalimpl implements personal {
  @override
  public void introduction() {
    system.out.println("我是程序員!");
  }
}

3.1.3、創建代理類

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

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

jdkproxyfactory.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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方法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 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

?
1
2
3
4
5
public class personalimpl {
  public void introduction() {
    system.out.println("我是程序員!");
  }
}

3.2.2、創建代理類

同樣,我們也需要創建代理類,并且在這里實現增強的邏輯,這次我們不是實現invocationhandler接口了,而是實現cglib提供的接口methodinterceptor,都是類似的,methodinterceptor中,全部方法調用都會交給intercept處理,我們在intercept添加處理邏輯即可。

cglibproxyfactory.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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的測試方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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文件

?
1
2
3
4
5
6
7
8
9
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動態代理詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!

原文鏈接:https://www.fengqiangboy.com/15377761043880.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲欧美日韩中文字幕网址 | 久久日韩精品无码一区 | 亚洲国产欧美在线人成aaa | 国产香蕉久久 | 国内精品视频一区二区三区八戒 | 欧美一二| 性色生活片在线观看 | 亚洲天堂男人的天堂 | 精品国产福利片在线观看 | 丁香六月色 | 99久久国产综合精麻豆 | 3d动漫美女物被遭强视频 | 亚洲精品国产成人中文 | 亚洲欧美久久婷婷爱综合一区天堂 | 欧美一级xxxx俄罗斯一级 | 999热这里全都是精品 | 修修视频在线观看 | 三级无删减高清在线影院 | 亚偷熟乱区视频在线观看 | 欧美成人免费观看bbb | 百合漫画咱啪全彩抚慰 | 免费黄色片网站 | 日韩一级精品视频在线观看 | 蜜桃影像传媒推广 | 日韩欧美在线看 | 国产福利一区二区精品视频 | 91精品综合 | 久久国产精品人妻中文 | 嗯啊好大视频 | chinesemature老女人 | 亚洲XXX午休国产熟女屁 | 日韩精品亚洲专区在线影视 | 麻豆网站在线免费观看 | 欧美日韩一区二区三区韩大 | 久久综合色超碰人人 | 亚洲国产韩国欧美在线不卡 | 久久AV国产麻豆HD真实 | 国产精品久久久久久爽爽爽 | 扒开双腿羞辱调教play视频 | 视频在线网站 | 69re在线观看|