AOP of Spring core

Keywords: Spring xml Programming Java

What is AOP

AOP (aspect oriented programming): aspect oriented programming is a complement to traditional object-oriented programming.
What do you mean?

For example, in the above figure, there are many identical function codes in different methods. Then we can extract these identical function codes and put them into the class, which is called tangent.

In fact, the principle of AOP is to use dynamic proxy. When we need to call the target object, Spring will help us to generate a proxy object, assemble the slice and core business logic code, and form a complete module. Even if we extract the code, it will not affect our normal use.

The benefits of doing so are:

  1. The business module is more concise and contains only the core business code. When we code, we can focus more on the writing of the core code. Secondly, it is easier to locate the problem during maintenance and debugging.
  2. When we need to modify the code of the public function, we just need to modify the aspect of the public function, and we don't need to modify it one by one.

Annotation configuration

For aspect oriented programming, we can use the AspectJ framework, which is the most complete and popular AOP framework in the Java community.

preparation

Therefore, if we want to use the AscpectJ framework, we must first import the dependent jar package:

  • aopalliance.jar
  • aspectj.weaver.jar
  • spring-aspects.jar

Secondly, we need to add aop Schema namespace to the configuration file.

Annotation based AOP

Next we can implement AOP using annotations.

Face to face programming, then first we have face to face. As mentioned above, facet is a class. Do we create a class, and this class is a facet? How does Spring recognize this as a facet?

We can use @ Aspect annotation. As long as we annotate this annotation on the corresponding class, this class is a tangent.

A slice is a notice. There can be multiple notices in a slice. The notice is the work to be done by the slice. When we want to call a business method, we will add these notices to a certain position in the business method, such as before and after the method, so as to form a complete business function. Notification is embodied in the code as a Java method with some annotations.

AspectJ supports 5 types of notifications. Their corresponding annotations are:

  • @Before: pre notification, executed before method execution.
  • @After: Post notification, which is executed after the method is executed, regardless of whether there is an exception in the method.
  • @AfterRunning: returns the notification, which is executed after the method returns the result.
  • @AfterThrowing: exception notification, which is executed after the method throws an exception.
  • @Around: around notification, we need to control it in our own way.

Before advice

(1)

@Before
public void beforeMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    Object [] args = joinPoint.getArgs();

    System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
}

(2)

After the notifications are configured, there is one thing missing. What methods should we apply these notifications to? We need to tell Spring, so we need to configure pointcut expressions.
For example:

@Before("execution(public int com.spring.aop.ArithmeticCalculator.*(..))")

Here are some examples:

expression meaning
execution(* com.atguigu.spring.ArithmeticCalculator.*(...)) All methods declared in the ArithmeticCalculator interface. The first "*" represents any modifier and any return value. The second "*" represents any method. “…” Match any number of parameters of any type. If the target class and interface are in the same package as the tangent class, the package name can be omitted.
execution(public * ArithmeticCalculator.*(...)) All public methods of the arithmetric calculator interface.
execution(public double ArithmeticCalculator.*(...)) Method in the ArithmeticCalculator interface that returns a value of type double.
execution(public double ArithmeticCalculator.*(double, ...)) The first parameter is a method of type double. “…” Match any number of parameters of any type.
execution(public double ArithmeticCalculator.*(double, double)) Parameter type is double, method of double type.
execution (* *.add(int,...)) || execution(* *.sub(int,...)) Pointcut expressions can be combined with "& &", "|", "!" and other operators.

(3)

In addition, for these annotations to work, you need to configure one thing in the configuration file. To enable AspectJ annotation support in the Spring IOC container, you need to define an empty XML element in the configuration file:

<! -- configure to automatically generate proxy objects for Java classes matching aspectJ annotations -- >
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

When the Spring IOC container detects the< aop:aspectj-autoproxy >Element automatically creates a proxy for the bean that matches the AspectJ facet.

JoinPoint class
Through JoinPoint class, we can access some link details, such as the name of the current method, parameters, etc.

Post notice

@After()
public void afterMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    System.out.println("The method " + methodName + " ends");
}

Return to notification

In the return notification, you can access the return value of the method. Just add the return attribute to the @ after returning annotation, and the return value will be passed to the corresponding value of the return attribute. Also add a parameter with the same name to the method.

@AfterReturning(value="declareJointPointExpression()",returning="result")
public void afterReturning(JoinPoint joinPoint, Object result){
    String methodName = joinPoint.getSignature().getName();
    System.out.println("The method " + methodName + " ends with " + result);
}

Exception notification

In exception notification, you can define what kind of exception occurs before exception notification is executed, and you can access the exception object. Similar to the return notification, we need to add the throwing property in the annotation and a parameter with the same name as the throwing property value in the method, which specifies what kind of exception occurs to perform the notification.

/**
  * Code that executes when an exception occurs to the target method
  * You can access the exception object; and you can 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 excetion:" + e);
}

Around Advice

Circular notification is similar to the whole process of dynamic agent, so we need to manually control when (before the method? After?) What code to execute.

Different from the above notification: the connection point parameter type surrounding the notification must be proceedingjoinpoint, which is a sub interface of JoinPoint. If you want to execute the proxy method, you must call the proceedingjoinpoint's proceed() method. In addition, the orbit notification must have a return value, which is the return value of the target method, that is ProceedingJoinPoint.proceed Return value of () method.

/**
   * Circular notifications need to carry parameters of type ProceedingJoinPoint 
   * Circular notification is similar to the whole process of dynamic agent: the parameter of ProceedingJoinPoint type can decide whether to execute the target method or not
   * And the circular notification must have a return value, which is the return value of the target method
*/
@Around("execution(public int com.atguigu.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()));
        //Implementation target method
        result = pjd.proceed();
        //Return to 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 notice
    System.out.println("The method " + methodName + " ends");

    return result;
}

Pointcuts are reusable

If we specify a pointcut expression after each annotation, it's very troublesome. If we modify it, we need to modify it one by one. So can we extract pointcut expressions?

The answer is: Yes. We can use @ Pointcut annotation to configure unified Pointcut expression. We only need to use @ Pointcut annotation above one method, and other annotations directly reference the method name.

The access controller of the pointcut method also controls the visibility of the pointcut. If pointcuts are to be shared across multiple facets, it is best to focus them on a common class. In this case, they must be declared public. When you introduce this pointcut, you must include the class name as well. If the class is not in the same package as this aspect, the package name must also be included.

/**
  * Defines a method for declaring pointcut expressions. Generally, there is no need to add other code to this method.
  * Use @ Pointcut to declare Pointcut expressions.
  * The other notifications that follow use the method name directly to refer to the current pointcut expression.
  */
@Pointcut("execution(public int com.spring.aop.ArithmeticCalculator.*(..))")
public void declareJointPointExpression(){

}

@Before("declareJointPointExpression()")

Configure slice priority

If we have several aspects, Spring doesn't know who should execute first and who should. However, we can explicitly specify the execution Order between them, and the priority of facets 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 is, the higher the priority is. @The Order annotation is similar. The smaller the value, the higher the priority.

@Aspect
@Order(1)
public class LoggingAspect {

}

XML configuration

Facet supports not only annotation configuration, but also configuration by using configuration file. Normally, however, annotation based declarations take precedence over XML based declarations.

(1)
Similar to annotation configuration, first we need to configure a facet. The class of the tangent should be instantiated first.

<!-- Configure tangent bean. -->
<bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect"></bean>

<!-- to configure AOP -->
<aop:config>
    <!-- Configure facets -->
    <aop:aspect ref="loggingAspect" order="2">

    </aop:aspect>	
</aop:config>

(2)
The second step is to configure the pointcut expression. Use< aop:pointcut >Label, if configured in< aop:config >Under the label, all facets are available, if configured in the< aop:aspect >Under label, it can only be used in this tangent.

<!-- to configure AOP -->
<aop:config>
    <!-- Configure tangent expression -->
    <aop:pointcut id="pointcut" expression="execution(*com.spring.aop.xml.ArithmeticCalculator.*(int, int))" />
</aop:config>

(3)
The third step is to configure each notice. Each notice corresponds to this different aop label. In the notice, you can use the pointcut property to configure the pointcut expression separately, or you can use the pointcut ref property to reference the configured pointcut expression.

<!-- Configure tangent bean. -->
<bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect"></bean>

<bean id="vlidationAspect" class="com.atguigu.spring.aop.xml.VlidationAspect"></bean>

<!-- to configure AOP -->
<aop:config>
    <!-- Configure tangent expression -->
    <aop:pointcut id="pointcut" expression="execution(* com.spring.aop.xml.ArithmeticCalculator.*(int, int))" />
    
    <!-- Configure sections and notifications -->
    <aop:aspect ref="loggingAspect" order="2">
        <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
        <aop:after method="afterMethod" pointcut-ref="pointcut"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
        <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>
        <!--  
            <aop:around method="aroundMethod" pointcut-ref="pointcut"/>
        -->
    </aop:aspect>	
    
    <aop:aspect ref="vlidationAspect" order="1">
        <aop:before method="validateArgs" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

Posted by poring on Thu, 25 Jun 2020 03:36:00 -0700