AOP
AOP: (Aspect Oriented Programming)面向切面编程
作用:在不惊动原始设计的基础上为其增强功能
核心概念


切面补充:在AOP中,切面(Aspect)是一种模块化的、可重用的代码单元,它用于实现横切关注点的功能。具体地,切面可以定义一组通知(Advice)和切入点(Pointcut),用于拦截和处理目标对象的方法调用。
使用步骤
1.导入坐标(pom.xml)
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
aop的包其实也需要导入,但是spring-context的包导入之后,aop就级联导入了,所以只需要再导入aspectjweaver

2.准备好连接点方法(原始操作,dao接口与实现类)
3.建立共性方法(通知和通知类)
4.定义切入点
//定义切入点
@Pointcut("execution(void com.yuan.dao.BookDao.update())")
private void pt(){}
//上述切入点表达式引用实现类的方法也可以,效果一样,即:
//@Pointcut("execution(void com.yuan.dao.impl.BookDaoImpl.update())")
5.绑定切入点和通知方法(切面)
@Component
@Aspect
public class MyAdvice
{
//定义切入点
@Pointcut("execution(void com.yuan.dao.BookDao.update())")
private void pt(){}
//绑定切入点和通知方法(切面)
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}
其中@Component
注解指定该类为一个可供spring管理的bean,@Aspect
告诉spring这个类不是一个普通的bean,而是一个切面aop.
6.spring配置文件中添加注解
@Configuration
@ComponentScan("com.yuan")
@EnableAspectJAutoProxy
public class SpringConfig {
}
@EnableAspectJAutoProxy
注解告诉spring项目中有用注解开发的aop,即该注解启动了通知类的注解@Aspect
.
AOP工作流程
核心:代理模式
1.spring容器启动
2.读取所有切面配置中的切入点。即只读取绑定了通知类的切入点,没有绑定的切入点不管它(因为没有绑定的话和没有建立切入点是一样的呀)
3.初始化bean,判定bean对应的类的方法是否匹配到切入点
- 匹配失败,创建对象
- 匹配成功,创建原始对象(目标对象)的代理对象
4.获取bean执行方法
- 获取bean,调用方法并执行
- 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法和增强的内容,完成操作
读取aop配置要在初始化bean之前,因为spring要判断是否需要创建代理对象。
切入点表达式
注意:切入点是指要增强的方法,切入点表达式是要增强的方法的描述方式。
动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
- 访问修饰符和异常名可以省略
- 注意:
- 描述切入点通常描述接口,不描述实现类(降低耦合)
- 访问控制修饰符针对接口开发均采用public描述(可省略)
- 返回值类型 对于增删改类型使用精准类型加速匹配,对于查询类型使用*通配快速描述
- 包名尽量不用..匹配(效率低),常用*或精准匹配
- 接口名/类名书写名称与模块相关的采用*匹配,eg.UserService写成*Service,绑定业务层接口
- 方法名书写以动词进行精准匹配,名词采用*匹配,eg.getById写为getBy*,selectAll写为selectAll
- 通常不使用异常
AOP通知类型
@Component
@Aspect
public class MyAdvice
{
//定义切入点
@Pointcut("execution(* com.yuan.*.*.*e(..))")
private void pt(){}
//绑定切入点和通知方法(切面)
//前置通知
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
//后置通知
@After("pt()")
public void after(){
System.out.println("after");
}
//环绕通知
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("before around");
Object ret = pjp.proceed();
System.out.println("after around");
return ret;
}
//返回后通知(了解)
@AfterReturning("pt()")
public void afterReturning(){
System.out.println("after returning");
}
//抛出异常后通知(了解)
@AfterThrowing("pt()")
public void afterThrowing(){
System.out.println("after throwing");
}
}
- 最重要的是
@Around
注解,标准格式如上述代码,因为某些切入点方法有返回值,所以需要返回Object类型结果 - 环绕通知需要依赖形参
ProceedingJoinPoint
才能对原始方法调用 - 环绕通知无法预知原始方法运行后是否会有异常,故需要抛出异常
Throwable
AOP通知获取数据
没什么好说的,用的比较少,了解一下:around用ProceedingJointPoint
,其他用JointPoint
的getArgs()
方法。
AOP的应用场景
AOP(面向切面编程)是一种编程范式,它通过将横切关注点(如日志记录、性能统计、安全控制等)从业务逻辑中分离出来,实现了系统的松耦合和高内聚。AOP主要应用于以下场景:
1.日志记录
通过AOP,我们可以在应用程序的不同层次上记录日志,例如方法调用、异常捕获等,以便后续的问题排查和性能优化。
2.缓存管理
AOP可以用来实现缓存管理,例如在方法执行前检查缓存中是否存在相应的数据,如果存在则直接返回缓存数据,否则执行方法并将结果存入缓存。
3.安全控制
AOP可以用来实现安全控制,例如在方法执行前检查用户权限,如果用户没有相应的权限则拒绝访问。
4.事务管理⭐
AOP可以用来实现事务管理,例如在方法执行前开启事务,在方法执行后根据执行结果提交或回滚事务。
5.性能统计
AOP可以用来实现性能统计,例如在方法执行前记录开始时间,在方法执行后记录结束时间,并计算方法执行时间。
总之,AOP可以在不修改原有业务逻辑的情况下,通过横向切割关注点来实现各种横切关注点的功能,从而提高系统的可维护性、可扩展性和可重用性。
chatGPT对应用场景举例:https://webapp.chatgpt4google.com/s/MzEwNjY4
总的来说,其实aop就是把大量重复工作抽离出来,建立通知类,应用于对应的方法场景,降低耦合度。这和直接封装方法,在需要的地方调用的区别在于,后者需要更改原来的代码(添加调用),开关都过于麻烦;并且有些代码和实际业务无关,例如性能统计等,写在aop中更清晰。