preface

Some time ago, I learned Java design pattern — proxy pattern in our Open class of Lebyte. Recently, when I looked at Spring Aop, I felt that the proxy pattern should be closely related, so I decided to understand the implementation principle of Spring Aop.

Speaking of AOP, WE have to say OOP. OOP introduces concepts such as encapsulation, inheritance and polymorphism to establish an object hierarchy that simulates a collection of common behaviors. However, OOP introduces a lot of repetitive code when we need to introduce common parts for partial objects. For example, log function.

Using a technique called crosscutting, AOP dissects the interior of encapsulated objects and encapsulates common behaviors that affect multiple classes into a reusable module. This reduces system duplication, reduces coupling between modules, and facilitates future operability and maintainability.

AOP divides a software system into two parts: core concerns and crosscutting concerns. The main flow of business processing is the core concern, and the less relevant part is the crosscutting concern. One of the things about crosscutting concerns is that they often occur at multiple points of the core concern, and they are basically similar everywhere. Such as permission authentication, logging, and transaction processing.

Realize the principle of

Before learning the proxy mode, we learned that the proxy mode is divided into dynamic proxy and static proxy. Now let’s implement our own AOP framework based on the proxy pattern, and examine the implementation principles of Spring’s AOP.

The key of static proxy is to realize the common interface between the proxy object and the target object, and the proxy object holds the reference of the target object.

Public interface code:

public interface IHello {

/ * *

* Business method

* @param str

* /

void sayHello(String str);

}

Target class code:

public class Hello implements IHello{

@Override

public void sayHello(String str) {

System.out.println(“hello “+str);

}

}

Proxy class code:

Isn’t it particularly AOP like adding logging capabilities to execute specific methods before and after methods start?

public class ProxyHello implements IHello{

private IHello hello;

public ProxyHello(IHello hello) {

super();

this.hello = hello;

}

@Override

public void sayHello(String str) {

Logger.start(); // Add specific methods

hello.sayHello(str);

Logger.end();

}

}

Logging class code:

public class Logger {

public static void start(){

System.out.println(new Date()+ ” say hello start…” );

}

public static void end(){

System.out.println(new Date()+ ” say hello end”);

}

}

Test code:

public class Test {

public static void main(String[] args) {

IHello hello = new ProxyHello(new Hello()); // If we need logging, use proxy classes

//IHello hello = new Hello(); // Use the target class if we don’t need logging

Hello. SayHello (” tomorrow “);

}

}

This gives us the simplest AOP implementation, but there is a problem: if we have a lot of classes like Hello, do we need to write a lot of classes like HelloProxy? In fact, it is also a very troublesome thing. After jdk1.3 JDK provides an API for Java. With us. Lang reflect. InvocationHandler class, this class can let us in the JVM dynamically call a class method for what some methods. Let’s implement dynamic proxy.

Dynamic proxy implementation mainly implements InvocationHandler and injects the target object into the proxy object, using reflection mechanism to execute the target object’s methods.

Interface implementation is the same as static proxy.

Proxy class code:

public class DynaProxyHello implements InvocationHandler{

private Object target; // Target object

/ * *

* Instantiate the target object by reflection

* @param object

* @return

* /

public Object bind(Object object){

this.target = object;

return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);

}

@Override

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

Object result = null;

Logger.start(); // Add additional methods

// Use reflection to run the target object’s methods

result = method.invoke(this.target, args);

Logger.end();

return result;

}

}

Test class code:

public class DynaTest {

public static void main(String[] args) {

IHello hello = (IHello) new DynaProxyHello().bind(new Hello()); // If we need logging, use proxy classes

//IHello hello = new Hello(); // Use the target class if we don’t need logging

Hello. SayHello (” tomorrow “);

}

}

The logging class can only be printed before and after a method, but AOP should be able to execute as soon as the conditions are met. So can we decouple the DynaPoxyHello object from the logging operation object (Logger)?

Look at the following code implementation, which will decouple the DynaPoxyHello object from the Logger:

If we want to add logging (or other operations) to or after the proxied object’s methods, we can abstract out an interface that has only two methods: One method is executed before the proxied object executes the method, which we call start. The second method is executed after the proxied object executes the method, which we call end.

Logger interface:

public interface ILogger {

void start(Method method);

void end(Method method);

}

Logger interface implementation:

public class DLogger implements ILogger{

@Override

public void start(Method method) {

System.out.println(new Date()+ method.getName() + ” say hello start…” );

}

@Override

public void end(Method method) {

System.out.println(new Date()+ method.getName() + ” say hello end”);

}

}

Dynamic proxy class:

public class DynaProxyHello1 implements InvocationHandler{

// Call the object

private Object proxy;

// Target object

private Object target;

public Object bind(Object target,Object proxy){

this.target=target;

this.proxy=proxy;

return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);

}

@Override

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

Object result = null;

Reflection gets an instance of the operator

Class clazz = this.proxy.getClass();

// reflection gets the operator’s Start method

Method start = clazz.getDeclaredMethod(“start”, new Class[]{Method.class});

// reflection executes the start method

start.invoke(this.proxy, new Object[]{method});

// Execute the original method of the object to be processed

method.invoke(this.target, args);

// reflection gets the operator’s end method

Method end = clazz.getDeclaredMethod(“end”, new Class[]{Method.class});

// reflection executes the end method

end.invoke(this.proxy, new Object[]{method});

return result;

}

}

Test code:

public class DynaTest1 {

public static void main(String[] args) {

IHello hello = (IHello) new DynaProxyHello1().bind(new Hello(),new DLogger()); // If we need logging, use proxy classes

//IHello hello = new Hello(); // Use the target class if we don’t need logging

Hello. SayHello (” tomorrow “);

}

}

As you can see from the above example, AOP has been basically implemented with dynamic proxy and emission techniques. If we only need to print logs before method execution, we can not implement the end method, so that we can control the printing timing.

If we want the specified method to print logs, all we need to do is add a judgment to the Invoke method name, which can be written to an XML file. This way we can implement decoupling from the configuration file, and we have implemented a simple Spring AOP framework.

The article turns to music bytes