JDK代理工具-JdkProxyUtil
# 1. 功能概述
JdkProxyUtil 是 Hutool 提供的 JDK 动态代理工具类,封装了 Java 原生 Proxy 类的功能,提供了创建动态代理对象和判断代理类型的便捷方法。该工具类简化了 JDK 动态代理的使用,同时扩展了代理类型的判断功能,支持 JDK 代理和 Cglib 代理的识别。
# 2. 核心功能
# 2.1 动态代理对象创建
- newProxyInstance(classloader, invocationHandler, interfaces):创建动态代理对象,指定类加载器、调用处理器和接口
- newProxyInstance(invocationHandler, interfaces):创建动态代理对象,使用默认类加载器
# 2.2 代理对象判断
- isProxy(object):判断对象是否为代理对象(JDK 代理或 Cglib 代理)
- isJdkProxy(object):判断对象是否为 JDK 代理对象
- isCglibProxy(object):判断对象是否为 Cglib 代理对象
# 2.3 代理类判断
- isProxyClass(clazz):判断类是否为代理类(JDK 代理或 Cglib 代理)
- isJdkProxyClass(clazz):判断类是否为 JDK 代理类
- isCglibProxyClass(clazz):判断类是否为 Cglib 代理类
# 3. 方法详解
# 3.1 动态代理对象创建
# newProxyInstance(classloader, invocationHandler, interfaces)
功能:创建动态代理对象
参数:
classloader:被代理类对应的 ClassLoaderinvocationHandler:InvocationHandler 对象,被代理类通过实现此接口提供动态代理功能interfaces:代理类中需要实现的被代理类的接口方法
返回值:代理类实例
说明:动态代理对象的创建原理是:
- 根据传入的 interfaces 动态生成一个类,实现 interfaces 中的接口
- 通过传入的 classloader 将刚生成的类加载到 JVM 中
- 调用生成类的构造函数创建实例,该实例的所有接口方法实现本质上是通过反射调用被代理对象的方法
- 当调用代理类的相应方法时,相当于调用 InvocationHandler.invoke() 方法
示例:
// 定义接口
interface UserService {
void sayHello(String name);
}
// 实现接口
class UserServiceImpl implements UserService {
@Override
public void sayHello(String name) {
Console.log("Hello, {}", name);
}
}
// 创建调用处理器
InvocationHandler handler = (proxy, method, args) -> {
Console.log("Before method: {}", method.getName());
Object result = method.invoke(new UserServiceImpl(), args);
Console.log("After method: {}", method.getName());
return result;
};
// 创建代理对象
UserService proxy = JdkProxyUtil.newProxyInstance(ClassLoader.getSystemClassLoader(), handler, UserService.class);
# newProxyInstance(invocationHandler, interfaces)
功能:创建动态代理对象
参数:
invocationHandler:InvocationHandler 对象,被代理类通过实现此接口提供动态代理功能interfaces:代理类中需要实现的被代理类的接口方法
返回值:代理类实例
说明:使用默认类加载器创建代理对象
示例:
// 创建调用处理器
InvocationHandler handler = (proxy, method, args) -> {
Console.log("Method invoked: {}", method.getName());
return null;
};
// 创建代理对象,使用默认类加载器
UserService proxy = JdkProxyUtil.newProxyInstance(handler, UserService.class);
# 3.2 代理对象判断
# isProxy(object)
功能:判断对象是否为代理对象
参数:
object:被检查的对象
返回值:是否为代理对象(JDK 代理或 Cglib 代理)
示例:
// 创建代理对象
UserService proxy = JdkProxyUtil.newProxyInstance(handler, UserService.class);
// 判断是否为代理对象
boolean isProxy = JdkProxyUtil.isProxy(proxy); // 返回 true
# isJdkProxy(object)
功能:判断对象是否为 JDK 代理对象
参数:
object:被检查的对象
返回值:是否为 JDK 代理对象
示例:
// 创建 JDK 代理对象
UserService jdkProxy = JdkProxyUtil.newProxyInstance(handler, UserService.class);
// 判断是否为 JDK 代理对象
boolean isJdkProxy = JdkProxyUtil.isJdkProxy(jdkProxy); // 返回 true
# isCglibProxy(object)
功能:判断对象是否为 Cglib 代理对象
参数:
object:被检查的对象
返回值:是否为 Cglib 代理对象
示例:
// 假设使用 Cglib 创建了代理对象
UserService cglibProxy = createCglibProxy();
// 判断是否为 Cglib 代理对象
boolean isCglibProxy = JdkProxyUtil.isCglibProxy(cglibProxy); // 返回 true
# 3.3 代理类判断
# isProxyClass(clazz)
功能:判断类是否为代理类
参数:
clazz:被检查的类
返回值:是否为代理类(JDK 代理或 Cglib 代理)
示例:
// 创建代理对象
UserService proxy = JdkProxyUtil.newProxyInstance(handler, UserService.class);
// 获取代理类
Class<?> proxyClass = proxy.getClass();
// 判断是否为代理类
boolean isProxyClass = JdkProxyUtil.isProxyClass(proxyClass); // 返回 true
# isJdkProxyClass(clazz)
功能:判断类是否为 JDK 代理类
参数:
clazz:被检查的类
返回值:是否为 JDK 代理类
示例:
// 创建 JDK 代理对象
UserService jdkProxy = JdkProxyUtil.newProxyInstance(handler, UserService.class);
// 判断是否为 JDK 代理类
boolean isJdkProxyClass = JdkProxyUtil.isJdkProxyClass(jdkProxy.getClass()); // 返回 true
# isCglibProxyClass(clazz)
功能:判断类是否为 Cglib 代理类
参数:
clazz:被检查的类
返回值:是否为 Cglib 代理类
说明:Cglib 代理类的类名通常包含 "$$" 分隔符
示例:
// 假设使用 Cglib 创建了代理对象
UserService cglibProxy = createCglibProxy();
// 判断是否为 Cglib 代理类
boolean isCglibProxyClass = JdkProxyUtil.isCglibProxyClass(cglibProxy.getClass()); // 返回 true
# 4. 示例代码
# 4.1 基本代理使用
// 定义接口
interface Calculator {
int add(int a, int b);
int subtract(int a, int b);
}
// 实现接口
class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public int subtract(int a, int b) {
return a - b;
}
}
// 创建调用处理器,实现日志记录
InvocationHandler loggingHandler = (proxy, method, args) -> {
Console.log("开始执行方法: {}", method.getName());
Console.log("参数: {}", Arrays.toString(args));
// 调用原始方法
Calculator calculator = new CalculatorImpl();
Object result = method.invoke(calculator, args);
Console.log("方法执行结束: {}", method.getName());
Console.log("结果: {}", result);
return result;
};
// 创建代理对象
Calculator proxy = JdkProxyUtil.newProxyInstance(loggingHandler, Calculator.class);
// 使用代理对象
int sum = proxy.add(10, 20); // 输出日志并返回 30
int difference = proxy.subtract(50, 30); // 输出日志并返回 20
# 4.2 代理类型判断
// 创建 JDK 代理对象
Calculator jdkProxy = JdkProxyUtil.newProxyInstance(loggingHandler, Calculator.class);
// 假设创建 Cglib 代理对象
Calculator cglibProxy = createCglibProxy();
// 创建普通对象
Calculator normalCalculator = new CalculatorImpl();
// 判断代理类型
Console.log("jdkProxy 是代理对象吗?{}", JdkProxyUtil.isProxy(jdkProxy)); // true
Console.log("jdkProxy 是 JDK 代理吗?{}", JdkProxyUtil.isJdkProxy(jdkProxy)); // true
Console.log("jdkProxy 是 Cglib 代理吗?{}", JdkProxyUtil.isCglibProxy(jdkProxy)); // false
Console.log("cglibProxy 是代理对象吗?{}", JdkProxyUtil.isProxy(cglibProxy)); // true
Console.log("cglibProxy 是 JDK 代理吗?{}", JdkProxyUtil.isJdkProxy(cglibProxy)); // false
Console.log("cglibProxy 是 Cglib 代理吗?{}", JdkProxyUtil.isCglibProxy(cglibProxy)); // true
Console.log("normalCalculator 是代理对象吗?{}", JdkProxyUtil.isProxy(normalCalculator)); // false
# 4.3 代理类判断
// 创建代理对象
Calculator proxy = JdkProxyUtil.newProxyInstance(loggingHandler, Calculator.class);
// 获取类信息
Class<?> proxyClass = proxy.getClass();
Console.log("代理类名: {}", proxyClass.getName());
Console.log("代理类实现的接口: {}", Arrays.toString(proxyClass.getInterfaces()));
// 判断类类型
Console.log("是代理类吗?{}", JdkProxyUtil.isProxyClass(proxyClass)); // true
Console.log("是 JDK 代理类吗?{}", JdkProxyUtil.isJdkProxyClass(proxyClass)); // true
Console.log("是 Cglib 代理类吗?{}", JdkProxyUtil.isCglibProxyClass(proxyClass)); // false
// 判断普通类
Class<?> normalClass = CalculatorImpl.class;
Console.log("普通类是代理类吗?{}", JdkProxyUtil.isProxyClass(normalClass)); // false
# 5. 注意事项
- 接口要求:JDK 动态代理只能代理实现了接口的类,不能代理普通类
- 类加载器:创建代理对象时可以指定类加载器,默认使用当前线程的类加载器
- InvocationHandler:必须实现 InvocationHandler 接口,代理对象的所有方法调用都会转发到该接口的 invoke 方法
- 代理对象类型:生成的代理对象是实现了指定接口的动态类实例
- Cglib 代理识别:通过类名是否包含 "$$" 分隔符来判断是否为 Cglib 代理
- 空指针安全:所有判断方法都进行了空指针检查,避免了空指针异常
- 性能考虑:动态代理会带来一定的性能开销,因为每次方法调用都需要通过反射实现
# 6. 最佳实践
- 日志记录:使用动态代理实现统一的日志记录,无需修改原有代码
- 权限控制:在代理中实现权限检查,控制方法的访问权限
- 事务管理:在代理中实现事务的开始、提交和回滚
- 缓存处理:对频繁调用的方法结果进行缓存,提高系统性能
- 异常处理:统一处理方法调用过程中抛出的异常
- 代理类型判断:在需要区分代理类型的场景中使用代理判断方法
- 结合 AOP:与 Spring AOP 等框架结合使用,实现更强大的面向切面编程
JdkProxyUtil 工具类简化了 JDK 动态代理的使用,同时提供了代理类型判断的功能,是 Java 反射和 AOP 编程中的重要工具。通过合理使用动态代理,可以实现代码的解耦和功能的增强,提高系统的可维护性和扩展性。