动态代理学习
动态代理学习
Java SDK实现动态代理
Demo
由于学习的开源项目中用到了动态代理,所以在使用前先学习下理论基础
在Claude 3.5的帮助下,简单学习了下动态代理的使用及原理
随后,动手写了个 DynamicProxyDemo.class,加深学习的印象
package Reflect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyDemo {
public static void main(String[] args) {
Hello hello = (Hello) Proxy.newProxyInstance(
// 指定 新生成代理类 使用的类加载器
Hello.class.getClassLoader(),
// 新生成代理类实现的接口类型
new Class<?>[] {Hello.class},
// 自定义调用处理器
// 当新生成的代理对象的方法被调用时,会触发自定义处理器的 invoke 方法,
// 方法调用的相关信息(如方法对象、参数等)会作为参数传入自定义调用处理器的 invoke 方法
// 自定义调用处理器的 invoke 方法中,调用 HelloImpl 的实现方法,并且可以在调用前后执行一些自定义操作
new MyInvocationHandler(new HelloImpl())
);
hello.sayHello();
}
}
/**
* 接口
*/
interface Hello{
void sayHello();
}
/**
* 接口实现类,被代理对象
*/
class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello World");
}
}
class MyInvocationHandler implements InvocationHandler {
/**
* 被代理对象
*/
private Object target;
/**
* 构造函数 传入被代理对象
* @param target
*/
public MyInvocationHandler(Object target) {
this.target = target;
}
/**
* 对 被代理对象 的调用方法增强
*
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法调用前,执行xxx
System.out.println(method.getName() + "方法调用前");
// 被代理对象 方法调用
Object result = method.invoke(target, args);
// 方法调用结束后,执行xxx
System.out.println(method.getName() + "方法执行完成");
return result;
}
}
问题,Answered By Claude
提示
注:回答可能存在错误,请仔细甄别!
问题一
请帮助我理解下java的动态代理
好的,我很乐意为您解释Java的动态代理。
Java的动态代理是一种在运行时创建代理类的机制,它允许我们在不修改原始类的情况下,为对象添加额外的行为。这是Java反射API的一个强大特性。
以下是动态代理的主要概念和步骤:
接口: 首先需要定义一个接口,代理类和被代理类都会实现这个接口。
被代理类: 实现上述接口的实际类。
InvocationHandler: 这是一个接口,定义了代理对象的行为。它有一个invoke方法,在这个方法中我们可以定义额外的逻辑。
Proxy.newProxyInstance(): 这个静态方法用于创建代理对象。
下面我会用一个简单的例子来说明:
// 定义接口
interface HelloInterface {
void sayHello();
}
// 被代理类
class HelloImpl implements HelloInterface {
@Override
public void sayHello() {
System.out.println("Hello, World!");
}
}
// InvocationHandler实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method " + method.getName());
return result;
}
}
// 主类
public class DynamicProxyDemo {
public static void main(String[] args) {
HelloInterface hello = new HelloImpl();
MyInvocationHandler handler = new MyInvocationHandler(hello);
HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(
HelloInterface.class.getClassLoader(),
new Class<?>[]{HelloInterface.class},
handler);
proxyHello.sayHello();
}
}
这个例子展示了Java动态代理的基本用法:
我们定义了一个
HelloInterface
接口和它的实现类HelloImpl
。MyInvocationHandler
类实现了InvocationHandler
接口。在它的invoke
方法中,我们在调用实际方法前后添加了额外的逻辑。在
main
方法中,我们使用Proxy.newProxyInstance()
创建了一个代理对象。这个方法需要三个参数:类加载器、要实现的接口数组,以及InvocationHandler
实例。最后,我们调用代理对象的
sayHello
方法。这会触发InvocationHandler
的invoke
方法,从而在实际方法调用前后执行额外的逻辑。
运行这段代码,你会看到输出:
Before method sayHello
Hello, World!
After method sayHello
这就是动态代理的工作原理。它允许我们在不修改原始类的情况下,动态地为对象添加行为。这在很多场景下非常有用,比如AOP(面向切面编程)、日志记录、事务管理等。
您对这个解释还有什么疑问吗?或者您想了解动态代理的更多细节?
问题二
那你能否深入底层帮我讲解下动态代理的原理吗
当然,我很乐意为您深入讲解Java动态代理的底层原理。
Java动态代理的实现主要依赖于 java.lang.reflect.Proxy
类和 java.lang.reflect.InvocationHandler
接口。让我们逐步深入了解其工作原理:
- 代理类的生成
当我们调用 Proxy.newProxyInstance()
方法时,Java会动态地在内存中生成一个新的类。这个类大致如下:
public final class $Proxy0 extends Proxy implements HelloInterface {
private static Method m1, m2, m3, ... // 接口中的方法
public $Proxy0(InvocationHandler h) {
super(h);
}
public void sayHello() {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
// 其他方法...
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("HelloInterface").getMethod("sayHello");
// 其他方法...
} catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
} catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
}
- 字节码生成
Java使用 sun.misc.ProxyGenerator
类来生成上述类的字节码。这个过程包括:
- 生成类声明
- 添加字段来存储方法对象
- 生成构造函数
- 为接口中的每个方法生成代理方法
- 生成静态初始化块来初始化方法对象
- 类加载
生成的字节码会被传递给 java.lang.ClassLoader
,它会将这个新生成的类加载到 JVM 中。
- 实例化
最后,newProxyInstance
方法会使用 Java 反射 API 来实例化这个新生成的代理类。
- 方法调用
当我们调用代理对象的方法时,实际上是调用了 $Proxy0
类中对应的方法。这个方法会将调用转发给 InvocationHandler
的 invoke
方法。
- 性能考虑
动态代理的性能开销主要在于:
- 生成代理类的过程(只在第一次创建代理对象时发生)
- 反射调用的开销(每次方法调用都会发生)
然而,现代 JVM 对反射调用有很多优化,如方法内联等,所以在大多数情况下,性能影响并不显著。
- 局限性
Java 动态代理只能代理接口,不能代理类。这是因为生成的代理类已经继承了 Proxy
类,而 Java 不支持多重继承。如果需要代理类,可以使用字节码操作库如 CGLIB。
- 应用场景
动态代理广泛应用于各种框架中,如:
- Spring AOP
- Hibernate 的懒加载
- RPC 框架中的远程调用
- 各种中间件进行拦截和增强
这就是 Java 动态代理的底层工作原理。它利用了 Java 反射和动态类生成的能力,在运行时创建新的类来实现代理模式。
您对这个深入解释还有什么问题吗?或者您想了解某个特定方面的更多细节?