Introduction and use of JDK dynamic agent

1, Introduction

JDK dynamic proxy is a way to implement the proxy pattern. Because it is based on the interface to do the proxy, so it is often called interface proxy. There are two important roles in the JDK dynamic agent:

  • InvocationHandler(Interface)
    The user implements this interface to write the core logic of agent class processing.
  • Proxy(Class)
    To create a proxy instance, you need to use the above custom InvocationHandler.

2, Function

Dynamic agent has the basic functions of agent mode, such as preprocessing and modularization. In addition, agent objects can be created dynamically at runtime without writing agent logic for each interface (corresponding processing logic is written for each interface, which is called static agent).

3, Use steps

  1. Write target interface and target implementation class
  2. Customize the InvocationHandler interface implementation class and write the agent processing logic
    Let's take a look at the invoke method, the only method of the InvocationHandler interface.
/**
 * proxy: Proxy object, generally not used
 * method: It refers to the Method object of a Method that we want to call the real object
 * args: Refers to the parameters accepted when a method of a real object is called
 **/
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
  1. Create proxy object
    When creating a proxy object, you need to associate an InvocationHandler object. In this way, when we call a method through a proxy object, the call of this method will be forwarded to the invoke method of the InvocationHandler interface.

We usually write the method of creating proxy object directly in the custom InvocationHandler implementation class.

  1. Using a proxy to call methods in the target interface

4, Example

Requirement: when each method is called, a line of log will be printed before, after and exception.
For this kind of requirement, we need to modularize the function of printing log. We can't write the code of printing log in every method, which will mix the general function and business function together.

1. Write target interface and target class

Target interface (because JDK dynamic proxy is implemented based on the interface, the target class to be proxy must implement an interface.)

public interface UserService {
    String getUserName(Long userId);

    void say(String msg);
}

Target class

public class UserServiceImpl implements UserService {
    @Override
    public String getUserName(Long userId) {
        return "user" + userId;
    }

    @Override
    public void say(String msg) {
        System.out.println("say " + msg);
    }
}
2. Custom InvocationHandler
public class LogInvocationHandler implements InvocationHandler {
    /**
     * 1. Target class
     */
    private final Object target;

    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 2. Agent logic
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Call target method
        Object result = null;
        try {
            //Before advice 
            System.out.println(method.getName() + "Method start call...");
            result = method.invoke(target, args);
            //Return notification, you can access the return value of the method
            System.out.println(method.getName() + "Method return value:" + result);
        } catch (Exception e) {
            e.printStackTrace();
            //Exception notification, you can access the exception of the method
            System.out.println(method.getName() + "Exception in method call");
        }
        //Post notice
        System.out.println(method.getName() + "Method call complete!");
        return result;
    }

    /**
     * 3. Get target class proxy
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }
}

The trilogy of the custom InvocationHandler implementation class (very important):

  1. Rewrite the invoke method and write the agent core logic
  2. Save the target object (that is, the target object above, which will be used when creating a proxy)
  3. Provides a way to create an agent
    The newProxyInstance method of the Proxy class is used to implement the three parameters: class loader, target class interface, and Proxy logic processing class (custom InvocationHandler)

In this way, when using the proxy object to call the interface method, it can be forwarded to the invoke method of the proxy processing class.

3. Use the proxy to call the target method
@Test
public void dynamicProxyTest() {
    UserService userService = new UserServiceImpl();

    LogInvocationHandler logInvocationHandler = new LogInvocationHandler(userService);
    UserService userServiceProxy = (UserService) logInvocationHandler.getProxy();

    userServiceProxy.getUserName(1L);
    System.out.println("=====================");
    userServiceProxy.say("hello");
}

Print results
It can be seen that when the proxy object userServiceProxy is used to call the interface method, the requests are forwarded to the invoke method of the proxy processing class, and the reflection method is used in the invoke method, and finally forwarded to the target target object.

Tags: JDK

Posted on Fri, 26 Jun 2020 01:15:15 -0400 by plisken