文章目录
- 什么是AOP切面编程
- AOP中重要概念
- 切面
- 连接点
- 通知
- 切入点
- springboot的切面编程的步骤
- 引入切面编程依赖
- 开发附加操作(在springboot项目新建config配置)
什么是AOP切面编程
所谓AOP就是一种编程范例,我们一般做一个项目,会分为很多个模块,这些模块中有很多不同的功能,功能中有很多不同的切入点,举个例子,现在我们有一个保存用户业务逻辑功能,我们需要在保存用户之前输入一句话,这句话必须在保存用户之前做到,一般对我们springboot项目中,针对这样的情况,我们就会在Service层的代码中加入一行,如下图所示:
===================保存用户业务逻辑=================
用户业务
UserServicevoid save(User user)void delete(Integer id);User queryById(Integer id);....UserServiceImpl implement UserServicevoid save(User user){sout("=============");//业务功能-1...userDao.save(user);}
但是你想过没有,假设我们又要在删除用户的逻辑中也输出这样的一行内容呢?
我们是不是又要在业务逻辑层中又增加这样的一行呢?
void delete(Integer id){sout("=============");//业务功能-1......userDao.delete(id);}
那如果我们的其他模块的功能结构也需要这样这样的功能,那岂不是我们都要去加入这一行,这岂不是很麻烦吗?所以我们引入了AOP,我们能把这个要加入的功能,作为一个关注点切入进去,就不用交织在每个模块的代码中,而是作为一个单独的模块存在。
从这里我们就可以发现AOP解决了两个问题:一个是代码混乱,核心的业务逻辑代码还必须兼顾其他功能,这就导致不同功能的代码交织到一起,可读性很差。
第二,是代码分散问题。同一个功能代码分散在多个模块中,不易维护。
AOP中重要概念
切面
切面(Aspect)由切点(Pointcut)和通知(Advice)组成,它既包含了横切逻辑的定义,也包括了连接点的定义。
连接点
应用执行过程中能够插入切面的一个点,这个点可以是方法调用时,抛出异常时,甚至修改字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
总的来说,就是程序执行的某一刻的,在这个点可以做的额外操作。
通知
切面的工作被称之为通知。
切入点
切入点是用来描述连接点的,它决定了当前代码与连接点匹配
我们可以简单的把 AOP 理解为贯穿于方法之中,在方法执行前、执行时、执行后、返回值后、异常后要执行的操作。
springboot的切面编程的步骤
引入切面编程依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
开发附加操作(在springboot项目新建config配置)
/*** 自定义切面配置类*/
@Configuration //代表这个类是spring的配置类
@Aspect //代表这个类是切面配置类
public class MyAspectConfig {//切面Aspect=Advice附加操作+Pointcut切入点@Before("execution(* com.demo.springbootaoptest.service.*.*(..))")//代表这是一个核心业务逻辑执行之前的附加操作,value属性表明切书店public void before(JoinPoint joinPoint){System.out.println("=========前置附加操作=========");System.out.println("当前执行目标类: "+ joinPoint.getTarget());System.out.println("当前执行目标类中方法: "+ joinPoint.getSignature().getName());}@After("execution(* com.demo.springbootaoptest.service.*.*(..))")public void after(){System.out.println("后置通知");}@Around("execution(* com.demo.springbootaoptest.service.*.*(..))")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("==========进入环绕的前置操作===========");System.out.println("当前执行类: "+proceedingJoinPoint.getTarget());System.out.println("方法名: "+proceedingJoinPoint.getSignature().getName());//放心目标方法执行Object proceed = proceedingJoinPoint.proceed();//继续处理 业务逻辑方法执行System.out.println("==========进入环绕的后置操作===========");return null;
}
}
我对这上面的注解一一进行解释
- @Aspect 用来类上,代表这个类是一个切面
- @Before 用在方法上代表这个方法是一个前置通知方法
- @After 用在方法上代表这个方法是一个后置通知方法
- @Around 用在方法上代表这个方法是一个环绕的方法
另外这个方法是 上述 的 execution() 方法,用于匹配需要被 AOP 切入的方法。
execution() 方法的参数有以下几个:
"*" 表示匹配任意类型的方法。
com.demo.springbootaoptest.service 表示匹配 com.demo.springbootaoptest.service 包下的所有类。
.* 表示匹配所有类的所有方法。
(..) 表示匹配方法的任意参数。
service类
public class UserService {public void save(String name){
// System.out.println("===========================");//=》额外操作System.out.println("处理save核心业务逻辑,调用mapper");}public void delete(Integer id) {System.out.println("===========================");System.out.println("处理delete核心业务逻辑,调用mapper");}public void update(String name){System.out.println("处理update核心业务逻辑,调用mapper");}public String find(String name){System.out.println("处理find核心业务逻辑,调用mapper");return name;}
}
前置结果
后置结果
环绕结果
这里要特别解释一下环绕通知的流程
在方法执行前,打印一条日志,表示进入环绕的前置操作。
获取目标方法的执行类和方法名。
调用 proceed() 方法,让目标方法执行。
在方法执行后,打印一条日志,表示进入环绕的后置操作。