Spring AOP

AOP overview

Aspect Oriented Programming is a technology that realizes program functions based on dynamic proxies through pre-compilation and runtime. AOP is a continuation of OOP, a hot topic in software development, and an important content in Spring framework. It is a derivative paradigm of functional programming. AOP can be used to isolate each part of the business logic, so as to reduce the degree of coupling between each part of the business logic, improve the reusability of the program, and improve the efficiency of development.

AOP related terms

The following aop concepts are presented in the original article on the Spring website.

  • Aspect: Represents an Aspect and is a declaration of pointcuts and advice.
  • JoinPoint: Method connection point during program execution, such as method execution or exception handling. Join points in AOP represent only method types
  • Advice: Declared at a specific Advice join point, Advice includes pre, post, and wrap
  • Pointcut: A Pointcut that specifies the Pointcut position of a method through an expression
  • Introduction: You can add methods or fields dynamically without changing the code.
  • Target object: The Target object, also represented as a proxy object
  • AOP Proxy: Represents an AOP proxy class object
  • Weaving: Connects sections to other application types or objects to create proxy objects

Notification type

There are five spring Aop advice types:

  1. Before advice: Before advice, called Before method execution, does not affect join point execution unless an exception is thrown.

  2. After RETURNING advice: Executed advice is run After normal completion of the join point and not executed if an exception is thrown

  3. After Throwing advice: After throwing advice, if the method exits by throwing an exception, the advice is executed.

  4. After (finally) advice: A final notification that is executed regardless of whether the method executed successfully or without exceptions.

  5. Around advice: Circular advice, executed before and after method execution.

Xml-based AOP configuration

Method 1: Use the Spring interface

The header files you need to import to use the AOP functionality.


      
<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">
</beans>
Copy the code

The following demonstrates the power of Aop by adding logging.

  1. Create the Maven project and import the following dependencies.
<dependencies>
        <! -- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.9. RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
Copy the code
  1. Write the UserService class to provide mock add, delete, change and query methods for demonstration.
public interface UserService {
    /* * * add/delete/add/delete */
    String insert(String name);
    
    int delete(int id);
    
    String update(String name);
    
    int query(int id);
}
Copy the code
  1. Write implementation classes, print out specific functionality, and finally use AOP techniques to add additional logging information
public class UserServiceImpl implements UserService{
    
    @Override
    public String insert(String name) {
        System.out.println("Add one"+ name + "Data");
        return name;
    }
    @Override
    public int delete(int id) {
        System.out.println("Deleted" + id + "No. Data");
        return id;
    }
    @Override
    public String update(String name) {
        System.out.println("Updated" + name + "Data");
        return name;
    }
    @Override
    public int query(int id) {
        System.out.println("Query" + id + "No. Data");
        returnid; }}Copy the code
  1. Write two classes, one implementationMethodBeforeAdviceInterface, an implementationAfterReturningAdviceInterface, respectively to achieve the effect of pre-notification and post-return notification.
/* Pre-notification class */
public class BeforeLog implements MethodBeforeAdvice {
    / * * *@paramMethod: executes the target object method *@paramArgs: target object parameter *@paramTarget: indicates the target object *@throwsThrowable: Abnormal */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"The" + method.getName() + "Executed."); }}/* Post-notification class */
public class AfterLog implements AfterReturningAdvice {
    / * * *@paramReturnValue: the returned value *@paramMethod: indicates the target object method *@paramArgs: target object parameter *@paramTarget: indicates the target object *@throwsThrowable: Abnormal */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Executed." + method.getName() + "Method, return result:+ returnValue); }}Copy the code
  1. Inject the written classes into the Spring Bean container, and then configure the AOP-related content.

First, register three classes into the bean.

<! Register three beans with id as unique identifier -->
<bean id="userService" class="com.spring.aop.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.spring.aop.log.BeforeLog"/>
<bean id="afterLog" class="com.spring.aop.log.AfterLog"/>
Copy the code

The second step is to configure the pointcut and notification class. The notification class is the information output to the target class. You can configure the pointcut and notification class in either of the following ↓ ways

Approach one: Specify advice classes and pointcuts directly in the < AOP: Advisor /> tag.

<aop:config>
 <aop:advisor advice-ref="beforeLog" pointcut="execution(* com.spring.aop.service.UserServiceImpl.*(..) )"/>
 <aop:advisor advice-ref="afterLog" pointcut="execution(* com.spring.aop.service.UserServiceImpl.*(..) )"/>
</aop:config>
Copy the code

Approach two: Configure the pointcut first according to the < AOP: Pointcut /> tag, and then let the configuration advice reference the pointcut

<aop:config>
    <aop:pointcut id="pointcut" expression="execution(* com.spring.aop.service.UserServiceImpl.*(..) )"/>
    
		<! Configure the logging class to cut into, referring to the section above.
    <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>

<! * matches all types.* executes all methods in the class. (..) Ideal-ref: indicates the unique id of the bean to reference the injection. Pointcut-ref: indicates the id of the configured slice to reference the configured slice. Expression =" Execution (* *.. *. * (..) ": expression can also be used to match all classes in a package. -->
Copy the code

There are two ways to configure the facets and advice from above. The first way is to lose a line of configuration but write an expression to specify the pointcut each time, and the second way is to configure the pointcut once so that multiple advice can reference it.

  1. Writing test classes

The type of the bean to be obtained must also be the implementation UserService interface type, not the implementation class, or an error will be reported

@Test
public void test(a) {
    ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
    UserService service = context.getBean("userService",UserService.class);
    
    service.insert("Bear");
    service.delete(2);
    service.update("Bear");
    service.query(1);
}
Copy the code

Test results:

It can be seen from the above that the four methods execute the printed log information of the pre-notification and post-notification respectively, and the post-notification also executes the return result.

Method 2: Custom class implementation

The first way is by class spring provides the interface to realize the function of Aop, advantage is provided through the interface reflection mechanism can use more features, the downside is that you need to create many classes to implement one by one, the method can also use custom classes and inform the way of function, the main function to configured in the XML tags to be realized, In a custom class, you just write the logging methods or information to add.

  1. Start by creating a custom method class.
public class General {

    public void before(a) {
        System.out.println("-------- Notification before method execution --------");
    }

    public void after(a) {
        System.out.println("-------- notification after method execution --------");
    }

    public Object around(ProceedingJoinPoint point) throws Throwable {
        
        Object Value;
        Object[] args = point.getArgs();// Get the parameters required to execute the method
        System.out.println("Around is being preceded.");
        Value = point.proceed(args); // Explicitly invoke the business layer pointcut method
        System.out.println("Around post-executed.");
        returnValue; }}Copy the code

Configuring surround advice requires the ProceedingJoinPoint interface, which has a method proceed() that is equivalent to explicitly calling the pointcut method. This interface can be used as a method parameter to circumvent advice and is used by the implementation class of this interface when the program executes.

  1. Configure the XML file
<! Method 2: Implement aop with custom classes
<! Inject the bean instance of the custom class -->
<bean id="definition" class="com.spring.aop.log.General"/>

<aop:config>
    <! Configure the section to crosscut custom methods into the target class.
    <aop:aspect ref="definition">
        <! Configure pointcuts, that is, the target class to add functionality
        <aop:pointcut id="point" expression="execution(* com.spring.aop.service.UserServiceImpl.*(..) )"/>
        
        <! Configure notification methods, specifying three methods in the custom class.
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" pointcut-ref="point"/>
        <aop:around method="around" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>
Copy the code
  1. The test class
@Test
public void test(a) {
    ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
    UserService service = context.getBean("userService", UserService.class);
    service.insert("Bear");
    service.delete(2);
    service.update("Bear");
    service.query(1);
}
Copy the code

Test results.

Test results show that the above three functions to inform all successful execution in the output method, according to the order and inform on top front, rear notice at the bottom, and around the notice are front and rear notice including inside, the real business method is in the most inside, realized without change the business code to add logging methods in the code.

Annotation based AOP configuration

The annotation implementation enables the configuration of custom aspect pointcuts and advice advice with much less fuss than XML configuration files, and the same functionality can be achieved with a small amount of configuration.

Here’s an example based on XML configuration.

Customizing the aspect information first requires creating a class, then adding its annotations and pointcuts and notification methods.

  1. Business class and its implementation class.
public interface UserService {
    /* * * add/delete/add/delete */
    String insert(String name);

    int delete(int id);

    String update(String name);

    int query(int id);
}

/* Implementation class */
public class UserServiceImpl implements UserService{
    @Override
    public String insert(String name) {
        System.out.println("Add one"+ name + "Data");
        return name;
    }

    @Override
    public int delete(int id) {
        System.out.println("Deleted" + id + "No. Data");
        return id;
    }

    @Override
    public String update(String name) {
        System.out.println("Updated" + name + "Data");
        return name;
    }

    @Override
    public int query(int id) {
        System.out.println("Query" + id + "No. Data");
        returnid; }}Copy the code
  1. Create a custom function class
@Aspect // Annotations that represent facets
@Component // Inject into the Spring container
public class AopAnnotation {

    // Define a pointcut method to unify the definitions of other methods
    @Pointcut("execution(* com.spring.aop.service.UserServiceImpl.*(..) )"
    public void log(a){}

    @Before("log()") // Pre-notification
    public void before(a) {
        System.out.println("-------- Notification before method execution --------");
    }
    
    @After("log()") // post notification
    public void after(a) {
        System.out.println("-------- notification after method execution --------");
    }

    @Around("log()") // Wrap around the notification
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Object Value;
        Object[] args = point.getArgs(); // Get the parameters required to execute the method
        System.out.println("Around is being preceded.");
        Value = point.proceed(args);  // Explicitly invoke the business layer pointcut method
        System.out.println("Around post-executed.");

        returnValue; }}Copy the code
  1. Inject beans and turn on support for annotations

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

    <! Insert bean into UserService -->
    <bean id="userService" class="com.spring.aop.service.UserServiceImpl"/>
    <! Scan components injected with custom classes -->
    <context:component-scan base-package="com.spring.aop.log"/>
    <! Turn on support for annotations -->
    <aop:aspectj-autoproxy/>

</beans>
Copy the code
  1. The test class.
@Test
public void test(a) {
    ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
    UserService service = context.getBean("userService", UserService.class);
    service.insert("Bear"); // Only one insertion method is tested here.
}
Copy the code

The test results

You can see that Around and pre – and post-notification are executed before and after the insert method, respectively.