Do you really understand the obscure concept of Spring AOP?

AOP core knowledge points

Aspect, Pointcut, Joinpoint, Advice, Weaving

Just looking at the concept makes me feel confused, and I can’t remember it at all.

Principles and Ideas

asm framework

Through a certain technology, the bytecode file is dynamically created according to the requirements during the running of the program. The generated bytecode can be stored in the memory or overflowed to the disk, but in the end it needs to be a class file. This asm framework is dynamically generated.

Matching method (regular expression)

Regenerate or directly add the relevant logic of log processing on the basis of the original class file. The name given to this core function is AOP.

AOP is a kind of programming idea. When practicing AOP, it is necessary to formulate some normative requirements, and implement AOP functions according to the normative requirements.

With the specification, it is necessary to install some kind of rules for a match, such as finding the method that needs to realize the AOP function through certain rules.

The matching method is performed through something similar to a regular expression. You can pass the method name, method parameters, method return value, class package name (fully qualified name), and so on.

Through the expression expression, find this entry point

Expression syntax

Spring aop official website syntax is as follows

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
          throws-pattern?)

translates to mean

execution(<modifier mode>?<return type mode><method name mode>(<parameter mode>)<exception mode>?)

Among them, except the return type mode, method name mode and parameter mode, other items are optional.

execution(* com.shuweicloud.bi.api.web..*Controller.list(..))

identifier

meaning

execution()

the body of the expression

first * symbol

represents an arbitrary return type

com.shuweicloud.bi.api.web

The package name of the service cut by AOP, that is, the business class that needs to be cross-cut

behind the bag..

Indicates the current package and subpackages

second * symbol

Indicates the class name, * means all classes

.list(..)

Indicates the matching method name list(), brackets indicate parameters, and two dots indicate any parameter type.

Notification

After the method (entry point) is matched by the above expression, which positions of the method need to be added?

AOP provides 5 notification methods

Notification method:
* Pre-notification (@Before): logStart(): run before the target method (div) runs
* Post notification (@After): logEnd(): run after the target method (div) finishes running
* Return notification (@AfterReturning): logReturn(): run after the target method (div) returns normally
* Exception notification (@AfterThrowing): logException: run after an exception occurs in the target method (div)
* Surround notification (@Around): dynamic proxy, manually advance the target method to run (joinPoint.procced())

Annotated version of AOP operation steps

  1. Import the jar package required by Spring AOP. (spring-aspects)
  2. Define a business logic class (MathCalculator); print the log when the business logic is running (before the method, the end of the method, exception, etc.).
  3. Define a log aspect class (LogAspects): the methods in the aspect class need to dynamically perceive where the MathCalculator.div() method runs?
  4. Mark when the target method of the aspect class is run.
  5. Add both the aspect class and the business logic class (the class where the target method is located) into the container;
  6. You must tell spring which class is the aspect class, and add an annotation @Aspect to the aspect class
  7. Add the annotation @EnableAspectJAutoProxy to the configuration class to enable the annotation-based AOP mode

maven configuration

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>

Business class: MathCalculator class

public class MathCalculator {

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

Noodles

@Aspect
public class LogAspects {
\t
/ / Extract the common pointcut expression
//1, this class reference
//2, other aspect references
@Pointcut("execution(public int cn.com.git.annotation.aop.MathCalculator.*(..))")
public void pointCut() {
\t\t
}
@Around("pointCut()")
public Object logArround(ProceedingJoinPoint pjp){
Signature signature = pjp. getSignature();
Object[] args = pjp. getArgs();
Object result = null;
System.out.println("Execute the around method, the obtained parameters are: " + Arrays.asList(args));
try {
result = pjp. proceed(args);
} catch (Throwable throwable) {
throwable. printStackTrace();
System.out.println("Surrounding the usual exception: " + signature.getName());
} finally {
System.out.println("Surround return notification, return result" + result);
}
return result;
}
//@Before cuts in before the target method; pointcut expression (specify which method to cut in)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint. getArgs();
System.out.println(joinPoint.getSignature().getName() + "before run... The parameter list is: {" + Arrays.asList(args) + "}");
}
@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {

System.out.println(joinPoint.getSignature().getName() + "after division ends");
}
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println(joinPoint.getSignature().getName() + "afterReturning division returns normally... The result of operation: {" + result + "}");
}
//JoinPoint must appear at the first place in the parameter list
@AfterThrowing(value = "pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception) {
System.out.println(joinPoint.getSignature().getName() + "Division exception... Exception information: {" + exception + "}");
}

Config class

@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
//Business logic class is added to the container
@Bean
public MathCalculator getMathCalculator() {
return new MathCalculator();
}
//The facet class is added to the container
@Bean
public LogAspects getLogAspects() {
return new LogAspects();
}
}

source debug

Through the AOP annotation version, create a new test class for testing

public class IOCTest_AOP {

@Test
public void testAOP() {
\t\t
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
\t\t
MathCalculator mathCalculator = applicationContext. getBean(MathCalculator. class);
mathCalculator. div(2, 2);
\t\t
applicationContext. close();
}
}

1. Get the Spring context context Beanfactory object

16 classes are loaded in the Spring context benfactory to protect custom classes and classes required by the container itself.

2. Proxy class generated by custom class (DynamicAdvisedInterceptor)

As can be seen from the name, the custom class generates a dynamic proxy class.

When the div() method of the business class is executed, the method of the proxy class is actually executed. As can be seen from the figure above, the class corresponding to the called CALLBACK_0 is the interceptor() method of the DynamicAdvisedInterceptor class.

3. Chain of Responsibility

In the interceptor() method, a chain of responsibility is obtained

There are 5 notification types stored in the chain.

After getting the chain, execute the proceed() method.

4. proceed() method.

The proceed() method is in the parent class ReflectiveMethodInvocation of the CglibMethodInvocation class, the method is as follows

public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
        //Start calling from the interceptor with index -1, and increment in order. If the interceptors in the interceptor chain are iteratively called, start calling the target function, which is completed through the reflection mechanism.
if (this. currentInterceptorIndex == this. interceptorsAndDynamicMethodMatchers. size() - 1) {
return invokeJoinpoint();
}
//Get the next interceptor to be executed, and process it along the defined interceptorOrInterceptionAdvice chain
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get( + + this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
            //Here is the dynamic matching judgment for the interceptor. This is where the pointcut trigger is matched. If it matches the defined pointcut, the advice will be executed.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this. targetClass != null ? this. targetClass : this. method. getDeclaringClass());
if (dm. methodMatcher. matches(this. method, targetClass, this. arguments)) {
return dm. interceptor. invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice). invoke(this);
}
}

Through the + + operation of the currentInterceptorIndex parameter, get the chain notification saved by the chain. Execute the invoke() method. Press F7 to enter the invoke method, and you can see what operations the specific invoke has done.

The first executed class is the invoke method of the ExposeInvocationInterceptor class

From the screenshot, you can see that invoke calls the mi.proceed() method. And mi here is ReflectiveMethodInvocation. So go back to the parent class ReflectiveMethodInvocation of the CglibMethodInvocation class and continue to call the proceed() method.

Earlier we know that the proceed() method will perform the ++ operation of the chain to get the next object on the chain.

The second class: AspectJAfterThrowingAdvice

Similarly, this notification class also executes mi.proceed();

The third class, AfterReturningAdviceInterceptor

The fourth class, AspectJAfterAdvice

The fifth class, MethodBeforeAdviceInterceptor

It can be seen that when executing Before, the before method marked by the AOP annotation is first used. After execution, it returns to mi.proceed(). Continue to chain execution.

Execute the @Before method

When currentInterceptorIndex ++ reaches 5,

question,

1. Recursive execution is not easy to understand, and it is necessary to deepen the understanding of this concept

2. @Around surrounds the execution logic

3. The relationship between the executed classes and the defined chains

ExposeInvocationInterceptor–>AspectJAfterThrowingAdvice–>AfterReturningAdviceInterceptor–>AspectJAroundAdvice–>MethodBeforeAdviceInterceptor–>LogAspects–>(@Around notification execution proceed

() the first half)–>LogAspects(@Before)–>the second half of the @Around method–>@after method–>@AfterReturning method.

Is the order of the chains the same?

no the same. The order on the chian is topologically sorted, and the order may be different, so the purpose of putting it on the chain is to make it easy to find the next task that needs to be executed.