java代理
静态代理:
步骤:
- 定义接口。
- 编写接口的实现类(目标类)。
- 编写代理类,实现接口并持有目标类的引用。
- 在代理类中,通过目标类的引用调用实际的方法。
实例:
// 定义接口
public interface Hello {
void morning(String name);
}
// 目标类
public class HelloWorld implements Hello {
@Override
public void morning(String name) {
System.out.println("Good morning, " + name);
}
}
// 代理类
public class HelloProxy implements Hello {
// 内部维护一个目标代理对象的属性字段
private Hello target;
public HelloProxy(Hello target) {
this.target = target;
}
@Override
public void morning(String name) {
System.out.println("Before method invoke...");
target.morning(name); // 调用目标类方法
System.out.println("After method invoke...");
}
}
// 测试静态代理
public class Main {
public static void main(String[] args) {
Hello target = new HelloWorld(); // 创建目标对象
Hello proxy = new HelloProxy(target); // 创建代理对象
proxy.morning("Bob"); // 调用代理方法
}
}
动态代理:
步骤:
- 定义接口。
- 创建
InvocationHandler接口的实现类,用来处理方法调用。 - 使用
Proxy.newProxyInstance()生成动态代理对象。
实例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义接口
public interface Hello {
void morning(String name);
}
// 测试动态代理
public class Main {
public static void main(String[] args) {
// 创建InvocationHandler
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method invoke...");
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
System.out.println("After method invoke...");
return null;
}
};
// 创建动态代理对象
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 类加载器
new Class[] { Hello.class }, // 需要实现的接口
handler // 方法调用处理器
);
// 调用代理方法
hello.morning("Bob");
}
}
动态代理本质上是JVM在运行时生成代理类的字节码,并加载到内存中。
附带一个invoke的实现:
// 这是 JDK 自动生成的 $Proxy0 类的大致源码
public final class $Proxy0 extends Proxy implements Hello {
// 保存 handler 的变量
private InvocationHandler h;
// 保存 morning 方法的 Method 对象 (静态初始化时获取)
private static Method m1;
static {
// 初始化时,通过反射获取 Hello.morning 方法的元数据
m1 = Class.forName("Hello").getMethod("morning", String.class);
}
public $Proxy0(InvocationHandler h) {
this.h = h;
}
// 你调用的 morning 方法实际上执行的是这段代码!
public final void morning(String name) {
try {
// 关键在这里:它自动调用了 handler.invoke()
// 它把 method 对象、参数数组打包传进去
h.invoke(this, m1, new Object[]{name});
return;
} catch (RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
对比:
| 特点 | 静态代理 | 动态代理 |
|---|---|---|
| 代理类实现方式 | 编译时定义代理类 | 运行时动态生成代理类 |
| 对接口的支持 | 每个接口需要单独实现代理类 | 可通用,只需实现InvocationHandler |
| 代码扩展性 | 低,新增接口时需增加代理类 | 高,只需更改代理逻辑即可 |
| 性能开销 | 无需反射,性能较高 | 依赖反射调用,性能略低 |
| 应用场景 | 小型项目或接口稳定的场景 | 大型项目、动态扩展功能的场景 |