An in depth analysis of Spring aop implementation principle

  • 2020-05-09 18:30:46
  • OfStack

What is a AOP

AOP (Aspect-OrientedProgramming, aspect-oriented programming), can be said to be the complement and improvement of OOP (Object-Oriented Programing, object-oriented programming). OOP introduces the concepts of encapsulation, inheritance, and polymorphism to create an object hierarchy that simulates a collection of common behaviors. When we need to introduce common behavior for scattered objects, OOP is powerless. That is, OOP allows you to define relationships from top to bottom, but it is not suitable for defining relationships from left to right. For example, logging. Logging code tends to be distributed horizontally across all object hierarchies, regardless of the core functionality of the objects it is distributed to. The same is true for other types of code, such as security, exception handling, and transparent persistence. This scattering of unrelated code is known as crosscutting (cross-cutting) code, and in the OOP design, it leads to a lot of code duplication that is not conducive to the reuse of individual modules.

Introduction to the

The design mode of java -- agent mode written some time ago, when I recently looked at Spring Aop, I felt that it should be closely related to the agent mode, so I decided to understand the implementation principle of Spring Aop.

When we talk about AOP, we have to talk about OOP. In OOP, the concepts of encapsulation, inheritance and polymorphism are introduced to build an object hierarchy to simulate a set of common behaviors. However, if we need to introduce common parts for partial objects, OOP will introduce a lot of repetitive code. For example, the logging function.

The AOP technology utilizes a technique called crosscutting to dissect the inside of the encapsulated object and encapsulate the common behavior that affects multiple classes into a reusable module, thus reducing the system's repetitive code, reducing the coupling between modules, and facilitating future operability and maintainability. AOP divides the software system into two parts: core concerns and crosscutting concerns. The main process of business processing is the core concern, and the less relevant part is the crosscutting concern. One of the characteristics of crosscutting concerns is that they often occur at multiple points in the core concern, where they are essentially the same. Such as permission authentication, logging, and transaction processing.

Realize the principle of

In the previous study of the proxy model, we learned that the proxy model is divided into dynamic proxy and static proxy. Now let's implement our own AOP framework based on the proxy pattern, and then study the implementation principle of AOP of Spring.

The key of the 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 methods 
* @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, we add logging to it, before and after the start of the method to execute the specific method, is it exactly like AOP?


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();
}
}

Log 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 the logging capability, we use the proxy class 
//IHello hello = new Hello();// Use the target class if you don't need the logging capability 
hello.sayHello(" Tomorrow, "); 
}
}

So we have one of the simplest AOP implementations, but there's a question: if we have a lot of classes like Hello, do we have to write a lot of classes like HelloProxy? In fact, it is also a very troublesome thing. In jdk1. 3, jdk provides one with us API java. lang. reflect. InvocationHandler class, this class can let us in JVM call a class method dynamically for what some methods. Now let's implement the dynamic proxy.

The dynamic proxy implementation mainly implements InvocationHandler, injects the target object into the proxy object, and USES the reflection mechanism to execute the method of the target object.

The interface implementation is the same as the static proxy. The proxy class code:


public class DynaProxyHello implements InvocationHandler{
private Object target;// The target object 
/**
*  The target object is instantiated through 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 
// A method that runs a target object through a reflection mechanism 
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 the logging capability, we use the proxy class 
//IHello hello = new Hello();// Use the target class if you don't need the logging capability 
hello.sayHello(" Tomorrow, ");
}
}

After reading the above code, there may be a problem compared with Spring AOP. The log class can only be printed before and after the method, but AOP should be executed when the conditions are met. So can we decouple the DynaPoxyHello object from the log operation object (Logger)?

Look at the code implementation below to decouple the DynaPoxyHello object from the log operation object (Logger) :

We want to in front or behind the proxy object method to add log operation code (or other operating code), so, we can abstract out a interface, this interface is only two methods: one is before being a proxy object to execute method execution method, we named start, the second method is executed after is a proxy object to perform the method method, named we end.

Interface of Logger:


public interface ILogger {
void start(Method method);
void end(Method method);
}

Interface implementation of Logger:


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 DynaProxyHello implements InvocationHandler{
// Call object 
private Object proxy;
// The 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 methods 
Method start = clazz.getDeclaredMethod("start", new Class[]{Method.class});
// Reflection to perform start methods 
start.invoke(this.proxy, new Object[]{this.proxy.getClass()});
// Execute the original method to process the object 
method.invoke(this.target, args);
// Reflection gets the operator's end methods 
Method end = clazz.getDeclaredMethod("end", new Class[]{Method.class});
// Reflection to perform end methods 
end.invoke(this.proxy, new Object[]{method});
return result;
}
} 

Test code:


public class DynaTest {
public static void main(String[] args) {
IHello hello = (IHello) new DynaProxyHello().bind(new Hello(),new DLogger());// If we need the logging capability, we use the proxy class 
//IHello hello = new Hello();// Use the target class if you don't need the logging capability 
hello.sayHello(" Tomorrow, ");
}
} 

From the above example, we can see that AOP has been basically implemented through dynamic proxy and emission techniques. If we only need to print the log before the method is executed, we can not implement end() method, so we can control the printing timing. If we want the specified method to print the log, we only need to add a judgment on the method name in the invoke () method. The method name can be written in the xml file, so that we can decouple the spring aop framework with the configuration file.

The above content is this site to introduce the Spring aop implementation principle, I hope to help you!


Related articles: