Java反射机制与动态代理

Java 反射机制

**反射概念:**在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制

反射机制是 Java 语言提供的一种基础功能,赋予程序在运行时自省(introspect,官方用语)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类定义。

获取Class对象的三种方式:

  • 通过Object类getClass()方法
  • 通过一个静态的class属性获取
  • 通过Class类的静态方法:forName(String className)——常用

Java 动态代理

动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如用来包装 RPC 调用、面向切面的编程(AOP)。

实现动态代理的方式很多,比如 JDK 自身提供的动态代理,就是主要利用了上面提到的反射机制。还有其他的实现方式,比如利用传说中更高性能的字节码操作机制,类似 ASM、cglib(基于 ASM)、Javassist 等。

JDK动态代理的实现与原理

实现:

  • 创建一个与代理对象相关联的InvocationHandler
  • 使用Proxy类的getProxyClass静态方法生成一个动态代理类ProxyClass
  • 获得ProxyClass 中一个带InvocationHandler参数的构造器constructor
  • 通过构造器constructor来创建一个动态实例Proxy

原理:

jdk为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。我们可以对InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的invoke方法,从而达到代理执行被代理对象的方法。【动态代理通过中介类实现了具体的代理功能】

CGLIB动态代理与JDK动态区别

  • java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvocationHandler【增强器】来处理。

  • cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理

  • JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。 CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。 因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态

    1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
    2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
    3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高

JDK动态代理的实例

本示例以交班费为实例,各个学生上交班费由班长进行代理上交,同时班长在统一上交时,可以作其他的操作。本例班长工具类作了计算上交过程的时间消耗的功能增强。

  • Person 接口
1
2
3
4
public interface Person {
//上交班费
void giveMoney();
}
  • Student对象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}

@Override
public void giveMoney() {
try {
//假设数钱花了一秒时间
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "上交班费50元");
}
}
  • 班长工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MonitorUtil {

private static ThreadLocal<Long> tl = new ThreadLocal<>();

public static void start() {
tl.set(System.currentTimeMillis());
}

//结束时打印耗时
public static void finish(String methodName) {
long finishTime = System.currentTimeMillis();
System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
}
}
  • 代理类
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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* Created by mzYan on 2020-03-21 11:22
*/
public class StuInvocationHandler<T> implements InvocationHandler {
//invocationHandler持有的被代理对象
T target;

public StuInvocationHandler(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() + "方法");
//代理过程中插入监测方法,计算该方法耗时
MonitorUtil.start();
Object result = method.invoke(target, args);
MonitorUtil.finish(method.getName());
return result;
}
}
  • 测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
* Created by mzYan on 2020-03-21 11:23
*/
public class ProxyTest {
public static void main(String[] args) {

//创建一个实例对象,这个对象是被代理的对象
Person zhangsan = new Student("张三");

//创建一个与代理对象相关联的InvocationHandler
InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);

//创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

//代理执行上交班费的方法
stuProxy.giveMoney();
}
}