新博客:https://blog.bigdataboy.cn/article/449.html
概念
AOP(面向切面编程)是一种编程思想,Python 的
装饰器
有这种思想,逆向中的Hook 技术
也有这种思想
在不改变
原来方法的情况下,增加功能
,只是在不同框架,不同语言下,实现方式和写法不一样
实现思路: 获取原来函数 - 执行顺序 - 参数 & 返回值
- 增强方法,那肯定会让原来的方法肯执行,所以得需要
获取原来方法
- 既然还要
增强方法
,那得考虑增强的逻辑什么时候执行
吧,是在原来的函数执行前
,还是执行后
吧 - 最后是
参数
和返回值
的问题,既然是增强,那我的参数
和返回值
要符合原来的函数
的规则
Spring 的实现方式
在
pom.xml
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<!-- 用来解析 切入点表达式 -->
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
编写 增强方法类,这个类还是需要按照 Spring 的规则变成交给容器管理的 Bean
@Component // 记得 Bean
public class Advice {
}
获取
原来的方法
,Spring 使用了一种用表达式匹配
的方式,后面详细介绍
@Component // 记得 Bean
public class Advice {
@Pointcut("execution(void cn.bigdataboy.dao.NameDao.getById())") // 切入点表达式
private void pt(){} // 可以理解为代表 原方法
}
编写 增强逻辑的方法,并设定执行的时机,有五种,后面详细介绍
@Component // 记得 Bean
public class Advice {
@Pointcut("execution(void cn.bigdataboy.dao.NameDao.getById())")
private void pt(){} // 可以理解为代表 原方法
@Before("pt()") // 增强方法执行时机,有五种
public void method(){
// 增强的方法在这里
System.out.println( "当前执行时间:" + System.currentTimeMillis());
}
}
添加注解,告诉 Spring 这是 AOP 的类
@Component // 记得 Bean
@Aspect // 添加注解
public class Advice {
@Pointcut("execution(void cn.bigdataboy.dao.NameDao.getById())")
private void pt(){} // 可以理解为代表 原方法
@Before("pt()") // 增强方法执行时机,有五种,参数是原方法
public void method(){
// 增强的方法在这里
System.out.println( "当前执行时间:" + System.currentTimeMillis());
}
}
最后在配置类添加注解,开启 AOP 功能
@Configuration
@ComponentScan({"cn.bigdataboy.dao","cn.bigdataboy.aop"}) // 注意 Bean 不要漏了
@EnableAspectJAutoProxy // 开启 aop 代理
public class SpringConfig {
}
五种执行时机
@Before("")
在原函数
执行前
执行
@Before("pt()") // 之前执行
public void methodBefore(){
// 增强的方法在这里
System.out.println( "@Before running ...");
}
@After("")
在原函数
执行后
执行,如果原函数报错也会执行
@After("pt()") // 之后执行,如果原方法报错,也会执行
public void methodAfter(){
// 增强的方法在这里
System.out.println( "@After running ...");
}
@Around("") 重点常用
环绕执行,所以它有点特殊,有一个参数,包含原方法和它的各种信息,相对于执行锚点,能控制在增强函数的什么位置执行
@Around("pt()") // 环绕执行
public void methodAround(ProceedingJoinPoint pjp) throws Throwable {
// 增强的方法在这里
System.out.println( "当前执行时间 @Around:" + System.currentTimeMillis());
pjp.proceed(); // 相对于传入锚点,执行位置
System.out.println( "当前执行时间 @Around:" + System.currentTimeMillis());
}
@AfterReturning("")
原方法成功执行时触发,原方法报错,则不会执行
@AfterReturning("pt()") // 在原方法正常执行后才会触发,也就说 入原方法报错,就不会触发了
public void methodAfterReturning(){
// 增强的方法在这里
System.out.println( "@AfterReturning running ...");
}
@AfterThrowing("")
在原方法报错触发
@AfterThrowing("pt()") // 在原方法报错触发
public void methodAfterThrowing(){
// 增强的方法在这里
System.out.println( "@AfterThrowing running ...");
}
切入点表达式
切入点表达式,是 Spring 用来表达增强方法对
哪些方法生效
的式子
@Pointcut("execution(void cn.bigdataboy.dao.NameDao.getById())")
private void pt(){} // 可以理解为代表 原方法
格式:
动作关键字(访问修饰符 返回值类型 包名.类/接口名.方法名(参数) 异常名)
(有些是可以省略的)
- 动作关键字:几乎都是
execution()
- 访问修饰符:
public
、private
... 可以省略 - 返回值:是什么写什么
- 异常名:方法中定义的抛出异常,可以省略
切入点表达式 通配 快速描述
*
:单个独立
的任意符号
,可以独立出现
,可以作为前缀
或者后缀
的匹配符出现
execution(* cn.bigdataboy.dao.*Dao.getBy*())
..
:多个连续
的任意符号
,可以独立出现
,常用于简化 包名 和 参数
出现
execution(* cn..NameDao.getById(..))
+
: 专用于匹配子类型
execution(* cn.bigdataboy.dao.NameDao+.*())
参数 & 返回值处理
既然是增强方法,那
参数
和返回值
就要处理,那同时,如果方法异常
,那异常信息
也要处理。都是使用形参获取的。
参数处理
@Before
、@After
、@AfterReturning
、@AfterThrowing
都是传入JoinPoint
对象执行getArgs()
获取原始方法参数数组
JoinPoint
对象包含大量信息
对于
@Around
是传入ProceedingJoinPoint
对象执行getArgs()
获取原始方法参数数组
返回值处理
对于 返回值,只有
@Around
、@AfterReturning
有,其他的不涉及返回值
@Around
方式设计参数和返回值,所以为了规范,返回值
可以是 Object
@Around("pt()") // 环绕执行
public Object methodAround(ProceedingJoinPoint pjp) throws Throwable {
// 增强的方法在这里
System.out.println("@Around running args: " + Arrays.toString(pjp.getArgs()));
Object proceed = pjp.proceed(pjp.getArgs());// 相对于锚点,执行位置
System.out.println("@Around running res: " + proceed);
return proceed;
}
@AfterReturning
获取返回值有点特殊,需要指定注解的returning
参数
@AfterReturning(value = "pt()", returning = "ret") // 在原方法正常执行后才会触发,也就说 入原方法报错,就不会触发了
public void methodAfterReturning(Object ret) {
// 增强的方法在这里
System.out.println("@AfterReturning running res: " + ret);
}
版权声明:《 【Spring】核心概念:AOP 说明 》为明妃原创文章,转载请注明出处!
最后编辑:2022-9-19 10:09:28