Getting Started with Spring: Explanation on the use of Spring AOP

Posted by kobayashi_one on Fri, 23 Aug 2019 04:57:15 +0200

1. What is AOP?

AOP is the abbreviation of Aspect Oriented Programming, which means Face-Oriented Programming. It is a technology that unifies the maintenance of program functions through precompilation and runtime dynamic agents.

You can think of AOP as a complement to OOP(Object Oriented Programming). It is mainly used in logging, performance statistics, security control scenarios, etc. AOP can reduce the coupling between parts of business logic, focus only on their own business logic implementation, thus improving the readability and readability of programs.Maintainability.

For example, we need to record all input and output of external interfaces in our project so that we can locate the cause when problems occur. Of course, adding code entry and output to each external interface's code can do the same thing, but this hard-coding is not very friendly, flexible, and log booksThe body and interface have nothing to do with the core functionality to be achieved.

At this point, we can define logging capabilities into a facet, then declaratively define when and where to use this facet without modifying any of the external interfaces.

Before explaining how to implement this, let's look at a few AOP terms.

1.1 Notification (Advice)

In AOP terms, the work to be done by a facet is called a notification, which defines what the facet is and when to use it.

There are five types of notifications for Spring facets:

  • Before: Call the notification function before the target method is called
  • After notification: Calls a notification after the target method completes, regardless of the method's output
  • After-returning notification: Call notification after successful execution of the target method
  • After-throwing: Call notification after the target method throws an exception
  • Around notification: Notification wraps the notified method and executes custom behavior before and after the notified method call

1.2 Join point

A join point is a point where a tangent can be inserted during application execution, which can be when a method is called, when an exception is thrown, or when a field is modified.

1.3 Pointcut

The tangent point is to narrow the range of connection points notified by the tangent, that is, where the tangent is executed.We typically specify a tangent point using explicit class and method names or using regular expressions to define matching class and method names.

1.4 Face (Aspect)

A slice is a combination of notification and a point of tangency.Notifications and tangents together define what a facet is: what it is, when and where it functions.

1.5 Introduction

The introduction allows us to add new methods or attributes to existing classes without modifying them.

1.6 Weaving

Weaving is the process of applying facets to the target object and creating new proxy objects.

Faces are woven into the target object at specified connection points, and there are several points in the life cycle of the target object that can be woven into:

  • Compile-time: Faces are woven at the target class compile time.This approach requires a special compiler.AspectJ's weaving compiler knits facets in this way.
  • Class Load Period: Faces are woven when the target class is loaded into the JVM.This approach requires a special class loader (ClassLoader), which enhances the byte code of the target class before it is introduced into the application.
  • Runtime: The facets are woven at a time when the application is running.In general, when facets are woven in, the AOP container dynamically creates a proxy object for the target object.This is how Spring AOP weaves into facets.

2. Spring support for AOP

2.1 Dynamic Proxy

Spring AOP is built on a dynamic proxy, that is, the Spring runtime dynamically creates proxy objects for the target object.

The proxy class encapsulates the target class, intercepts calls to the notified method, and forwards the calls to the real target bean.

When a proxy class intercepts a method call, facet logic is executed before the target bean method is called.

2.2 Weaving Cutting Timing

By wrapping facets in a proxy class, Spring weaves facets into Spring-managed beans at run time, that is, Spring does not create proxy objects until the beans that need to be proxied are applied.

Because the Spring runtime creates proxy objects, we do not need a special compiler to weave Spring AOP facets.

2.3 Connection Point Limitation

Spring only supports method-level join points. If you need field-level or constructor-level join points, you can supplement Spring AOP with AspectJ.

3. Spring AOP usage

Suppose we have a live performance interface Performance and its implementation class SleepNoMore:

package chapter04.concert;

/**
 * Live performances, such as stage plays, movies, concerts
 */
public interface Performance {
    void perform();
}
package chapter04.concert;

import org.springframework.stereotype.Component;

/**
 * Drama: Sleep No More on a Sleepless Night
 */
@Component
public class SleepNoMore implements Performance {
    @Override
    public void perform() {
        System.out.println("The Drama Sleepless Night Sleep No More>");
    }
}

Now that it's a performance, you need the audience. Suppose we need to: before a performance, the audience seats and mutes the phone, and after a performance, the audience applauds. If the performance fails and the audience returns, we can certainly write these logic in the perform() method above, but this is not recommended becauseThese logics have nothing to do with the core of the performance. Even if the audience does not mute the phone or applause it after the performance, it will not affect the performance.

For this requirement, we can use AOP.

3.1 Defining a Face

First, define the facets of an audience as follows:

package chapter04.concert;

import org.aspectj.lang.annotation.Aspect;

/**
 * Audience
 * Define as a facet using the @Aspect annotation
 */
@Aspect
public class Audience {
}

Note: The @Aspect annotation indicates that the Audience class is a facet.

3.2 Define pre-notifications

Define a pre-notification in the Audience facet as follows:

/**
 * Before the performance, the audience sits
 */
@Before("execution(* chapter04.concert.Performance.perform(..))")
public void takeSeats() {
    System.out.println("Taking seats");
}

/**
 * Mute phone before performance
 */
@Before("execution(* chapter04.concert.Performance.perform(..))")
public void silenceCellPhones() {
    System.out.println("Silencing cell phones");
}

The key code here is @Before("execution(* chapter04.concert.Performance.perform(.))"), which defines a preceding notification, where execution(* chapter04.concert.Performance.perform(.)) is called an AspectJ tangent expression, and each section is explained as follows:

  • @Before: This annotation is used to define a pre-notification that the notification method will execute before the target method is called
  • Execution: triggered on method execution
  • *: Indicates that we don't care about the type of method return value, that is, any type
  • chapter04.concert.Performance.perform: Use fully qualified class and method names to specify the method to add a pre-notification
  • (..)): The parameter list for a method uses (..) to indicate that we don't care what the method's parameters are, that is, it can be of any type

3.3 Define Post Notification

Defining post-notifications in Audience facets is as follows:

/**
 * End of performance, whether successful or unsuccessful
 */
@After("execution(* chapter04.concert.Performance.perform(..))")
public void finish() {
    System.out.println("perform finish");
}

Note: @After comment is used to define post-notification, which is called after the target method returns or throws an exception

3.4 Define return notifications

Return notifications are defined in Audience facets as follows:

/**
 * After the performance, applause
 */
@AfterReturning("execution(* chapter04.concert.Performance.perform(..))")
public void applause() {
    System.out.println("CLAP CLAP CLAP!!!");
}

Note: @AfterReturning annotation is used to define the return notification, the notification method is called after the target method returns

3.5 Define exception notifications

Define exception notifications in the Audience facet as follows:

/**
 * After the performance failed, the audience asked for a refund
 */
@AfterThrowing("execution(* chapter04.concert.Performance.perform(..))")
public void demandRefund() {
    System.out.println("Demanding a refund");
}

Note: @AfterThrowing annotation is used to define exception notifications, notification methods are called after the target method throws an exception

3.6 Define a reusable tangent expression

Carefully, you may find that among the five tangent points we defined above, the tangent expression is the same, which is obviously not good, but we can use the @Pointcut annotation to define reusable tangent expressions:

/**
 * Reusable Tangent Points
 */
@Pointcut("execution(* chapter04.concert.Performance.perform(..))")
public void perform() {
}

Then all five previously defined tangent points can reference this tangent expression:

/**
 * Before the performance, the audience sits
 */
@Before("perform()")
public void takeSeats() {
    System.out.println("Taking seats");
}

/**
 * Mute phone before performance
 */
@Before("perform()")
public void silenceCellPhones() {
    System.out.println("Silencing cell phones");
}

/**
 * End of performance, whether successful or unsuccessful
 */
@After("perform()")
public void finish() {
    System.out.println("perform finish");
}

/**
 * After the performance, applause
 */
@AfterReturning("perform()")
public void applause() {
    System.out.println("CLAP CLAP CLAP!!!");
}

/**
 * After the performance failed, the audience asked for a refund
 */
@AfterThrowing("perform()")
public void demandRefund() {
    System.out.println("Demanding a refund");
}

3.7 Unit Test

The new configuration class ConcertConfig is as follows:

package chapter04.concert;

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

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {
    @Bean
    public Audience audience() {
        return new Audience();
    }
}

Note: Unlike before, we used the @EnableAspectJAutoProxy annotation, which is used to enable automatic proxy.

Create a new Main class and add the following test code to its main() method:

package chapter04.concert;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConcertConfig.class);

        Performance performance = context.getBean(Performance.class);
        performance.perform();

        context.close();
    }
}

Run the code with the following output:

Silencing cell phones

Taking seats

The Drama Sleepless Night Sleep No More>

perform finish

CLAP CLAP CLAP!!!

Modify the perform() method of the SleepNoMore class slightly so that it throws an exception:

@Override
public void perform() {
    int number = 3 / 0;
    System.out.println("The Drama Sleepless Night Sleep No More>");
}

Run the code again, and the output is as follows:

Silencing cell phones

Taking seats

perform finish

Demanding a refund

Exception in thread "main" java.lang.ArithmeticException: / by zero

This also means that the @After annotation will execute regardless of whether the target method is successfully executed or not, but the @AfterReturning annotation will only execute if the target method is successfully executed.

It is worth noting that the facet class using the @Aspect annotation must be a bean (declared in any way) or the facet will not take effect because the AspectJ automatic proxy only creates proxy classes for beans using the @Aspect annotation.

That is, if we delete or comment out the following code in the ConcertConfig configuration class:

@Bean
public Audience audience() {
    return new Audience();
}

The result of the run will change to:

Sleep No More on Sleepless Nights

3.8 Create surround notifications

We can create surround notifications using the @Around annotation, which allows you to customize your logic before and after invoking the target method.

Therefore, the five previously defined tangent points can now be defined in one tangent point. To not affect the previous facets, we create a new facet, AroundAudience, as follows:

package chapter04.concert;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class AroundAudience {
    /**
     * Reusable Tangent Point
     */
    @Pointcut("execution(* chapter04.concert.Performance.perform(..))")
    public void perform() {
    }

    @Around("perform()")
    public void watchPerform(ProceedingJoinPoint joinPoint) {
        try {
            System.out.println("Taking seats");
            System.out.println("Silencing cell phones");

            joinPoint.proceed();

            System.out.println("CLAP CLAP CLAP!!!");
        } catch (Throwable throwable) {
            System.out.println("Demanding a refund");
        } finally {
            System.out.println("perform finish");
        }
    }
}

Note here that this method has a ProceedingJoinPoint type parameter in which the target method can be invoked by calling its proceed() method.

Then modify the code for the ConcertConfig class:

package chapter04.concert;

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

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {
    /*@Bean
    public Audience audience() {
        return new Audience();
    }*/

    @Bean
    public AroundAudience aroundAudience() {
        return new AroundAudience();
    }
}

The results are as follows:

Taking seats

Silencing cell phones

The Drama Sleepless Night Sleep No More>

CLAP CLAP CLAP!!!

perform finish

4. Source Code and Reference

Source address: https://github.com/zwwhnly/spring-action.git Welcome to download.

Craig Walls The Spring Actual War (4th Edition)

AOP (Face Oriented Programming) _Baidu Encyclopedia

5. Finally

Play a small advertisement, welcome to sweep code and pay attention to the WeChat public number: "Shencheng Foreigners". Share the dried Java technology regularly, so we can make progress together.

Topics: Java Spring Programming jvm