Analysis of the principle of Spring AOP

Spring AOP learning record

AOP (face-to-face programming) knowledge is to see forget to see, today free to record AOP knowledge points. It is mainly divided into the following aspects:

1. AOP related terms

2. Basic knowledge and examples

3. Enhanced classification

1. AOP related terms

Join point a class has some specific points with boundary properties, for example, each method of a class can be called join point.

Pointcut is to select the connection points of interest among many connection points. If the connection point is compared to all records in a database, then the pointcut is the query condition of a query statement.

Enhanced (Advice) is a piece of code put into the target connection point, which carries orientation information, such as before method call, after call, before and after call, exception throw, etc.

Aspect is composed of tangent and enhancement. It contains not only the enhancement logic and orientation information, but also the connection point information.

The Target object enhances the Target class of logical weaving.

Introduction is a special enhancement that provides some properties and methods for classes. In this way, even if a business class does not implement an interface originally, the introduction function of AOP can also dynamically add interface implementation logic for the class, making the class an interface implementation class.

Weaving weaving is the process of enhancing the specific methods added to the target class. AOP has three weaving methods: ① compile time weaving, ② class load time weaving, ③ dynamic agent weaving (generally used in Spring)

After a class of Proxy is enhanced by AOP weaving, a Proxy result class is generated. It integrates enhanced logic. According to different Proxy methods, Proxy class may be a class with the same interface as the original class or a subclass of the original class.

Through the above concepts, we can think of defining the tangent point, weaving the enhanced logic into the tangent point and other steps, and finally we can complete the tangent programming.

2. Basic knowledge and examples
2.1 JDK dynamic agent

The dynamic Proxy technology provided by Java JDK allows developers to create Proxy classes of interfaces during runtime. The dynamic Proxy of JDK mainly involves two classes in java.lang.reflect package: Proxy and InvocationHandler.

Among them, InvocationHandler is an interface, through which enhanced logic can be realized, and the target class code can be called through the reflection mechanism, which dynamically compiles enhanced logic and business logic together. The Proxy uses the InvocationHandler to dynamically create an example that conforms to an interface and generate the Proxy class of the target class. I'll use the code to demonstrate.

// Let's assume we have the following code
public interface GreetService {
	void serviceTo(String name);
}

public class Waiter implements GreetService{

	@Override
	public void serviceTo(String name) {
         // Suppose the following is the code time performance monitoring code
		// Long beginTime = System.currentTimeMillis();
		try {
			// The simulator is working
			System.out.println("service to :"+name);
			Thread.sleep(10000L);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// System.out.println("consume time:"+(System.currentTimeMillis()-beginTime));
	}
}


// We use JDK dynamic agent to weave the performance monitoring code in the above code
// First, write the InvocationHandler implementation class
public class WaiterHandler implements InvocationHandler{
	// Target class
	private Object target;
	public WaiterHandler(Object target) {
		this.target = target;
	}
	// Proxy call method
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Long beginTime = System.currentTimeMillis();
		// Call the corresponding method of target and pass in the parameter
		method.invoke(target,args);
		
		System.out.println("consume time : "+(System.currentTimeMillis()-beginTime));
		return null;
	}
}
public class MainTest {

	public static void main(String[] args) {
		// Create a GreetService instance, which is the business class you want to proxy
		GreetService waiter = new Waiter();
		// Create the InvocationHandler implementation class and weave the enhanced logic for the business class
		WaiterHandler waiterHandler = new WaiterHandler(waiter);
		// Using Proxy to generate a GreetService Proxy instance
		GreetService waiterProxy = (GreetService)Proxy.newProxyInstance(waiter.getClass().getClassLoader(), Waiter.class.getInterfaces(),waiterHandler);
		// Call the proxy instance method
		waiterProxy.serviceTo("fuhang");
	}
}
// output:
//  service to :fuhang
//  consume time : 10002
 

The above shows you a sample code that uses JDK dynamic agent to extract performance monitoring code from business logic and then dynamically weave in.

From the above example code, you may find several problems:

1. Waiter must implement an interface

2. newProxyInstance(waiter.getClass().getClassLoader(), Waiter.class.getInterfaces(),waiterHandler); the second parameter must be passed into the interface list implemented by Waiter

As you can see from the above, there is a limitation for JDK to create proxy. It can only create proxy instances for interfaces. Do we have to define an interface for a business class? For classes that do not define business methods through interfaces, how to dynamically create proxy instances for them? Let's take a look at CGLib, the next dynamic proxy technology.

2.2CGLib dynamic agent

CGLib uses dynamic bytecode technology in the bottom layer, which can create subclass for a class. In the subclass, method interception technology is used to intercept all the method calls of the parent class and weaves enhanced logic along the trend. Next, we use CGLib to implement the above performance monitoring weaving logic.

// First, we create a CglibProxy class to implement MethodInterceptor
public class CglibProxy implements MethodInterceptor{

	@Override
	public Object intercept(Object obj, Method arg1, Object[] arg2, MethodProxy methodProxy) throws Throwable {
		Long beginTime = System.currentTimeMillis();
		// Where the actual method is called
		methodProxy.invokeSuper(obj, arg2);
		
		System.out.println("consume time : "+(System.currentTimeMillis()-beginTime));
		return null;
	}
}

// Test class
public class MainTest {

	public static void main(String[] args) {
		// Create EnHancer object
		Enhancer enhancer = new Enhancer();
        // Create our CglibProxy proxy object
		CglibProxy cglibProxy = new CglibProxy();
		// Set the parent class to Waiter
		enhancer.setSuperclass(Waiter.class);
        // Set callback function to CglibProxy
		enhancer.setCallback(cglibProxy);
		// Using enhancer dynamic bytecode technology to create Waiter's proxy class
		Waiter waiter = (Waiter) enhancer.create();
		waiter.serviceTo("fuhang");
	}
}
// output:
//    service to :fuhang
//    consume time : 10044

The above is the generation of proxy classes by Cglib dynamic bytecode technology to generate subclasses for business classes, which involves the review of Java knowledge points. The classes modified by final and private modifiers cannot use Cglib dynamic proxy technology.

Summary:

The underlying layer of spring AOP technology uses dynamic proxy technology to weave enhanced logic for the target bean, but the following problems can be found from the example code of the above two types of proxy technology:

(1) after the target class is proxied, all methods are added with enhanced logic. Sometimes this is not what we want.

(2) enhanced logic is specified by manual encoding

(3) manually write the agent instance creation process, and manually write the corresponding agent instance creation process code when creating agents for different types.

So the main work of spring AOP is to solve the above three problems: Spring AOP Through Pointcut, you can specify which methods of which classes are woven into the enhanced logic. Through Advice, you can describe the specific weaving points (before, after and before the method call). Spring organizes the aspects and enhancements through Advisor. Next, spring can use the dynamic proxy technology to weave enhancements for the target bean in a unified way.

3. Enhancement type

Spring uses Advice to define enhanced logic, which includes not only enhanced logic, but also orientation information (i.e. before, after and before method call)

Spring supports five types of enhancements, which are explained as follows:

  • Pre enhancement: org.springframework.aop.BeforeAdvice represents pre enhancement. Spring only supports method level enhancement, so at present, MethodBeforeAdvice is a pre enhancement available in spring. The BeforeAdvice interface is defined for future extension.
  • Post enhancement: org.springframework.aop.afterreturnadvice, indicating enhancement after target method execution.
  • Surround enhancement: org.aopalliance.intercept.MethodInterceptor represents the surround enhancement, which means the enhancement is implemented before and after the target method is executed.
  • Exception throwing enhancement: org.springframework.aop.ThrowsAdvice represents the exception throwing enhancement, which means the enhancement is implemented after the exception is thrown by the target method.
  • Introduction enhancement: org.springframework.aop.IntroductionInterceptor represents the introduction enhancement, which means adding some new methods and properties to the target class.
3.1 pre enhancement

We also use our previous GreetService and Waiter as example code. In our previous code, the Waiter.service to (name) method just outputs "service to xxx". Now we want to implement Waiter.serviceTo(name) to greet customers before outputting. According to the previous understanding, we need to define a pre enhancement for it. Let's start to transform.

// We define a greeting pre class to implement MethodBeforeAdvice
public class GreetBeforeAdvice implements MethodBeforeAdvice{
	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
         // Output greeting words
		System.out.print("hello "+ arg1[0]);
	}
}
public class MainTest {
	public static void main(String[] args) {
		// Creating Waiter and pre enhanced classes
		Waiter waiter = new Waiter();
		GreetBeforeAdvice greetBeforeAdvice = new GreetBeforeAdvice();
		// Create a proxy factory, and then explain the factory
		ProxyFactory proxyFactory = new ProxyFactory();
		// Set the target to be represented and the enhancement to weave
		proxyFactory.setTarget(waiter);
		proxyFactory.addAdvice(greetBeforeAdvice);
		// Create a proxy class through a proxy factory
		Waiter myWaiter = (Waiter) proxyFactory.getProxy();
		// Calling method
		myWaiter.serviceTo("fuhang");	
	}
}
// output: 
// 		hello fuhang service to :fuhang

The above is an example of pre enhancement. Next, I will explain the details of ProxyFactory. First, I will go back and forth to the contents of the previous basic knowledge chapter and say two dynamic proxy technologies. In fact, ProxyFactory uses JDK or Cglib dynamic proxy technologies.

Spring defines the org.springframework.aop.framework.AopProxy interface, and finally provides two final implementation classes, as shown in the following figure.

Cglib2Proxy in the figure above uses cglib dynamic proxy technology, and JDK dynamic AOPP proxy uses JDK dynamic proxy technology. If the interface information of the target class is specified through the setInterfaces(Class [] interfaces) method of ProxyFactory, then the underlying layer of ProxyFactory will use JDK dynamic proxy technology, otherwise cglib dynamic proxy technology will be used without setting. So we can see that cglib dynamic agent technology is used in our sample code.

3.2 post enhancement

For example, after each service, the waiter needs to ask the customer, "what else do you need? "In this sentence, this requirement can be implemented through a post enhancement. An example is as follows.

// Declare a post enhanced class and implement afterreturnadvice
public class GreetAfterAdvice implements AfterReturningAdvice{

	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		System.out.println("Is there anything else you need?");
	}
}

// Test class
public class MainTest {

	public static void main(String[] args) {
		// Create Waiter and pre and post enhancements
		Waiter waiter = new Waiter();
		GreetBeforeAdvice greetBeforeAdvice = new GreetBeforeAdvice();
		GreetAfterAdvice greetAfterAdvice = new GreetAfterAdvice();
		// Agent factory
		ProxyFactory proxyFactory = new ProxyFactory();
		// Set target agent class and pre enhancement and post enhancement
		proxyFactory.setTarget(waiter);
		proxyFactory.addAdvice(greetAfterAdvice);
		proxyFactory.addAdvice(greetBeforeAdvice);
		// Create agent Waiter
		Waiter myWaiter = (Waiter) proxyFactory.getProxy();
		// Calling method
		myWaiter.serviceTo("fuhang");
	}
}
// output:
//     hello fuhang service to :fuhang
//     Is there anything else you need?
3.3 surround enhancement

Use the surround enhancement to realize the pre post enhancement complex above. The example code is as follows.

// Declare surround enhancement class and implement methodinterceptor (package of aopalliance)
public class SurrondAdvice implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
         // Get target class parameters
		Object [] args = arg0.getArguments();
		System.out.println("hello :"+args[0]);
         // Execute target class method
		arg0.proceed();
		System.out.println(" Is there anything else I need?");
		return null;
	}
}

// The following code is not verbose, just look at the output
public class MainTest {

	public static void main(String[] args) {
		
		Waiter waiter = new Waiter();
		SurrondAdvice surrondAdvice = new SurrondAdvice();
		
		ProxyFactory proxyFactory = new ProxyFactory();
		
		proxyFactory.setTarget(waiter);
		proxyFactory.addAdvice(surrondAdvice);

		
		Waiter myWaiter = (Waiter) proxyFactory.getProxy();
		
		myWaiter.serviceTo("fuhang");
		
	}
}
// output:
//  	hello :fuhang
//   	service to :fuhang
//		Is there anything else I need?
3.4 exception throwing enhancement

The appropriate application scenario for exception throwing enhancement is transaction management. When a Dao involved in transaction management throws an exception, it rolls back the transaction. An example is as follows.

public class Waiter{

	@Override
	public void serviceTo(String name) {
		throw new RuntimeException("Dao abnormal");
	}
}

// Exception throwing enhancement class, which will be called before exception throwing
public class ExceptionAdvice implements ThrowsAdvice{
	
	public void afterThrowing(Method method,Object [] args,Object target,Exception exception){
		System.out.println("Transaction rollback");
	}
}

// Test class
public class MainTest {

	public static void main(String[] args) {
		
		Waiter waiter = new Waiter();
		ExceptionAdvice exceptionAdvice = new ExceptionAdvice();
		
		ProxyFactory proxyFactory = new ProxyFactory();
		
		proxyFactory.setTarget(waiter);
		proxyFactory.addAdvice(exceptionAdvice);

		
		Waiter myWaiter = (Waiter) proxyFactory.getProxy();
		
		myWaiter.serviceTo("fuhang");
		
	}
}
// output:
// 		Transaction rollback
//		Exception in thread "main" java.lang.RuntimeException: Dao exception

ThrowingAdvice is a tag interface, in which no method is declared. During the runtime, Spring automatically judges the enhanced method through the reflection mechanism. The enhanced method signature must be in the following format:

​ void afterThrowing([Method method,Object [] args,Object obj],Throwable)

The first three parameters of the method are optional, either passed in or not. The last one must be passed in. The last parameter is a subclass of Throwable.

3.5 introduction enhancement

Introduction enhancement is special. Instead of adding enhancement logic around methods, it dynamically adds new methods and attributes to the target class. So the reference enhanced join point is located on the class rather than the method. Next, we add an interface for the target class through the introduction enhancement to complete the monitoring switch function.

// Declare an additional interface
public interface Monitor {
	void setMonitorActive(boolean active);
}

// Target class
public class Waiter{
	public void serviceTo(String name) {
		System.out.println("hello :"+name);
	}
}

// Create a class to inherit DelegatingIntroductionInterceptor and implement the interface to be attached to the target class in the future
public class PerformanceMonitor extends DelegatingIntroductionInterceptor implements Monitor{
	// Equivalent to the attribute finally added to the class
	ThreadLocal<Boolean> isOpen = new ThreadLocal<>();

	@Override
	public void setMonitorActive(boolean active) {
		isOpen.set(active);
	}

	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		if(isOpen.get()!=null && isOpen.get() ){
			Long beginTime = System.currentTimeMillis();
			super.invoke(arg0);
			System.out.println("consume time :"+(System.currentTimeMillis()-beginTime));
		}else{
			super.invoke(arg0);
		}
		return null;
	}

}

// Test class
public class MainTest {

	public static void main(String[] args) {
		
		Waiter waiter = new Waiter();
		PerformanceMonitor performanceMonitor = new PerformanceMonitor();
		
		ProxyFactory proxyFactory = new ProxyFactory();
		// Next, setOptimize(true) must be set to enable Cglib proxy
		proxyFactory.setInterfaces(Monitor.class);
		proxyFactory.setTarget(waiter);
		proxyFactory.addAdvice(performanceMonitor);
		proxyFactory.setOptimize(true);

		// First call to method
		Waiter myWaiter = (Waiter) proxyFactory.getProxy();
		myWaiter.serviceTo("fuhang");
		
        // Call the method again after setting the monitoring switch after turning to Monitor
		Monitor monitor = (Monitor)myWaiter;
		monitor.setMonitorActive(true);
		
		myWaiter.serviceTo("zhangsan");
		
	}
}
// output: 
// 		hello :fuhang
//		hello :zhangsan
//		consume time :0

summary

The above is the main content of this learning. Later, how to convert the above manually created method to Spring xml configuration is not recorded in this article (because it is not difficult, and there is no technical content). Hope to learn the core knowledge of Spring Aop through this record.

Reference material Proficient in spring 4. X enterprise application development practice

Tags: Programming Spring JDK Java

Posted on Sat, 14 Mar 2020 02:14:08 -0700 by MentalMonkey