2【hutool】hutool-aop

601 阅读2分钟

该系列文章主要是对 hutool 工具类的介绍,详情可以参考

hutool.cn/docs/#/

2 hutool-aop

AOP模块主要针对JDK中动态代理进行封装,抽象动态代理为切面类Aspect,通过ProxyUtil代理工具类将切面对象与被代理对象融合,产生一个代理对象,从而可以针对每个方法执行前后做通用的功能。

在aop模块中,默认实现可以下两个切面对象:

  • SimpleAspect 简单切面对象,不做任何操作,继承此对象可重写需要的方法即可,不必实现所有方法
  • TimeIntervalAspect 执行时间切面对象,用于简单计算方法执行时间,然后通过日志打印方法执行时间

2.1 jdk 动态代理实现切面

定义一个接口

public interface Animal{
    void eat();
}

定义一个实现类

public class Cat implements Animal{

    @Override
    public void eat() {
        Console.log("猫吃鱼");
    }
}

使用代理方式

Animal cat = ProxyUtil.proxy(new Cat(), TimeIntervalAspect.class);
cat.eat();

2.2 cglib 实现切面

默认使用 jdk 动态代理,这边需要额外引入第三方依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.7</version>
</dependency>

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

定义一个普通类

public class Dog {
    public void eat() {
        Console.log("狗吃肉");
    }
}

使用代理方式

Dog dog = ProxyUtil.proxy(new Dog(), TimeIntervalAspect.class);
dog.eat();

2.3 自定义切面

public class MyAspect extends SimpleAspect {

    private String test;

    public MyAspect(String test) {
        super();
        this.test = test;
    }

    @Override
    public boolean before(Object target, Method method, Object[] args) {
        Console.log("before:target:{},method:{},args:{},test:{}", target, method, args, test);
        return super.before(target, method, args);
    }

    @Override
    public boolean after(Object target, Method method, Object[] args, Object returnVal) {
        Console.log("after:target:{},method:{},args:{},test:{}", target, method, args, test);
        return super.after(target, method, args, returnVal);
    }

    @Override
    public boolean afterException(Object target, Method method, Object[] args, Throwable e) {
        Console.log("before:target:{},method:{},args:{},exception:{}", target, method, args, e);
        return super.afterException(target, method, args, e);
    }
}

使用代理

Dog dog = ProxyUtil.proxy(new Dog(), new MyAspect("测试"));
dog.eat();

2.4 两种代理区别

  • java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
  • 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

查看源码

JDK 动态代理类

@Override
public <T> T proxy(T target, Aspect aspect) {
   return ProxyUtil.newProxyInstance(//
         target.getClass().getClassLoader(), //
         new JdkInterceptor(target, aspect), //
         target.getClass().getInterfaces());
}

CGlibProxy 动态代理类

@Override
@SuppressWarnings("unchecked")
public <T> T proxy(T target, Aspect aspect) {
   final Enhancer enhancer = new Enhancer();
   enhancer.setSuperclass(target.getClass());
   enhancer.setCallback(new CglibInterceptor(target, aspect));
   return (T) enhancer.create();
}