ProxyUtil

Usage

Using Dynamic Proxy in JDK to Implement Aspects

  1. We define an interface:
public interface Animal {
 void eat();
}
  1. Define an implementation class:
public class Cat implements Animal {

 @Override
 public void eat() {
 Console.log("Cat eats fish");
 }
 
}
  1. We use the TimeIntervalAspect aspect to proxy the aforementioned object to calculate the execution time of the cat eating fish:
Animal cat = ProxyUtil.proxy(new Cat(), TimeIntervalAspect.class);
cat.eat();

TimeIntervalAspect is located in the cn.hutool.aop.aspects package and extends from SimpleAspect. The code is as follows:

public class TimeIntervalAspect extends SimpleAspect {
    // TimeInterval is a timer implemented by 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;
    }
}

The execution result is:

Cat eats fish
Method [cn.hutool.aop.test.AopTest$Cat.eat] execute spend [16]ms

After calling the proxy method, the IDE automatically completes the returned object as Cat, due to the JDK mechanism. Our return value must be the interface implemented by the proxy class, so we need to manually change the return value to Animal otherwise we will encounter a type conversion error.

Using Cglib to Implement Aspects

The benefit of using Cglib is that it is unnecessary to define an interface to apply aspects directly to an object, and the usage is identical:

  1. Include the Cglib dependency
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.7</version>
</dependency>
  1. Define a class with no interface (it can have an interface or not)
public class Dog {
    public String eat() {
        Console.log("dog eats meat");
    }
}
Dog dog = ProxyUtil.proxy(new Dog(), TimeIntervalAspect.class);
String result = dog.eat();

The execution result is:

dog eats meat
Method [cn.hutool.aop.test.AopTest$Dog.eat] execute spend [13]ms

Other Methods

ProxyUtil also provides some convenient methods forProxynewProxyInstance method is encapsulated to provide generic return values and support for additional parameter types.

Principles of Operation

The creation of dynamic proxy objects assumes that the proxy object is named $Proxy0:

  1. Dynamically generate a class based on the provided interfaces and implement the interfaces in interfaces.
  2. Load the recently generated class into the JVM using the provided classloader. In other words, load the $Proxy0 class.
  3. Invoke the $Proxy0($Proxy0) constructor to create an instance of $Proxy0 and use the interfaces parameter to traverse all of its interface methods, generating implementation methods. The implementation of these methods is essentially to invoke the method of the target object using reflection.
  4. Return an instance of $Proxy0 to the client.
  5. When the client calls a method on the proxy class, it is equivalent to calling the InvocationHandler.invoke(Object, Method, Object[]) method.