当我们需要对一个类中的方法进行功能的增强,又不想改变原方法的代码时
当我们需要保护一个类中的代码时
我们都可以使用代理模式,帮助这个类完成一些功能,这就是 AOP 切面编程
核心:切面 = 通知 + 切点
首先,需要引入 AOP 要用到的切面
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.breeze</groupId>
<artifactId>aspect</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!--仓库-->
<repositories>
<!--spring里程碑版本的仓库-->
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<!--依赖-->
<dependencies>
<!--spring context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--spring aspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
spring.xml 配置
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--包扫描-->
<context:component-scan base-package="all"/>
<!-- 要写上这一行才能开启动态代理,使用 AOP-->
<aop:aspectj-autoproxy/>
</beans>
编写 需要被增强 的目标类方法
@Component("first")
public class First {
//原始方法,称作目标方法
public void origin(){
System.out.println("我是原始方法");
}
}
编写代理方法
@Component
@Aspect
public class Plus {
//增强原始方法,称之为代理方法
@Before("execution(public * all.First.*(..))")//引号中的就是切点,以切点表达式的方式找到原始方法
public void add(){
System.out.println("在原始方法执行前,增强了一些功能");
}
}
@Before 就是通知,@Before 里面的参数就是切点。合起来就是切面
通知
常用通知:
@Before 目标方向执行前
@AfterReturning 目标方法执行后
@Around 前后都添加
eg:
@Around("execution(public * dao.Orcle.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {//传参连接点,固定参数
System.out.println("----------------------------");//前环绕
joinPoint.proceed();//执行目标,固定方法
System.out.println("----------------------------");//后环绕
}@AfterThrowing 发生异常时添加
@After 放在 finally 语句块的最终通知
叠加通知的作用顺序:
前环绕
前置
原本的方法被执行了
后置
最终通知
后环绕异常通知在最终通知之后,在异常通知之后的后环绕不会执行
切点
常用切点表达式:
一、基于方法执行的切点表达式
匹配特定方法名称
execution(* com.example.service.UserService.findUserById(..)):匹配 com.example.service.UserService 类中名为 findUserById 的方法,无论其参数类型是什么。
匹配特定包下的所有方法
execution(* com.example.service.*.*(..)):匹配 com.example.service 包下任意类的任意方法。
匹配特定返回类型的方法
execution(* com.example.service.UserService.getUser(..)) && return-type=com.example.domain.User:匹配 com.example.service.UserService 类中名为 getUser 且返回类型为 com.example.domain.User 的方法。
二、基于类的切点表达式
匹配特定类中的所有方法
within(com.example.service.UserService):匹配 com.example.service.UserService 类中的所有方法。
匹配特定包下的所有类的所有方法
within(com.example.service.*):匹配 com.example.service 包下所有类的所有方法。
三、基于注解的切点表达式
匹配带有特定注解的方法
@annotation(com.example.annotation.Loggable):匹配带有 @com.example.annotation.Loggable 注解的方法。
四、基于目标对象的切点表达式
匹配特定类型的目标对象的所有方法调用
target(com.example.domain.User):匹配目标对象为 com.example.domain.User 类型的所有方法调用。
切面叠加
当多个切面作用于一个目标方法时
可以通过 @Order(n) 排序
n 越小优先级越高
不论优先级怎样,前置通知一定在目标发放前就被执行,后置通知同理