好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

详解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 方法里面调用了 invocationhandler 的 introduction ,而 invocationhandler 是由我们编写的代理类,在这里我们增加了 sayhello 和 saygoodbye 操作,然后还调用了原对象的 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、如何选择

既然有两种实现方式,那么到底应该怎么选择呢?

就两个原则:

目标类有接口实现的, jdk 和 cglib 都可以选择,你开心就好
目标类没有实现任何接口,那只能用 cglib 了

5、后记

其实在第一次看到动态代理的时候,我就想不明白,我们都把目标类new出来了,为什么还要将目标类丢给代理类呢?为什么不直接调用目标类对应的方法呢?

后来才发现,原来我没搞清楚动态代理的使用场景,场景很清晰,就是:

不修改原来已有的代码(满足设计模式的要求)
对已有方法进行增强
关键是增强,代理类里面我们是可以添加很多处理逻辑的,从而实现增强效果。就像黄牛抢票比我们厉害些一样。

以上所述是小编给大家介绍的java动态代理详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

原文链接:https://HdhCmsTestfengqiangboy测试数据/15377761043880.html

查看更多关于详解JAVA动态代理的详细内容...

  阅读:14次