Article directory
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
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 :
- Import coordinates to the pom.xml file
- make join point method
- Make common functions, notification classes and notifications
- define pointcut
- 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
- Spring container startup
- 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());
}
}
- 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代理
- 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 highAccess 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 matchThe 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