Deep understanding and use of Spring AOP

Spring AOP

1, Introduction

Aspect oriented programming (AOP) provides a supplement to Object-oriented Programming (OOP). The core unit of Object-oriented Programming is class, but the core unit of aspect oriented programming is aspects. Different from the object-oriented sequential process, AOP adopts a horizontal aspect approach and injects functions unrelated to the main business process, such as transaction management and log management.

A key component of Spring is the AOP framework. Although the Spring IoC container does not rely on AOP (which means you don't need to rely on AOP in IOC), AOP provides a very powerful middleware solution for Spring IoC.

2, Concept

Before further studying spring AOP, let's have a general concept of several basic terms of AOP. These concepts are not easy to understand and are relatively abstract. We can know that there are several concepts. Let's take a look at them together:

  • Aspect: aspect declaration is similar to class declaration in Java. Transaction management is one of the most typical applications of AOP. In AOP, facets are generally used with @ aspect annotation. In XML, you can use < AOP: aspect > to define a facet.
  • Join point: an operation during program execution, like executing a method or handling an exception. In Spring AOP, a join point represents the execution of a method.
  • Advice: there are four different notification methods for actions taken at a connection point (method exit) in the aspect (class): around, before, after, exception and return. Many AOP frameworks, including Spring, will recommend using notifications as interceptors and maintaining a series of interceptors around connection points.
  • Pointcut: represents a set of join points, the notification is related to the pointcut expression, and runs at any join point where the pointcut matches (for example, executing a method with a specific name). The concept of join points matched by pointcut expressions is the core of AOP. Spring uses AspectJ pointcut expression language by default.
  • Introduction: introduction can add new attributes and methods to existing objects. For example, you can use introduction to make bean s implement IsModified interface to simplify caching.
  • Target object: an object represented by one or more facets. Also known as "tangent object". Since Spring AOP is implemented using a runtime proxy, this object is always a proxy object.
  • AOP proxy: an object created by the AOP framework. In the Spring framework, there are two types of AOP proxy objects: JDK dynamic proxy and CGLIB proxy
  • Weaving: refers to the process of applying enhancements to the target object to create a new proxy object. It (such as AspectJ compiler) can be completed at compile time, load time or run time. Like other pure Java AOP frameworks, Spring AOP is woven at runtime.

1 Classification of notifications in spring AOP

  • Top note (Before Advice): calling function before the target method is invoked. Related classes org springframework. aop. MethodBeforeAdvice
  • Post base note (After Advice): calling function after the target method is invoked. Related classes org springframework. aop. AfterReturningAdvice
  • Return notification (After-returning): calling base note after successful execution of target method;
  • Base note notification (After-throwing): calling function after the exception of the target method is thrown. Related classes org springframework. aop. ThrowsAdvice
  • Surround: wrap up the whole target method and call the class org. Org related to the notification function before and after being called aopalliance. intercept. MethodInterceptor

2 three periods woven in spring AOP

  • Compile time: the aspect is woven in when the target class is compiled. This method requires a special compiler. AspectJ's weaving compiler weaves facets in this way.
  • Class loading period: the aspect is woven when the target class is loaded into the JVM. This method requires a special class loader, which can enhance the bytecode of the target class before the target class is introduced into the application.
  • Running period: the section is woven in at a certain period of application running. In general, when weaving the aspect, the AOP container will dynamically create a proxy object for the target object. Spring AOP adopts this weaving method.

3, Implementation mode

AOP is implemented in two ways: static weaving (AspectJ Implementation) and dynamic proxy (Spring AOP Implementation)

1 AspectJ

AspectJ is an AOP framework implemented in java. It can compile the code (generally during the compilation period) and make the code have the AOP function of AspectJ. AspectJ is the most mature and functional language in the current AOP framework. ApectJ mainly adopts the way of static weaving during compilation. During this period, AspectJ's acj compiler (similar to javac) is used to compile the aspect class into class bytecode, which is woven in when compiling the java target class, that is, compile the aspect class first and then the target class.

2 Spring AOP implementation

Spring AOP is implemented through dynamic proxy technology, and dynamic proxy is designed based on reflection. Spring AOP adopts two mixed implementation methods: JDK dynamic proxy and CGLib dynamic proxy. Let's understand them respectively

3 Summary

  • JDK dynamic proxy: the preferred method of Spring AOP. Whenever the target object implements an interface, the JDK dynamic proxy is used. The target object must implement the interface
  • CGLIB proxy: if the target object does not implement the interface, you can use CGLIB proxy.

4, Spring support for AOP

Spring provides two implementations of AOP: annotation based configuration and XML based configuration

1 @AspectJ support

In order to use @ AspectJ in Spring configuration, you need to enable Spring support to configure Spring AOP according to the @ AspectJ aspect and configure automatic proxy. Automatic proxy means that Spring will generate a proxy for the Bean based on the automatic proxy to intercept method calls and ensure that the interception is performed as needed.

You can use XML or Java style configuration to enable @ AspectJ support. In either case, you also need to ensure aspectjweaver The jar third-party library is located in the classpath of the application (version 1.8 or later).

2 enable @ AspectJ support

When using @ Configuration to support @ AspectJ, you need to add the @ EnableAspectJAutoProxy annotation, as shown in the following example, to enable the AOP proxy

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}

You can also use XML configuration to turn on @ AspectJ support

<aop:aspectj-autoproxy/>

By default, you have added the schema space of aop. If not, you need to add it manually

<?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 https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- bean definitions here -->
</beans>
(1) Declare a section

When @ AspectJ support is enabled, any bean defined in the application context has a class of @ AspectJ Aspect (with @ Aspect annotation), and Spring will automatically detect and use it to configure Spring AOP.

Define an aspect using XML configuration

<aop:aspect />

Define a section using annotations

@Aspect
public class MyAspect {}

Facets (that is, classes annotated with @ Aspect) have properties and methods like other classes. They can contain pointcuts, notifications, and introductory statements.

Section detection by automatic scanning

You can register Aspect classes as regular beans in the Spring XML configuration, or automatically detect them through classpath scanning - the same as any other Spring managed bean. However, a class annotated with @ Aspect will not be managed as a bean. You also need to add @ Component annotation on the class and hand it over to Spring as a component.

(2) Define a tangent point

A Pointcut consists of two parts: a signature containing the name and any parameters, and a Pointcut expression that determines the method we want to execute. In @ AspectJ annotation style AOP, the Pointcut expression needs to be annotated with @ Pointcut annotation (this expression is used as the signature of the method, and its return value must be void).

@Pointcut("execution(* transfer(..))") // Pointcut expression
private void definePointcut() {}// Method signature

The rules for writing pointcut expressions are as follows:

Now, assuming that the pointcut we need to configure only matches the specified package, it can be expressed by using the within() qualifier, as described in the following expression:

Please note that we use the & & operator to connect the execution() and within() indicators together to represent the relationship between and. Similarly, you can also use the | operation to represent the relationship between or. Use! Indicates a non-linear relationship.

In addition to the qualifier represented by within(), there are other qualifiers. The following is a list of qualifiers

AspectJ descriptordescribe
arg()Restrict the connection point matching parameter to the execution method of the specified type
@args()The execution method of restricting the connection point matching parameter to be annotated by the specified annotation
execution()Execution method for matching connection points
this()Restrict the bean reference of AOP proxy matching the connection point to the class of the specified type
targetRestrict the connection point to match a class whose target object is the specified type
@target()Restrict connection points to match specific execution objects, and the corresponding classes of these objects should have annotations of the specified type
within()Restrict connection points to match the specified type
@within()Restrict connection points to match the type of annotation specified
@annotationnQualifies matching join points with the specified annotation

Configure pointcuts using XML configuration

<aop:config>
	<aop:aspect ref = "">
  	<aop:poincut id = "" expression="execution(** com.cxuan.aop.definePointcut(......))"/>
  </aop:aspect>
</aop:config>
(3) Declare a notification

The above example is very clear. It defines an Audience aspect, and defines a pointcut of performance(). The following customize the notification before the performance, the return after the performance, and the failure of the performance. In addition, you also need to start @ EnableAspectJAutoProxy in the main method to start the automatic proxy.

In addition to using Java Config, you can also use XML based configuration

<bean id="logException" class="org.lanqiao.aop.LogException"></bean>
	<aop:config>
		<!-- Entry point (one end of the connection line: the specific method of the business class) -->
		<aop:pointcut expression="execution(public * org.lanqiao.service.impl.StudentServiceImpl.addStudent(..))"   id="poioncut3"/>
		<!-- (The other end of the connector: notification class) -->
		<aop:advisor advice-ref="logException"  pointcut-ref="poioncut3" />
	</aop:config>

Of course, this cut point definition is redundant. In order to solve this similar if else... For catastrophic business logic, you need to define a < AOP: pointcut >, and then use the pointcut ref attribute to point to the label above, as shown below

(4) Surround notification

Notifications of additional code can be executed before and after the execution of the target method. In the surround notification, the target method must be explicitly called before the target method can be executed. This explicit call is implemented through the processingjoinpoint. You can receive a formal parameter of this type in the surround notification, and the spring container will automatically pass in the object. Note that this parameter must be in the first formal parameter position of the surround notification.

The surround notification needs to return the return value, otherwise the real caller will not get the return value and can only get a null. Here is an example of a surround notification

 <aop:around method="around" pointcut-ref="pc1"/>
 public Object around(ProceedingJoinPoint jp) throws Throwable{
   System.out.println("1 -- around before...");
   Object obj = jp.proceed(); //--Explicitly call the target method
   System.out.println("1 -- around after...");
   return obj;
 }

5, AOP practice (annotated version)

1 Introduction

The StudentService class has a SayHello () method to say Hello, but before students say "Hello" to others, they must open their mouth and eyes, and then look back and smile after saying "Hello". The figure is as follows:

2. Implementation mode I

(1) pom dependency
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
(2) Service interface and implementation class
## Interface:
package org.ymx.service;

public interface StudentService {
     void sayHello();
}

## Implementation class:
package org.ymx.service.impl;

import org.springframework.stereotype.Service;
import org.ymx.service.StudentService;

@Service
public class StudentServiceImpl implements StudentService {
    @Override
    public void sayHello() {
        System.out.println("Hello");
    }
} 
(3) Section class
package org.ymx.service;

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

/**
 * The annotation @ Aspect identifies this class as a faceted class
 */
@Component
@Aspect
public class ServiceAspect {

    /**
     * Define the tangent point through the annotation @ Pointcut. stuSayHello() is just an identification, regardless of what it is,
     * The content in the method itself is also empty. Where the pointcut is used, the pointcut expression is directly referenced through the identification stuSayHello().
     */
    @Pointcut("execution(* org.ymx.service.impl.StudentServiceImpl.sayHello(..))")
    public void stuSayHello() {
    }

    /**
     * Before you speak -- open your mouth
     */
    @Before("stuSayHello()")
    public void openMouth() {
        System.out.println("Opens his mouth");
    }

    /**
     * Before you speak -- open your eyes
     */
    @Before("stuSayHello()")
    public void openEyes() {
        System.out.println("Open one's eyes");
    }

    /**
     * After talking -- smile
     */
    @After("stuSayHello()")
    public void smile() {
        System.out.println("Smile");
    }

}

(4) AOP global configuration
package org.ymx.config;


import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * Jdk Proxy: interface based proxy, which must be interface based, will generate sub objects of the interface of the target object.
 * Cglib Proxy: class based proxy, which does not need to be based on interface, will generate sub objects of the target object.
 *
 * 1. Annotate @ EnableAspectJAutoProxy to enable proxy;
 *
 * 2. If the attribute proxyTargetClass is false by default, it means that jdk dynamic proxy weaving enhancement is used;
 *
 * 3. If the property proxyTargetClass is set to true, it means that Cglib dynamic agent technology is used to weave in the enhancement;
 *
 * 4. If the property proxyTargetClass is set to false, but the target class does not declare an interface,
 *    Spring aop Cglib dynamic proxy will still be used, that is, cglib will be used for non interface classes to generate proxy.
 */

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("org.ymx.service")
public class AopConfig {
}
(5) Testing
package test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.ymx.config.AopConfig;
import org.ymx.service.StudentService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AopConfig.class) //Load global configuration
public class test01 {

    @Autowired
    private StudentService studentService;

    @Test
    public void Test01(){
        studentService.sayHello();
    }
}

During the test, the global configuration @ ContextConfiguration(classes = AopConfig.class) must be loaded, otherwise AOP will not work

Implementation mode 3

The only difference from the above implementation is that the facet class uses the @ Around annotation instead of @ Before and @ After

package org.ymx.service;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * The annotation @ Aspect identifies this class as a faceted class
 */
@Component
@Aspect
public class ServiceAspect {

    /**
     * Define the tangent point through the annotation @ Pointcut. stuSayHello() is just an identification, regardless of what it is,
     * The content in the method itself is also empty. Where the pointcut is used, the pointcut expression is directly referenced through the identification stuSayHello().
     */
    @Pointcut("execution(* org.ymx.service.impl.StudentServiceImpl.sayHello(..))")
    public void stuSayHello() {
    }

    @Around("stuSayHello()")
    public void testAround(ProceedingJoinPoint jp) {
        try {
            System.out.println("Opens his mouth");
            System.out.println("Open one's eyes");
            jp.proceed();//Execution method
            System.out.println("Smile");
        } catch (Throwable e) {
            System.out.println("Didn't say it");
        }
    }
}

4 test results

The content of the article is quoted from:

https://blog.csdn.net/yhl_jxy/article/details/78815636
https://github.com/crisxuan/bestJavaer

Tags: Java Programming Spring AOP

Posted by bslevin on Sat, 16 Apr 2022 22:46:44 +0300