Java动态代理(Dynamic Proxy)是一种运行时生成代理类的技术,可以在不改变原始类代码的情况下,为其创建一个代理对象,对方法调用进行拦截和增强。
动态代理的实现分为两种方式:基于接口的动态代理和基于类的动态代理。其中,基于接口的动态代理可以为任何实现了接口的类生成代理对象,而基于类的动态代理只能为继承了指定类的子类生成代理对象。
Java动态代理通常由以下三个部分组成:
- 接口或父类:被代理对象必须实现的接口或继承的父类。
- InvocationHandler(调用处理器):实现InvocationHandler接口,在invoke方法中编写拦截和增强逻辑。
- Proxy(代理类):使用Proxy类中的静态方法创建代理对象,并将被代理对象和InvocationHandler传递给它。
动态代理的优点是可以在运行时动态地为目标对象添加功能,同时也可以减少重复代码的编写。经常用于AOP编程、RPC框架和事务管理等方面。
下面是一个简单的基于接口的动态代理代码演示:
假设我们有一个接口 HelloService
,其中定义了一个方法 sayHello()
。我们想要为 HelloService
创建一个代理对象,并在执行 sayHello()
方法时添加一些额外的逻辑。
首先,我们需要编写一个实现了 InvocationHandler
接口的调用处理器类 MyInvocationHandler
,并在其中实现拦截和增强逻辑。例如,以下是一个简单的调用处理器类,它将在方法调用前后记录日志信息:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public 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;
}
}
在这里,target
是被代理的目标对象,我们将其传递给构造函数中,然后在 invoke()
方法中使用反射机制调用目标对象的方法,并在方法调用前后输出日志信息。
接下来,在客户端代码中创建一个代理对象。我们可以使用 Proxy.newProxyInstance()
方法来创建代理对象。该方法需要三个参数:
- 类加载器(ClassLoader):用于加载动态生成的代理类。
- 接口数组(Class<?>[]):被代理的接口集合。
- 调用处理器(InvocationHandler):用于拦截和增强方法调用的逻辑。
例如,以下是一个简单的客户端代码,它使用上述调用处理器类创建代理对象:
public class Main {
public static void main(String[] args) {
// 创建目标对象
HelloService helloService = new HelloServiceImpl();
// 创建调用处理器对象
MyInvocationHandler handler = new MyInvocationHandler(helloService);
// 创建代理对象
HelloService proxy = (HelloService) Proxy.newProxyInstance(
helloService.getClass().getClassLoader(),
helloService.getClass().getInterfaces(),
handler);
// 调用代理对象的方法
proxy.sayHello();
}
}
在这里,我们创建了一个 HelloServiceImpl
对象作为被代理对象,并将其传递给调用处理器 MyInvocationHandler
中。然后,使用 Proxy.newProxyInstance()
方法为该对象创建代理对象,并将其转换为 HelloService
类型。最后,我们调用代理对象的方法 sayHello()
。
当我们运行上述代码时,将会输出以下日志信息:
Before method sayHello
Hello, world!
After method sayHello
从中可以看出,在调用 sayHello()
方法之前和之后,代理对象会执行拦截和增强逻辑,即输出日志信息。