好得很程序员自学网

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

Java反射(JDK)与动态代理(CGLIB)详解

一、反射

概念 :在运行状态中,对于任意的一个类,都能够知道这个类的所有字段和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法

实现方法 :JVM在第一次加载某个类时会生成一个Class对象,里面记录了这个类的信息

链接:类加载机制(留坑)

二、动态代理

动态代理的作用 :在不改变原代码的基础上增加新的功能,如日志、权限检验等

反射在动态代理中的应用 :由于知道原类的字段、方法等信息,才可以通过代理类执行被代理类的方法

动态代理的实现 有两种

1、JDK代理

实现方法:通过创建一个代理类,这个代理类继承于一个Proxy类,Proxy类中有一个InvocationHandler接口,这个接口持有被代理对象和一个invoke()方法。创建好代理类对象后,对该对象调用的方法都会交由invoke方法处理。invoke方法接受3个参数:代理对象、方法、参数列表。重写invoke方法便可以在原方法的基础上添加其他逻辑

一个JDK代理的简单实现:

?

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

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

// 定义一个接口

interface MyInterface {

     void fun();

}

// 用一个类去实现这个接口

class Person implements MyInterface {

     @Override

     public void fun() {

         System.out.println( "Person实现接口方法" );

     }

}

// 用一个类实现InvocationHandler接口

class MyInvocationHandler<T> implements InvocationHandler {

     //InvocationHandler持有的被代理对象

     T target;

     public MyInvocationHandler(T target) {

         this .target = target;

     }

     /**

      * proxy:代表动态代理对象

      * method:代表正在执行的方法

      * args:代表调用目标方法时传入的实参

      */

     @Override

     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

         System.out.println( "代理执行" + method.getName() + "方法" ); // 在原方法的基础上添加其他逻辑

         Object result = method.invoke(target, args); // 通过invoke方法调用原方法

         return result;

     }

}

public class Main {

     public static void main(String[] args) {

         Person person = new Person();

         InvocationHandler invocationHandler = new MyInvocationHandler<>(person);

         MyInterface proxy = (MyInterface) Proxy.newProxyInstance(Person. class .getClassLoader(),

                 Person. class .getInterfaces(), invocationHandler);

         proxy.fun();

     }

}

输出结果:

代理执行fun方法
Person实现接口方法

缺陷 :只能代理接口方法,因为JDK代理需要继承一个Proxy类,又由于Java的单继承机制,导致代理类无法继承父类的函数,只能实现接口

2、CGLIB代理

原理与JDK代理类似,区别在于CGLIB代理创建的代理类直接继承于被代理类,所以可以实现被代理类的方法而非仅仅接口方法

一个简单的CGLIB代理实现:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public class Main {

     public static void main(String[] args) {

         Enhancer enhancer = new Enhancer();

         enhancer.setSuperclass(Car. class );

         enhancer.setCallback( new MethodInterceptor() {

             @Override

             public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)

                     throws Throwable {

                 System.out.println( "before" );

                 Object res = methodProxy.invokeSuper(obj, args);

                 System.out.println( "after" );

                 return res;

             }

         });

         Car car = (Car) enhancer.create();

         car.print();

     }

}

class Car {

     void print() {

         System.out.println( "执行原方法" );

     }

}

由于CGLIB并非JDK自带,所以需要通过Maven引入一个依赖

?

1

2

3

4

5

< dependency >

     < groupId >org.sonatype.sisu.inject</ groupId >

     < artifactId >cglib</ artifactId >

     < version >3.1.1</ version >

</ dependency >

输出结果:

before
执行原方法
after

3、JDK代理与CGLIB代理对比

1、JDK代理只能实现接口方法,而CGLIB代理既可以实现接口方法也可以实现类中自带的方法

2、性能上,在JDK1.8,CGLIB3.1.1的环境上,每次创建一个代理类并执行同样的方法

当执行10000次,JDK代理用时85ms,而CGLIB代理用时190ms,明显JDK代理性能更佳;

当执行1000000(一百万)次时,两种代理耗时几乎相等;

当执行10000000次时,CGLIB代理已经优于JDK代理。

所以在执行次数少时,JDK代理性能更好;反之CGLIB代理性能更好(但是重复执行多于1000000次的任务几乎没有吧

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!

原文链接:https://blog.csdn.net/qq_45404693/article/details/119972532

查看更多关于Java反射(JDK)与动态代理(CGLIB)详解的详细内容...

  阅读:8次