切面代理工具-ProxyUtil

使用

使用JDK的动态代理实现切面

  • 我们定义一个接接口:
public interface Animal{
    void eat();
}
  • 定义一个实现类:
public class Cat implements Animal{
    @Override
    public void eat() {
        Console.log("猫吃鱼");
    }
}
  • 我们使用TimeIntervalAspect这个切面代理上述对象,来统计猫吃鱼的执行时间:
Animal cat = ProxyUtil.proxy(new Cat(), TimeIntervalAspect.class);
cat.eat();

TimeIntervalAspect位于cn.hutool.aop.aspects包,继承自SimpleAspect,代码如下:

public class TimeIntervalAspect extends SimpleAspect{
    //TimeInterval为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", target.getClass().getName(), method.getName(), interval.intervalMs());
        return true;
    }
}

执行结果为:

猫吃鱼
Method [cn.hutool.aop.test.AopTest$Cat.eat] execute spend [16]ms

在调用proxy方法后,IDE自动补全返回对象为Cat,因为JDK机制的原因,我们的返回值必须是被代理类实现的接口,因此需要手动将返回值改为Animal,否则会报类型转换失败。

使用Cglib实现切面

使用Cglib的好处是无需定义接口即可对对象直接实现切面,使用方式完全一致:

  • 引入Cglib依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.7</version>
</dependency>
  • 定义一个无接口类(此类有无接口都可以)
public class Dog {
    public String eat() {
        Console.log("狗吃肉");
    }
}
Dog dog = ProxyUtil.proxy(new Dog(), TimeIntervalAspect.class);
String result = dog.eat();

执行结果为:

狗吃肉
Method [cn.hutool.aop.test.AopTest$Dog.eat] execute spend [13]ms

其它方法

ProxyUtil中还提供了一些便捷的Proxy方法封装,例如newProxyInstance封装了Proxy.newProxyInstance方法,提供泛型返回值,并提供更多参数类型支持。

原理

动态代理对象的创建原理是假设创建的代理对象名为 $Proxy0:

  • 根据传入的interfaces动态生成一个类,实现interfaces中的接口
  • 通过传入的classloder将刚生成的类加载到jvm中。即将$Proxy0类load
  • 调用$Proxy0的$Proxy0(InvocationHandler)构造函数 创建$Proxy0的对象,并且用interfaces参数遍历其所有接口的方法,并生成实现方法,这些实现方法的实现本质上是通过反射调用被代理对象的方法。
  • 将$Proxy0的实例返回给客户端。
  • 当调用代理类的相应方法时,相当于调用 InvocationHandler.invoke(Object, Method, Object []) 方法。

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「方志朋」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

results matching ""

    No results matching ""