java动态代理详细介绍
1.动态代理概述
动态代理(Dynamic Proxy)是指在运行时动态地生成代理对象的技术,通常用于为目标对象提供额外的功能,比如日志、事务管理、权限控制等。与静态代理不同,静态代理在编译时已经确定代理类,而动态代理则是在程序运行时动态生成代理类,能够有效减轻代码的重复性。
动态代理的主要作用:
- 增强功能:可以为目标对象添加附加功能(例如日志记录、性能统计、安全控制等)。
- 解耦:使得业务逻辑和增强功能分离,增加代码的可维护性。
- 灵活性:代理类可以在运行时动态生成,不需要手动创建。
什么叫运行期动态创建?听起来好像很复杂。所谓动态代理,是和静态相对应的。我们来看静态代码怎么写:
- 定义接口
public interface Hello {
void morning(String name);
}
- 编写实现类
public class HelloImpl implements Hello {
public void morning(String name) {
System.out.println("Good morning, " + name);
}
}
- 创建实例,转型为接口并调用
Hello hello = new HelloImpl();
hello.morning("Bob");
这种方式就是我们通常编写代码的方式。
还有一种方式是动态代码,我们仍然先定义了接口Hello,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。
这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。
一个最简单的动态代理实现如下:
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);
}
在运行期动态创建一个interface实例的方法如下:
- 定义一个InvocationHandler实例,它负责实现接口的方法调用;
- 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
- 使用的ClassLoader,通常就是接口类的ClassLoader;
- 需要实现的接口数组,至少需要传入一个接口进去;
- 用来处理接口方法调用的InvocationHandler实例。
- 将返回的Object强制转型为接口。
动态代理实际上是JVM在运行期动态创建class字节码并加载的过程,把上面的动态代理改写为静态实现类大概就是下面Java中的动态代理的实现。
2.Java中的动态代理
Java 提供了两种方式来实现动态代理:
- JDK 动态代理(基于接口)
- CGLIB 动态代理(基于继承)
2.1 JDK 动态代理
JDK 动态代理是通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现的。使用 JDK 动态代理时,代理对象需要实现至少一个接口。代理对象会在运行时根据接口生成。
主要类和接口:
- java.lang.reflect.Proxy:用于创建代理实例。
- java.lang.reflect.InvocationHandler:处理被代理对象方法调用的接口。
JDK 动态代理示例
2.1.1 定义接口
public interface UserService {
void addUser(String userName);
void deleteUser(String userName);
}
2.1.2 定义接口实现类
public class UserServiceImpl implements UserService {
@Override
public void addUser(String userName) {
System.out.println("User " + userName + " added.");
}
@Override
public void deleteUser(String userName) {
System.out.println("User " + userName + " deleted.");
}
}
2.1.3 创建 InvocationHandler 实现类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class UserServiceInvocationHandler implements InvocationHandler {
private Object target;
public UserServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Method " + method.getName() + " is invoked with arguments: " + args[0]);
// 调用目标对象的方法
Object result = method.invoke(target, args);
System.out.println("Method " + method.getName() + " executed.");
return result;
}
}
2.1.4 创建代理对象并测试
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserServiceImpl();
// 创建 InvocationHandler
UserServiceInvocationHandler handler = new UserServiceInvocationHandler(userService);
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
handler);
// 调用代理对象的方法
proxy.addUser("John");
proxy.deleteUser("John");
}
}
解释:
- 目标对象:UserServiceImpl ,它实现了 UserService 接口,实际的业务逻辑类。
- InvocationHandler:UserServiceInvocationHandler 是对目标对象的代理处理类。它会拦截对目标对象方法的调用,在方法执行前后添加自己的逻辑。
- 代理对象:通过 Proxy.newProxyInstance 动态创建的代理对象。该代理对象会通过 InvocationHandler 来执行方法。
- 方法执行:通过代理对象调用目标对象的方法时,实际上是由 InvocationHandler 来处理,代理对象在方法调用前后可以执行额外的操作。
2.2 CGLIB 动态代理
CGLIB 动态代理通过字节码技术,在运行时生成一个目标类的子类,并重写其中的方法。CGLIB 代理不要求目标类实现接口,因此它适用于没有接口的类。
主要类:
- net.sf.cglib.proxy.Enhancer:用于创建代理类。
- net.sf.cglib.proxy.MethodInterceptor:用于拦截目标对象的方法调用。
CGLIB 动态代理示例
引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version> <!-- 你可以根据需要选择其他版本 -->
</dependency>
2.2.1 定义目标类
public class UserService {
public void addUser(String userName) {
System.out.println("User " + userName + " added.");
}
public void deleteUser(String userName) {
System.out.println("User " + userName + " deleted.");
}
}
2.2.2 创建 MethodInterceptor 实现类
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class UserServiceInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Method " + method.getName() + " is invoked with arguments: " + args[0]);
// 调用目标对象的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("Method " + method.getName() + " executed.");
return result;
}
}
2.2.3 创建代理对象并测试
import net.sf.cglib.proxy.Enhancer;
public class ProxyTest {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserService();
// 创建 MethodInterceptor
UserServiceInterceptor interceptor = new UserServiceInterceptor();
// 创建 CGLIB 代理对象
UserService proxy = (UserService) Enhancer.create(UserService.class, interceptor);
// 调用代理对象的方法
proxy.addUser("John");
proxy.deleteUser("John");
}
}
解释:
- 目标对象:UserService 类,定义了业务方法。
- MethodInterceptor:UserServiceInterceptor 实现了 MethodInterceptor 接口,用于在方法执行前后添加自定义逻辑。
- 代理对象:Enhancer.create 创建了 UserService 类的代理对象,代理对象会通过 MethodInterceptor 来处理方法调用。
2.3 主要区别
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
代理对象类型 | 必须是接口类型 | 生成目标类的子类 |
适用场景 | 适用于目标类实现了接口的情况 | 适用于目标类没有实现接口的情况 |
性能 | 相比 CGLIB 更快,且不需要依赖第三方库 | 生成子类,性能相对较慢,且需要依赖 CGLIB 库 |
使用限制 | 只能代理接口类型 | 可以代理任何类(不需要接口) |
生成的代理类 | 动态生成实现接口的类 | 动态生成目标类的子类 |