Java反射&动态代理

反射

反射是Java在运行时动态获取类的信息(如类名、方法、属性、构造方法等)并操作类的能力

API

获取Class对象

1
2
3
4
5
6
7
String str="test";
Class<?> clazz = String.class;

String str = "test"; 
Class<?> clazz = str.getClass();

Class<?> clazz = Class.forName("java.lang.String");

通过反射创建对象

1
2
3
4
5
Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.getDeclaredConstructor().newInstance();

Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("Alice", 25);

获取字段

getDeclaredField(“字段名”)

优点

  1. 可以访问类的私有对象/字段, 方便单元测试
1
2
3
Field nameField = personClass.getDeclaredField("name");
nameField.setAccessible(true); // 如果字段是 private,需要设置可访问性
System.out.println("Name before: " + nameField.get(person));
  1. 动态代理,提高代码复用性

  2. Spring框架

泛型、反射和类型推断的关系

动态代理

优点:动态生成代理类,不需要手动定义多个代理类。

  1. 定义一个接口UserService,里面定义我想被代理的方法A
  2. 创建一个代理类UserServiceProxy,这个类implements InvocationHandler这个接口,也就是重写invoke()这个方法,具体是在调用方法A之前或之后加上我想要的逻辑。invoke里面通过反射调用方法A。
  3. UserServiceImpl类实现UserService接口的方法。
  4. 在主程序里,首先创建一个UserServiceImpl类对象,再用 newProxyInstance方法创建UserService类对象,这就是一个代理。方法的参数有:UserService的类加载器、UserService的接口和一个UserServiceProxy类对象。
  5. userService即可调用方法 A,实现我们想要的逻辑
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class UserServiceProxy implements InvocationHandler {

    private Object target;

    public UserServiceProxy(Object target) {
        this.target = target;
    }

    @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;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy(userService);

        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                proxy);

        userServiceProxy.addUser();
    }
}

vs静态代理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Car car = new Car();
InvocationHandler carHandler = new DynamicProxyHandler(car);//根据传入的对象来动态生成代理类
Vehicle carProxy = (Vehicle) Proxy.newProxyInstance(
        car.getClass().getClassLoader(),
        car.getClass().getInterfaces(),
        carHandler);
carProxy.drive();

Motorcycle motorcycle = new Motorcycle();
InvocationHandler motorcycleHandler = new DynamicProxyHandler(motorcycle);//根据传入的对象来动态生成代理类
Vehicle motorcycleProxy = (Vehicle) Proxy.newProxyInstance(
        motorcycle.getClass().getClassLoader(),
        motorcycle.getClass().getInterfaces(),
        motorcycleHandler);
motorcycleProxy.drive();

静态代理需要分别为 Vehicle 接口分别实现一个CarProxy代理类和一个Motorcycle 代理类,里面分别传入一个 Car 和 Motor对象才行。

CGLib Proxy

Spring框架在选择使用哪种代理方式时,如果有接口就用JDK Proxy,没有接口就用CGLib。
比如有个没有实现接口的类,想给它的方法添加一些功能,就可以用 CGLIB。

  1. 定义实现类
  2. 定义一个Interceptor类,实现Method Interceptor接口,里面实现我们想要被代理的逻辑。
  3. 用 Enhancer 来设置它的父类为目标类,然后设置回调,也就是方法拦截器。这样 CGLIB 就能在运行时动态生成目标类的子类作为代理类。
场景JDK ProxyCGLib Proxy
代理生成速度较快(直接生成接口代理类)较慢(需生成子类并增强字节码)
方法调用速度较慢(反射调用)较快(直接调用子类方法)
内存占用低(代理类轻量)较高(生成子类占用更多元空间)
 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 UserService {
    public void save() {
        System.out.println("保存用户");
    }
}

// 方法拦截器
public class MyMethodInterceptor implements MethodInterceptor {
    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;
    }
}

// 创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MyMethodInterceptor());
UserService proxy = (UserService) enhancer.create();
proxy.save();