AOP introduction: dynamic agent

Posted by atticus on Fri, 01 Oct 2021 00:31:51 +0200

AOP introduction: dynamic agent

Before introducing AOP, let's introduce the knowledge of dynamic agent

Let's give you a simple example. Now we have the following user interface and its implementation class (for convenience, only the corresponding statements are printed in the implementation class)

package com.zy.service;

public interface userService
{
    public void selectUser();

    public void insertUser();
}
package com.zy.service;

public class userServiceImpl implements userService
{

    @Override
    public void selectUser()
    {
        System.out.println("Find users");
    }

    @Override
    public void insertUser()
    {
        System.out.println("Insert user");
    }
}

Now we want to add two functions to these two methods: add the recording time function before execution and add the * * recording log * * function after execution. How do we implement it?

Mode 1:

Most people may want to add these two new functions directly to both methods, as shown below

public class userServiceImpl implements userService
{

    @Override
    public void selectUser()
    {
        System.out.println("Execution time:" + new Date());
        System.out.println("Find users");
        System.out.println("Print log information");
    }

    @Override
    public void insertUser()
    {
        System.out.println("Execution time:" + new Date());
        System.out.println("Insert user");
        System.out.println("Print log information");
    }
}

However, this changes the original code, and the original business processing code and the newly added irrelevant business code are integrated and repeated, which leads to a lot of clutter. Moreover, if we modify the irrelevant business code, we need to modify it in each function, how should we optimize it?

Mode 2:

We extract the code independent of the business separately, encapsulate it into a tool class and call it in the business code, as shown below.

public class otherService
{
    public static void getTime()
    {
        System.out.println("Execution time:" + new Date());
    }

    public static void getLog()
    {
        System.out.println("Print log information");
    }
}
@Override
public void selectUser()
{
    otherService.getTime();
    System.out.println("Find users");
    otherService.getLog();
}

@Override
public void insertUser()
{
    otherService.getTime();
    System.out.println("Insert user");
    otherService.getLog();
}

This solves some problems, but we can also find that the business code is still the caller's non business code, and the coupling is still too high, which leads to our dynamic agent~

With the emergence of dynamic proxy mechanism, Java developers do not need to write proxy classes manually. As long as they simply specify a group of interfaces and delegate class objects, they can dynamically obtain proxy classes. Proxy classes are responsible for dispatching all method calls to delegate objects for reflection and execution. In the process of dispatching and execution, developers can also adjust delegate class objects and their functions as needed It is a very flexible and flexible agent framework.

The basic process is as follows. The proxy class creates the proxy class object through the business interface, and finally returns the proxy class object.

Mode 3:

We then use the JDK's own dynamic agent to solve the above requirements

First, we create a UserService proxy class to implement the InvocationHandler interface

package com.zy.service;

public class UserProxy implements InvocationHandler
{
    private Object target;//Proxy interface (target object)

    public UserProxy(Object target)
    {
        this.target = target;
    }
    /**
     * Add functions to the original method and return the proxy class object
     * @param proxy Proxy class object
     * @param method Original method
     * @param args Parameters in the original method
     * @return Proxy class object
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        otherService.getTime();//Print time
        Object invoke = method.invoke(target, args);
        otherService.getLog();//Print log
        return invoke;
    }
    
     public void setTarget(Object target)
    {
        this.target = target;
    }
}

Next, specify a ClassLoader object and a set of interface s for the Proxy class to create a dynamic Proxy class

public void test()
{
    //First create the target object
    UserServiceImpl target = new UserServiceImpl();
    //Create InvocationHandler object
    UserProxy proxy = new UserProxy(target);
    //Creating a Proxy using Proxy
    UserService userService = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
            target.getClass().getInterfaces(), proxy);

    //Test proxy class object
    userService.insertUser();
}

give the result as follows

In this way, we did not modify the original business code, but achieved the function of adding new functions.

Dynamic agency

Through the above example, we can find that the dynamic agent has the following functions

  • You can add functions without changing the source code of the target class.
  • Can reduce code duplication.
  • Allows programmers to focus on business logic code.
  • The code can be decoupled to separate business functions from non business functions.

Topics: Java Spring Rust