Introduction to the use of Spring aspect-oriented programming AOP

AOP

AOP core concept

AOP (Aspect Oriented Programming) Aspect Oriented Programming, a programming paradigm that guides developers on how to organize program structures

OOP (Object Oriented Programming) object-oriented programming

Role : To enhance the function without disturbing the original design

Spring Philosophy : No Intrusion/No Intrusion

Let's look at the following program :

In a certain class, there are four methods as follows, the save method will print a sentence, and calculate the time consumed by looping 10,000 times; while the other three methods only print a sentence, we want update and delete to have the same save function, while the select method simply prints a sentence

public void save() {
    
    
  	//记录程序当前执行执行(开始时间)
  	Long startTime = System.currentTimeMillis();
  	//业务执行百万次
  	for (int i = 0;i<10000;i++) {
    
    
    System.out.println(“book dao save ...);
    }
  	//记录程序当前执行时间(结束时间)
  	Long endTime = System.currentTimeMillis();
  	//计算时间差
  	Long totalTime = endTime-startTime;
  	//输出信息
  	System.out.println("万次耗时:" + totalTime + "ms");
}

public void update(){
    
    
  	System.out.println("book dao update ...");
}

public void delete(){
    
    
  	System.out.println("book dao delete ...");
}

public void select(){
    
    
  	System.out.println("book dao select ...");
}

We can use the AOP idea to extract the commonality of the method, and add the function of looping 10,000 times and calculating the time without changing the original code of the method

insert image description here

Join Point (JoinPoint): Any position in the program execution process, which can be used to execute methods, throw exceptions, set variables, etc.

In Spring AOP, understood as方法的执行

Pointcut: match the expression of the connection point

In Spring AOP, a pointcut can only describe a specific method, or it can match multiple methods

  • A specific method: the save method with no formal parameters and no return value in the BookDao interface under the com.chenyq.dao package
  • Matches multiple methods: all save methods, all methods starting with get, any method in any interface ending with Dao, all methods with one parameter

Advice: The operation performed at the entry point, that is, the common function

In Spring AOP, functions are finally presented in the form of methods, methods for performing common operations

Notification class: the class that defines the notification

Aspect: Describes the correspondence between notifications and pointcuts


Brief summary :

Junction point: represents all methods

Entry point: represents the method to add functionality

Notifications: Methods that represent generic functionality

Notification class: represents the class that defines the notification ( common function method )

Aspect: Represents the relationship between pointcuts and advice

Quick Start with AOP

Case requirements : require the method to print out the current system time before

Development mode : annotation development

AOP entry case analysis :

  1. Import coordinates to the pom.xml file
  2. make join point method
  3. Make common functions, notification classes and notifications
  4. define pointcut
  5. Binding pointcuts and advice relationships

The implementation steps are as follows :

Import aop and aspect related coordinates, we will import aop dependent coordinates by default when importing spring coordinates, so we only need to import aspect coordinates

<dependencies>
  <!--spring依赖-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
  </dependency>

  <!--aspect依赖-->
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
  </dependency>
</dependencies>

Define dao interface and implementation class

There are the following methods in the BookDaoImpl implementation class. We hope that the update method will add the function of printing the current system time before execution without changing the source program, while the add method will remain unchanged.

public interface BookDao {
    
    
    void save();
    void update();
    void add();
}
@Repository
public class BookDaoImpl implements BookDao {
    
    
    public void save() {
    
    
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save...");
    }

    public void update() {
    
    
        System.out.println("book dao update...");
    }

    public void add() {
    
    
        System.out.println("book dao add...");
    }
}

Extract the common function into a notification under a notification class. The @Component annotation needs to be used in the notification class to let Spring recognize the class as a bean; tell Spring that the current class is an aspect class through the @Aspect annotation

@Component
@Aspect
public class MyAdvice {
    
    
    public void method() {
    
    
        System.out.println(System.currentTimeMillis());
    }
}

@Pointcut defines the entry point, and the entry point is required to rely on a method that has no practical significance, that is, no parameters, no return value, and no actual logic in the method body

@Component
@Aspect
public class MyAdvice {
    
    
    // 表示包下无返回值无参数的update方法
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {
    
    }
    
    public void method() {
    
    
        System.out.println(System.currentTimeMillis());
    }
}

Bind the relationship between the entry point and the notification, and specify the specific execution location where the notification is added to the original connection point. Since we want to execute before the method, we use the @Before annotation

@Component
@Aspect
public class MyAdvice {
    
    
    // 表示包下无返回值无参数的update方法
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {
    
    }

    // 描述切入点与通知之间的关系
    @Before("pt()")
    public void method() {
    
    
        System.out.println(System.currentTimeMillis());
    }
}

In the Spring core configuration file, enable Spring's AOP annotation-driven support through @EnableAspectJAutoProxy

@Configuration
@ComponentScan("com.chenyq")
@EnableAspectJAutoProxy // 告知Spring有用注解开发的AOP
public class SpringConfig {
    
    
}

At this time, running the update method will also print the current system time, and we have added new functions without changing the source code

public class App {
    
    
    public static void main(String[] args) {
    
    
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.update();
    }
}

AOP workflow

  1. Spring container startup
  2. Read all 切面配置entry points in

Note: It is the entry point of all aspect configurations, not all configurations. For example, in the following code, we have two entry points, but only one entry point pt is used for configuration. If ptx is not configured, it will not be read taken

@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(void com.chenyq.dao.BookDao.add())")
    public void ptx() {
    
    }
    
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {
    
    }

    // 描述切入点与通知之间的关系
    @Before("pt()")
    public void method() {
    
    
        System.out.println(System.currentTimeMillis());
    }
}
  1. Initialize the bean and determine whether the method in the class corresponding to the bean matches any entry point

Match failed, create object

If the match is successful, an object 目标对象of the original object ( ) is created代理

  1. Get the bean execution method

Get the bean, call the method and execute it, and complete the operation

When the obtained bean is a proxy object, run the original method and enhanced content according to the operation mode of the proxy object to complete the operation

Target object (Target):

The original function removes the object generated by the class corresponding to the common function, and this kind of object cannot directly complete the final work

Proxy :

The target object cannot directly complete the work, and needs to be backfilled with functions, which are realized through the proxy object of the original object

AOP pointcut expressions

Entry point : the method to be enhanced

pointcut expression : a description of the method to be enhanced

For any one method, its expression is various :

For example we have an interface BookDao

package com.chenyq.dao;
public interface BookDao {
    
    
    void update();
}

The implementation class BookDaoImpl of the BookDao interface

@Repository
public class BookDaoImpl implements BookDao {
    
    
    public void update() {
    
    
        System.out.println("book dao update...");
    }
}

To describe the update method above, we have two ways to describe it :

Description method 1: According to the interface description

// 执行com.chenyq.dao包下的BookDao接口中的无参数, 无返回值的update方法
execution(void com.itheima.dao.BookDao.update())

Description method 2: According to class description

// 执行com.chenyq.dao.impl包下的BookDaoImpl类中的无参数, 无返回值的update方法
execution(void com.itheima.dao.impl.BookDaoImpl.update())

The standard format of a pointcut expression is as follows :

Action keyword (access modifier return value package name. class/interface name. method name (parameter) exception name)

execution(public User com.chenyq.service.UserService.findById(int))

Action keywords : describe the behavior of the entry point, for example, execution means to execute to the specified entry point

Access modifiers : public, private, etc., can be omitted

return value : the type of the method return value

Package name : which package the method is in

Class/Interface Name : The class or interface where the method resides

method name

parameter

Exception name : The specified exception is thrown in the method definition, which can be omitted

In the program, we can use wildcards to describe the pointcut, quickly describe

*: A single independent arbitrary symbol, which can appear independently or as a prefix or suffix match character

// 匹配chenyq包下的任意包中的UserService类或接口下的所有以find开头且带有一个任意参数,任意返回值的方法
execution(public * com.chenyq.*.UserService.find*(*))

..: Multiple consecutive arbitrary symbols, which can appear independently, are often used to simplify the writing of package names and parameters

// 匹配com包下任意包中的UserService类或接口的findById方法, 且参数任意个(0个或多个)
execution(public User com..UserService.findById(..))

+: dedicated to matching subclass types

// 匹配任意返回值 任意包下以Service结尾的类或接口的子类的任意方法
execution(* *..*Service+.*(..))

Pointcut expression writing skills :

All codes are developed according to standard specifications, that is, the naming must be standard, otherwise the following techniques will all fail

Describe the pointcut 通常描述接口instead of the implementation class, because the coupling between the description and the implementation class is too high

Access control modifiers use public descriptions for interface development (the description of access control modifiers can be omitted)

The return value type uses precise type to speed up matching for addition, deletion and modification classes, and uses * wildcard quick description for query classes

Writing the package name 尽量不使用..匹配, the efficiency is too low, often use * to do a single package description match, or exact match

The name of the interface name/class name is matched with the module by *, for example, UserService is written as *Service, and the interface name of the business layer is bound

The method name is written with verbs for precise matching, and nouns are used for matching. For example, getById is written as getBy , selectAll is written as selectAll

Parameter rules are relatively complex, and can be flexibly adjusted according to business methods

通常不使用异常as a matching rule

AOP notification type

The AOP notification describes the extracted common functions. According to the location of the common function extraction, it should be added to a reasonable position when the code is finally run.

There are 5 types of AOP notifications :

advance notice

post notification

Surround notifications (emphasis)

Notify upon return (know)

Notify after an exception is thrown (understand)

advance notice

Name: @Before

Type: method annotation

Location: Above the notification method definition

Function: Set the binding relationship between the current notification method and the entry point, the current notification method runs before the original entry point method runs

Related attributes: value (default): entry point method name, format is class name. method name ()

Example:

@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {
    
    }

    // 描述切入点与通知之间的关系
    @Before("pt()")
    public void method() {
    
    
        System.out.println(System.currentTimeMillis());
    }
}

post notification

Name: @After

Type: method annotation

Location: Above the notification method definition

Function: Set the binding relationship between the current notification method and the pointcut, the current notification method runs after the original pointcut method runs

Related attributes: value (default): entry point method name, format is class name. method name ()

Example:

@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {
    
    }

    @After("pt()")
    public void method() {
    
    
        System.out.println(System.currentTimeMillis());
    }
}

surround notification

Name: @Around (key, commonly used)

Type: method annotation

Location: Above the notification method definition

Function: Set the binding relationship between the current notification method and the entry point, the current notification method runs before and after the execution of the original entry point method

Related attributes: value (default): entry point method name, format is class name. method name ()

Note: For the surround operation, we need to accept a parameter of type ProceedingJoinPoint. The proceed method of this parameter represents the operation of calling the original method; in simple terms, the proceed method represents what executes the original method

Example:

@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {
    
    }

    @Around("pt()")
    public void method(ProceedingJoinPoint pjp) throws Throwable {
    
    
        System.out.println(System.currentTimeMillis());
        System.out.println("方法执行前的操作...");
        pjp.proceed(); // 代表着原始方法发调用
        System.out.println("方法执行后的操作...");
    }
}

The surrounding notification must rely on the formal parameter ProceedingJoinPoint to realize the call to the original method, and then realize adding notifications before and after the original method call

If the notification does not use ProceedingJoinPoint to call the original method, the execution of the original method will be skipped

The call to the original method does not need to receive the return value, and the notification method can be set to void; if the return value is received, it must be set to Object type, and the return value of the original method is returned

If the return value of the original method is void type, the return value type of the notification method can be set to void or Object

Since it is impossible to predict whether an exception will be thrown after the original method runs, the surrounding notification method must throw a Throwable object

@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(int com.chenyq.dao.BookDao.update())")
    private void pt() {
    
    }

    @Around("pt()")
    public Object method(ProceedingJoinPoint pjp) throws Throwable {
    
    
        System.out.println("方法执行前的操作...");
        Object res = pjp.proceed();// 接收原始方法的返回值
        System.out.println("方法执行后的操作...");
        // 将原始方法的返回值返回
        return res;
    }
}

Notify upon return

Name: @AfterReturning (Understanding)

Type: method annotation

Location: Above the notification method definition

Function: Set the binding relationship between the current notification method and the entry point. The current notification method runs after the original entry point method is executed normally.

Example:

@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(int com.chenyq.dao.BookDao.update())")
    private void pt() {
    
    }
  
    @AfterReturning("pt()")
    public void method() {
    
    
        System.out.println("afterReturning...");
    }
}

Notify when an exception is thrown

Name: @AfterThrowing (Understanding)

Type: method annotation

Location: Above the notification method definition

Function: Set the binding relationship between the current notification method and the entry point. The current notification method is executed after the original entry point method throws an exception. It will be executed if there is an exception, and it will not be executed if there is no exception.

Example:

@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(int com.chenyq.dao.BookDao.update())")
    private void pt() {
    
    }

    @AfterThrowing("pt()")
    public void method() {
    
    
        System.out.println("AfterThrowing...");
    }
}

Of the five notification types, the most commonly used is the surround notification @Around

AOP notification to get data

There are three types of notifications to obtain data :

Get the parameters of the pointcut method

Get the return value of the pointcut method

Obtain the exception information of the entry point method ( understand )

AOP notification gets parameter data :

JoinPoint: Applicable to pre-, post-, post-return, and post-throw notifications

  • The JoinPoint object describes the running status of the join point method, and can get the calling parameters of the original method
@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(String com.chenyq.dao.BookDao.findName(*))")
    private void pt() {
    
    }

    @Before("pt()")
    public void methods(JoinPoint jp) {
    
    
        // 通过JoinPoint获取参数
        Object[] args = jp.getArgs();
        System.out.println(Arrays.toString(args));
    }
}

ProceedJointPoint: for surround notifications

@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(String com.chenyq.dao.BookDao.findName(*))")
    private void pt() {
    
    }

    @Around("pt()")
    public void methods(ProceedingJoinPoint pjp) throws Throwable {
    
    
        // 环绕通知通过ProceedingJoinPoint获取参数
        Object[] args = pjp.getArgs();
        pjp.proceed();
        System.out.println(Arrays.toString(args));
    }
}

Get the return value of the pointcut method, only the post-return notification and surround notification can get it

After returning, notify to get the return value

@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(String com.chenyq.dao.BookDao.findName(*))")
    private void pt() {
    
    }
  
    // 设置通过ret变量获取返回值
    @AfterReturning(value = "pt()", returning = "ret")
    public void methods(Object ret) {
    
    
        System.out.println(ret);
    }
}

The call to the original method can be manually written in the surround notification, and the result obtained is the return value of the original method

@Component
@Aspect
public class MyAdvice {
    
    
    @Pointcut("execution(String com.chenyq.dao.BookDao.findName(*))")
    private void pt() {
    
    }

    @Around("pt()")
    public void methods(ProceedingJoinPoint pjp) throws Throwable {
    
    
        // 环绕通知通过ProceedingJoinPoint获取参数
        Object res = pjp.proceed();
        System.out.println(res);
    }
}

To get the exception information of the entry point method, only the exception notification and the surrounding notification can be obtained ( understand )

After throwing an exception, the notification can obtain the exception information that appears in the pointcut method, and use the formal parameter to receive the corresponding exception object

@AfterThrowing(value = "pt()", throwing = "err")
public void afterThrowing(Throwable err) {
    
    
    System.out.println(err);
}

Surround notifications can be obtained through try...catch

@Around("pt()")
public void methods(ProceedingJoinPoint pjp) {
    
    
    // 环绕通知通过ProceedingJoinPoint获取参数
    Object res = null;
    try {
    
    
        res = pjp.proceed();
    } catch (Throwable e) {
    
    
        e.printStackTrace();
    }
    System.out.println(res);
}

AOPSummary

Concept: AOP (Aspect Oriented Programming) aspect-oriented programming, a programming paradigm

Role: Enhance the function of the method without disturbing the original design

Core concepts :

Proxy: The core essence of Spring AOP is realized by proxy mode

Connection point (JoinPoint): In Spring AOP, it is understood as the execution of any method

Pointcut: the expression matching the connection point is also a method description with common functions

Advice: The common functions of several methods are executed at the entry point and finally embodied as a method

Aspect: Describes the correspondence between notifications and pointcuts

Target object (Target): The original object being proxied becomes the target object

Pointcut expression standard format:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)

execution(* com.itheima.service.*Service.*(..))

Pointcut expressions describe wildcards :

Function: for quick description, range description

*: match any symbol (commonly used)

..: Match multiple consecutive arbitrary symbols (commonly used)

+: match subclass type

Pointcut expression writing skills :

Developed according to standard specifications

The return value of the query operation is recommended to use * match

reduce the use of the form description package

Describe the interface, using the module name, for example, the matching description of UserService is Service

The method name is written as a reserved verb, such as get, and used to represent a noun, such as getById matching description as getBy

The parameters can be adjusted flexibly according to the actual situation

Notification Type :

advance notice

post notification

Surround notifications (emphasis)

  • The surround notification depends on the formal parameter ProceedingJoinPoint to realize the call to the original method
  • Surround advice can isolate the call execution of the original method
  • Surround notification return value is set to Object type
  • Exceptions that occur during the original method call can be handled in the surround notification

Notify upon return

Notify when an exception is thrown

Get the parameters of the pointcut method

JoinPoint: Applicable to pre-, post-, post-return, and post-throw notifications, set as the first parameter of the method

ProceedJointPoint: for surround notifications

Get the return value of the pointcut method

Notify upon return

surround notification

Obtain the exception information of the entry point method

Notify when an exception is thrown

surround notification

Guess you like

Origin blog.csdn.net/m0_71485750/article/details/127968999