动态代理
约 779 字大约 3 分钟
2025-07-15
6.1 class
和 interface
的区别
class
(非abstract
) 可以被实例化。interface
不能被实例化。
所有 interface
类型的变量总是通过某个实例向上转型并赋值给接口类型变量,例如:
CharSequence cs = new StringBuilder();
6.2 动态代理的概念
动态代理允许在运行期动态创建某个 interface
的实例,而无需预先编写实现类。
6.3 静态 vs 动态
静态方式: 传统的编写代码方式,先定义接口,然后编写实现类,最后创建实例。
// 定义接口 public interface Hello { void morning(String name); } // 编写实现类 public class HelloWorld implements Hello { public void morning(String name) { System.out.println("Good morning, " + name); } } // 创建实例 Hello hello = new HelloWorld(); hello.morning("Bob");
动态方式: 定义接口,但不编写实现类,而是通过
Proxy.newProxyInstance()
在运行期动态创建接口对象。
6.4 动态代理的实现
以下是一个简单的动态代理实现示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}
interface Hello {
void morning(String name);
}
代码解释:
这段代码展示了如何使用 java.lang.reflect.Proxy
类来创建一个动态代理对象。Proxy.newProxyInstance()
方法接收三个参数:
- ClassLoader: 用于加载代理类的类加载器。通常使用接口类的类加载器。
- Interfaces: 代理类需要实现的接口列表。
- InvocationHandler: 一个实现了
InvocationHandler
接口的对象,它负责处理代理对象上的方法调用。
在 InvocationHandler
的 invoke()
方法中,可以拦截并处理接口方法的调用。在本例中,当调用 hello.morning("Bob")
时,invoke()
方法会被调用,打印方法信息,然后根据方法名执行相应的逻辑。 这个例子中,InvocationHandler
实现了对 Hello
接口中 morning
方法的拦截和处理,可以在方法调用前后加入额外的逻辑,这是动态代理的核心。
创建 interface
实例的步骤:
- 定义一个
InvocationHandler
实例,负责实现接口的方法调用。 - 通过
Proxy.newProxyInstance()
创建interface
实例,需要三个参数:- 使用的
ClassLoader
,通常是接口类的ClassLoader
。 - 需要实现的接口数组,至少需要传入一个接口。
- 用来处理接口方法调用的
InvocationHandler
实例。
- 使用的
- 将返回的
Object
强制转型为接口。
6.5 动态代理的本质
动态代理实际上是 JVM 在运行期动态创建 class 字节码并加载的过程。
例如,上述动态代理的静态实现类大致如下:
public class HelloDynamicProxy implements Hello {
InvocationHandler handler;
public HelloDynamicProxy(InvocationHandler handler) {
this.handler = handler;
}
public void morning(String name) {
try {
handler.invoke(
this,
Hello.class.getMethod("morning", String.class),
new Object[] { name }
);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
JVM 帮我们自动编写了一个上述类(不需要源码,可以直接生成字节码),所以并不存在可以直接实例化接口的“黑魔法”。