Spring aspect oriented AOP

1, Dynamic agent

1. Export mode
  • Code confusion: after more and more non business requirements (logging, verification, etc.) are added, the original business methods expand sharply Each method must take into account many other concerns while dealing with the core logic
  • Code dispersion: take the log requirement as an example. In order to meet this single requirement, you have to repeat the same log code in multiple modules (Methods) for many times If the logging requirements change, all modules must be modified
//Interface
public interface ArithmeticCalculator {
	
	public int add(int i,int j);
	public int sub(int i,int j);
	public int mul(int i,int j);
	public int div(int i,int j);

}
public class ArithmeticCalculatorLoggingImpl implements ArithmeticCalculator {

	//There are many codes that add log duplication, and the coupling is not good
	public int add(int i, int j) {
		System.out.println("the method add begin with ["+i+","+j+"]");
		int result=i+j;
		System.out.println("the method add ends with"+result);
		return result;
	}

	public int sub(int i, int j) {
		System.out.println("the method sub begin with ["+i+","+j+"]");
		int result=i-j;
		System.out.println("the method sub ends with"+result);
		return result;
	}

	public int mul(int i, int j) {
		System.out.println("the method mul begin with ["+i+","+j+"]");
		int result=i*j;
		System.out.println("the method mul ends with"+result);
		return result;
	}

	public int div(int i, int j) {
		System.out.println("the method div begin with ["+i+","+j+"]");
		int result=i/j;
		System.out.println("the method div ends with"+result);
		return result;
	}

}
2. Principle of mode
  • Wrap the object with a proxy object and replace the original object with the proxy object Any call to the original object must pass through the proxy The proxy object determines whether and when to transfer the method call to the original object

  • The code is as follows

//Test class
ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
		
		arithmeticCalculator = 
				new ArithmeticCalcualtorLoggingProxy(arithmeticCalculator).getLoggingProxy();
		
		int result = arithmeticCalculator.add(11, 12);
		System.out.println("result:" + result);
		
		result = arithmeticCalculator.div(21, 3);
		System.out.println("result:" + result);
//Dynamic proxy class
public class ArithmeticCalcualtorLoggingProxy {
	//Dynamic agent
	//Object to proxy
		private ArithmeticCalculator target;
		
		public ArithmeticCalcualtorLoggingProxy(ArithmeticCalculator target) {
		super();
		this.target = target;
	}

		//Return proxy object
		public ArithmeticCalculator getLoggingProxy(){
			ArithmeticCalculator proxy = null;
			
			ClassLoader loader = target.getClass().getClassLoader();
			Class [] interfaces = new Class[]{ArithmeticCalculator.class};
			InvocationHandler h = new InvocationHandler() {
				/**
				 * proxy: Proxy object. This object is not normally used
				 * method: Method being called
				 * args: Parameters passed in by calling method
				 */
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {

					String methodName = method.getName();
					//Print log
					System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));
					
					//Call target method
					Object result = null;
					
					try {
						//Before advice 
						result = method.invoke(target, args);
						//Return notification, you can access the return value of the method
					} catch (NullPointerException e) {
						e.printStackTrace();
						//Exception notification, you can access the exception of the method
					}
					
					//Post notification Because the method can make exceptions, the return value of the method cannot be accessed
					
					//Print log
					System.out.println("[after] The method ends with " + result);
					
					return result;
				}
			};
			/**
			 * loader: The class loader used by the proxy object. 
			 * interfaces: Specifies the type of proxy object That is, what methods can be used in the proxy object 
			 * h: When the method of the proxy object is specifically called, how to respond is actually to call the invoke method of InvocationHandler
			 */
			proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
			
			return proxy;
		}

}

  • The above dynamic proxy is still troublesome, and it is more convenient to use spring AOP.

2, Section oriented AOP

1. Concept and details of AOP
  • Aspect oriented programming is a supplement to traditional OOP object-oriented programming. The main programming object is section.

  • In AOP programming, you still need to define common functions. And you don't have to modify the affected classes. In this way, dependency crosscutting concerns are modularized into special objects (Facets).

  • Each logical thing is in one place, and the code is not scattered, which is easy to maintain and upgrade. Business modules are more concise. Only core business codes are included.

  • Basic language

    • Aspect: a special object whose crosscutting concerns are modularized.
    • Adive: work that must be completed in all aspects.
    • Target: the notified object.
    • Proxy: an object created after notification is applied to the target object.
    • Join point: a specific location where a program executes.
    • Pointcut: AOP locates a specific connection point through the pointcut. Analogy: the connection point is equivalent to the record of the database, and the tangent point is equivalent to the query condition.
  • AspectJ:AOP framework. Use Aop. Net based on AspectJ annotations or XML configuration

//applicationContext-aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!-- Automatically scanned packages -->
	<context:component-scan base-package="com.xiaojian.spring.AOP"></context:component-scan>

	<!-- send AspectJ The annotation of the works -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
  • Annotation declaration section
    • AspectJ supports five types of notification annotations:
      @Before: pre notification, which is executed before the method is executed
      @After: Post notification, executed after method execution
      @After returning: returns the notification, which is executed after the method returns the result
      @AfterThrowing: exception notification, after the method throws an exception
      @Around: around notification, around method execution
2. Preparation process of section (based on aspectJ annotation)
1. Pre notification and post notification
package com.xiaojian.spring.AOP;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

/**
 * AOP Aspect oriented programming
 * 1.Add jar package
 * 2.Add the namespace of aop to the configuration file of spring
 * 3.Using aop based on annotation
 * 	3.1 Configure automatically scanned packages in the configuration file
 * 	3.2 Add a configuration that makes the AspjectJ annotation work
 * 
 * 4.Write section class:
 *	4.1 A general Java class
 *	4.2 Add classes to be implemented additionally
 *
 * 5.Configuration section
 * 	5.1 The facet must be a bean in the IOC container. Add annotation @ Component annotation
 * 	5.2 Declarative Aspect: @ Aspect
 * 	5.3 Declaration notification, that is, the method corresponding to adding additional functions
 * 		@before Before advice 
 * 		@after Post notification
 * @author lovexx
 *
 */
@Aspect
@Component
public class LoggingAspect {
	
	//Parameter problem
	@Before("execution(public int com.xiaojian.spring.AOP.*.*(..))")
	public void beforeMethod(JoinPoint joinPoint){
		//Get the name and parameters of the method through the connection point
		 String methodName = joinPoint.getSignature().getName();
		Object[] args = joinPoint.getArgs();
		System.out.println("the method"+methodName+" begins with"+Arrays.asList(args));
	}
	
	@After("execution(public int com.xiaojian.spring.AOP.*.*(..))")
	public void afterMethod(JoinPoint joinPoint){
		String mathodName = joinPoint.getSignature().getName();
		
		System.out.println("the method"+mathodName+" ends ");
	}
	
}

  • Test class
package com.xiaojian.spring.AOP;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

	public static void main(String[] args) {
		
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-aop.xml");
		ArithmeticCalculator arithmeticCalculator=(ArithmeticCalculator)ctx.getBean("arithmeticCalculator");
		System.out.println(arithmeticCalculator.getClass().getName());
		
		int result = arithmeticCalculator.add(10, 2);
		System.out.println(result);		
	}
}
2. Return notification, exception notification and surround notification
  • Return notification whether the connection point returns normally or throws an exception, the post notification will be executed If you only want to log when the connection point returns, you should use the return notification instead of the post notification. And there is a return value.
/**
	 * A piece of code executed after the normal execution of a method
	 * The return notification can access the return value of the method
	 */
	@AfterReturning(value="declareJointPointExpression()",returning="result")
	public void afterReturnning(JoinPoint joinPoint,Object result){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("the method"+methodName+"ends with"+result);
	}
  • Exception notification exception notification occurs only when the connection point throws an exception
/**
	 * Exception notification
	 * Code that executes when an exception occurs in the target method
	 * You can access the exception object and specify that the notification code is executed when a specific exception occurs
	 */
	@AfterThrowing(value="declareJointPointExpression()",throwing="e")
	public void afterThrowing(JoinPoint joinPoint,Exception e){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("the method"+methodName+" occurs exception:"+e);
	}
  • Around Advice
    • Surround notification is the most powerful of all notification types and can fully control the connection point You can even control whether connection points are executed
      For wrap notifications, the parameter type of the join point must be ProceedingJoinPoint It is a sub interface of JoinPoint, which allows you to control when to execute and whether to execute the connection point
    • In the surround notification, you need to explicitly call the processed () method of the ProceedingJoinPoint to execute the proxied method If you forget to do so, the notification will be executed, but the target method will not be executed
    • Note: the method surrounding the notification needs to return the result after the execution of the target method, that is, call joinpoint proceed(); Otherwise, a null pointer exception will occur
@Around("execution(public int com.xiaojian.spring.aop.ArithmeticCalculator.*(..))")
	public Object aroundMethod(ProceedingJoinPoint pjd){
		
		Object result = null;
		String methodName = pjd.getSignature().getName();
		
		try {
			//Before advice 
			System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
			//Execution target method
			result = pjd.proceed();
			//Return notification
			System.out.println("The method " + methodName + " ends with " + result);
		} catch (Throwable e) {
			//Exception notification
			System.out.println("The method " + methodName + " occurs exception:" + e);
			throw new RuntimeException(e);
		}
		//Post notification
		System.out.println("The method " + methodName + " ends");
		
		return result;
	}
3. Import notification (you can understand it without code description)
3. Specify the priority of the section
  • When more than one slice is applied at the same connection point, their priority is uncertain unless explicitly specified
    The priority of the aspect can be specified by implementing the Ordered interface or using the @ Order annotation
  • Implement the Ordered interface. The smaller the return value of getOrder() method, the higher the priority
  • If @ Order annotation is used, the sequence number appears in the annotation
4. Reuse entry points
  • In AspectJ aspect, a Pointcut can be declared as a simple method through @ Pointcut annotation The method body of the Pointcut is usually empty because it is unreasonable to mix the Pointcut definition with the application logic
  • The access control character of the pointcut method also controls the visibility of the pointcut
/**
	 * Define a method to declare a pointcut expression Generally, there is no need to add other code to this method 
	 * Use @ Pointcut to declare Pointcut expressions 
	 * Other notifications that follow use the method name directly to reference the current pointcut expression 
	 */
	@Pointcut("execution(public int com.xiaojian.spring.aop.*.*(..))")
	public void declareJointPointExpression(){}
	
5. Configure AOP based on XML
  • configuration file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

	<!-- to configure bean -->
	<bean id="arithmeticCalculator" 
		class="com.xiaojian.spring.aop.xml.ArithmeticCalculatorImpl"></bean>

	<!-- Configuration of section bean. -->
	<bean id="loggingAspect"
		class="com.xiaojian.spring.aop.xml.LoggingAspect"></bean>

	
	<!-- to configure AOP -->
	<aop:config>
		<!-- Configure tangent expression -->
		<aop:pointcut expression="execution(* com.xiaojian.spring.aop.xml.ArithmeticCalculator.*(..))" 
			id="pointcut"/>
		<!-- Configuration aspect and notification -->
		
		<aop:aspect ref="loggingAspect" >
			<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
			<aop:after method="afterMethod" pointcut-ref="pointcut"/>
			<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
			<!--  
			<aop:around method="aroundMethod" pointcut-ref="pointcut"/>
			-->
		</aop:aspect>	
		
	</aop:config>

</beans>

  • Interface class
package com.xiaojian.spring.aop.xml;

public interface ArithmeticCalculator {
	
	public int add(int i,int j);
	public int sub(int i,int j);
	public int mul(int i,int j);
	public int div(int i,int j);
}
  • Implementation class of interface
package com.xiaojian.spring.aop.xml;

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

	public int add(int i, int j) {
		int result = i+j;
		return result;
	}

	public int sub(int i, int j) {
		int result = i-j;
		return result;
	}

	public int mul(int i, int j) {
		int result = i*j;
		return result;
	}

	public int div(int i, int j) {
		int result = i/j;
		return result;
	}

}
  • Test class
package com.xiaojian.spring.aop.xml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
	public static void main(String[] args) {
		
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-xml.xml");
		ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
		
		System.out.println(arithmeticCalculator.getClass().getName());
		
		int result = arithmeticCalculator.add(1, 2);
		System.out.println("result:" + result);
		
	}
}
  • Section class
package com.xiaojian.spring.aop.xml;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;

public class LoggingAspect {
	
	public void beforeMethod(JoinPoint joinPoint){
		//Get the name and parameters of the method through the connection point
		 String methodName = joinPoint.getSignature().getName();
		Object[] args = joinPoint.getArgs();
		System.out.println("the method"+methodName+" begins with"+Arrays.asList(args));
	}
	
	public void afterMethod(JoinPoint joinPoint){
		String mathodName = joinPoint.getSignature().getName();
		
		System.out.println("the method"+mathodName+" ends ");
	}
	
	public void afterReturnning(JoinPoint joinPoint,Object result){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("the method"+methodName+"ends with"+result);
	}

}

Tags: Spring AOP

Posted by Bad HAL 9000 on Mon, 09 May 2022 23:03:52 +0300