切面代理工具 - ProxyUtil
# 切面代理工具 - ProxyUtil
# 介绍
ProxyUtil
是 Hutool 中提供的切面代理工具,支持基于 JDK 动态代理和 Cglib 的切面实现。它能够帮助开发者在不侵入业务代码的情况下,为方法添加通用功能,如日志记录、性能监控等。无论目标类是否实现接口,ProxyUtil
都能够为其提供灵活的切面代理支持。
# 使用示例
# 1. 使用 JDK 的动态代理实现切面
JDK 动态代理要求被代理的类必须实现接口。以下示例展示了如何使用 JDK 动态代理为一个实现了接口的类添加切面功能:
- 定义一个接口:
/**
* 定义一个动物接口,包含一个吃的行为
*/
public interface Animal {
void eat();
}
1
2
3
4
5
6
2
3
4
5
6
- 定义接口的实现类:
/**
* 猫类实现了 Animal 接口,表示猫会吃鱼
*/
public class Cat implements Animal {
@Override
public void eat() {
// 打印猫的行为
System.out.println("猫吃鱼");
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- 使用
TimeIntervalAspect
切面代理上述对象,统计猫吃鱼的执行时间:
import cn.hutool.aop.ProxyUtil;
import cn.hutool.aop.aspects.TimeIntervalAspect;
public class AopExample {
public static void main(String[] args) {
// 使用 ProxyUtil 创建代理对象
Animal cat = ProxyUtil.proxy(new Cat(), TimeIntervalAspect.class);
cat.eat();
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
TimeIntervalAspect
是 Hutool 提供的一个简单切面,继承自SimpleAspect
,用于统计方法执行时间:
import cn.hutool.aop.aspects.SimpleAspect;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.lang.Console;
import java.lang.reflect.Method;
/**
* 统计方法执行时间的切面
*/
public class TimeIntervalAspect extends SimpleAspect {
// Hutool 提供的计时器,用于记录时间
private TimeInterval interval = new TimeInterval();
@Override
public boolean before(Object target, Method method, Object[] args) {
// 方法执行前启动计时器
interval.start();
return true;
}
@Override
public boolean after(Object target, Method method, Object[] args) {
// 方法执行后打印执行时间
Console.log("Method [{}] execute spend [{}]ms", method.getName(), interval.intervalMs());
return true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
执行结果:
猫吃鱼
Method [eat] execute spend [16]ms
1
2
2
说明:由于 JDK 动态代理的机制,返回的代理对象类型是接口类型(如 Animal
),如果直接将其转为实现类(如 Cat
),会导致类型转换异常。因此代理对象应按接口类型接收。
# 2. 使用 Cglib 实现切面
与 JDK 动态代理不同,Cglib 可以对没有实现接口的类进行代理。以下示例展示了如何使用 Cglib 为无接口类添加切面功能:
- 引入 Cglib 依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
1
2
3
4
5
2
3
4
5
- 定义一个普通类:
/**
* 狗类没有实现任何接口,表示狗会吃肉
*/
public class Dog {
public void eat() {
System.out.println("狗吃肉");
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 使用
TimeIntervalAspect
进行切面代理:
import cn.hutool.aop.ProxyUtil;
import cn.hutool.aop.aspects.TimeIntervalAspect;
public class CglibAopExample {
public static void main(String[] args) {
// 使用 ProxyUtil 创建代理对象
Dog dog = ProxyUtil.proxy(new Dog(), TimeIntervalAspect.class);
dog.eat();
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
执行结果:
狗吃肉
Method [eat] execute spend [13]ms
1
2
2
说明:Cglib 代理不要求目标类实现接口,因此适用范围更广,使用方式与 JDK 动态代理类似。
# 其它方法
ProxyUtil
提供了更多便捷的方法封装,例如:
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
:封装了Proxy.newProxyInstance
方法,提供泛型返回值,支持更多参数类型。proxy(Object target, Class<? extends SimpleAspect> aspectClass)
:自动根据目标类选择合适的代理方式(JDK 或 Cglib)。
这些方法简化了代理对象的创建过程,使代码更加简洁。
# 原理解析
动态代理的核心原理是通过生成代理类(如 $Proxy0
)来实现方法拦截:
- 根据传入的接口动态生成一个代理类,代理类实现了接口中的方法。
- 将生成的代理类加载到 JVM 中(即
load $Proxy0
类)。 - 代理类通过构造函数接受
InvocationHandler
对象,并在方法调用时通过反射执行被代理对象的方法。 - 返回代理对象,并在方法调用时进行切面增强。
# 实际应用场景
- 日志记录:为方法添加统一的日志记录逻辑。
- 性能监控:统计方法执行时间,定位性能瓶颈。
- 事务管理:在方法执行前后添加事务控制逻辑。
通过 ProxyUtil
,开发者可以轻松实现这些通用功能,而无需侵入业务代码,极大提升了代码的可维护性和灵活性。
# 总结
ProxyUtil
通过封装 JDK 动态代理和 Cglib,提供了统一的切面代理实现,使开发者能够在不修改现有业务逻辑的情况下,为方法添加通用功能。无论目标类是否实现接口,ProxyUtil
都能够自动选择合适的代理方式,极大简化了切面代理的使用。
如需进一步了解,请参考 Hutool 的官方文档及示例。
编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08