反射
反射是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
2
3
| Field nameField = personClass.getDeclaredField("name");
nameField.setAccessible(true); // 如果字段是 private,需要设置可访问性
System.out.println("Name before: " + nameField.get(person));
|
动态代理,提高代码复用性
Spring框架
泛型、反射和类型推断的关系
动态代理
优点:动态生成代理类,不需要手动定义多个代理类。
- 定义一个接口UserService,里面定义我想被代理的方法A
- 创建一个代理类UserServiceProxy,这个类implements InvocationHandler这个接口,也就是重写invoke()这个方法,具体是在调用方法A之前或之后加上我想要的逻辑。invoke里面通过反射调用方法A。
- UserServiceImpl类实现UserService接口的方法。
- 在主程序里,首先创建一个UserServiceImpl类对象,再用 newProxyInstance方法创建UserService类对象,这就是一个代理。方法的参数有:UserService的类加载器、UserService的接口和一个UserServiceProxy类对象。
- 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。
- 定义实现类
- 定义一个Interceptor类,实现Method Interceptor接口,里面实现我们想要被代理的逻辑。
- 用 Enhancer 来设置它的父类为目标类,然后设置回调,也就是方法拦截器。这样 CGLIB 就能在运行时动态生成目标类的子类作为代理类。
场景 | JDK Proxy | CGLib 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();
|