当我们需要对一个类中的方法进行功能的增强,又不想改变原方法的代码时
当我们需要保护一个类中的代码时
我们都可以使用代理模式,帮助这个类完成一些功能,这就是 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.xsdhttp://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsdhttp://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 越小优先级越高
不论优先级怎样,前置通知一定在目标发放前就被执行,后置通知同理