Java 的 动态代理入门

Java 的 动态代理入门

Java的动态代理是一种在运行时动态生成代理对象的机制,可以在不修改源代码的情况下,为类和接口创建代理对象,并在代理对象上实现一些自定义的逻辑,例如日志记录、安全控制、性能监控等。Java的动态代理通过反射机制实现,在运行时动态生成代理类,从而实现代理功能。

JDK动态代理主要涉及以下两个类:

  1. java.lang.reflect.InvocationHandler:定义了一个代理对象要执行的操作,即代理逻辑。该接口只有一个方法invoke,在代理对象调用方法时,会自动回调该方法。
  2. java.lang.reflect.Proxy:提供了创建动态代理对象的方法。该类有一个静态方法newProxyInstance,该方法接收一个ClassLoader对象、一组接口和一个InvocationHandler对象作为参数,返回一个代理对象。

在使用Java动态代理时,需要先创建一个实现了InvocationHandler接口的类,在该类中实现代理逻辑,然后使用Proxy类的newProxyInstance方法创建代理对象。

动态代理有许多应用场景,例如AOP编程、RPC调用、对象持久化等。相比于静态代理,动态代理具有更好的灵活性和可扩展性,同时也减少了代码的重复性。

实例

  1. 创建接口
1
2
3
4
public interface SmsService {
String send(String message);
String receive(String message);
}
  1. 实现接口
1
2
3
4
5
6
7
8
9
10
11
12
13
public class SmsServiceImpl implements SmsService{
@Override
public String send(String message) {
System.out.println("message :" + message);
return message;
}

@Override
public String receive(String message) {
System.out.println("receive :" + message);
return message;
}
}
  1. 定义一个JDK动态代理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DebugInvocationHandler implements InvocationHandler {

private final Object target;

public DebugInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//当前调用的方法名
System.out.println(method);
//调用前添加的操作
System.out.println("before method "+method.getName());
//当我们的动态代理对象调用原生方法的时候,实际上是调用到了invoke方法,然后invoke方法代替我们去调用了被代理对象的原生方法
Object result = method.invoke(target,args);
//调用后添加的操作
System.out.println("after method "+method.getName());
return result;
}
}
  1. 获取代理对象的工厂类
1
2
3
4
5
6
7
8
9
10
public class JdkProxyFactory {
public static Object getProxy(Object target) {
//通过Proxy.newProxyInstance()获取到某个类的代理对象
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),//目标类的类加载
target.getClass().getInterfaces(),//代理需要实现的接口
new DebugInvocationHandler(target)//代理对象对应的自定义InvocationHandle
);
}
}
  1. 使用
1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
//创建代理对象后,我们调用的方法先被转发到对应InvocationHandler中的invoke方法,再由invoke方法调用原生方法
smsService.send("Java");
smsService.receive("Java");
}
}

CGLIB

静态代理和动态代理的对比

灵活性:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!

JVM 层面 :静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

参考

07 静态代理+JDK/CGLIB 动态代理实战 (yuque.com)