当前位置:   article > 正文

Spring AOP(1)

Spring AOP(1)

AOP概述

AOP是Spring框架的第二大核心(第一大核心是IoC).

什么是AOP?

Aspect Oriented Programming(面向切面编程)

什么是面向切面编程呢? 切面就是指某一类特定的问题, 所以AOP也可以叫做面向特定方法编程

什么是面向特定方法编程呢?比如上一篇中讲到的拦截器, 就是对比如登录校验这一类问题的统一处理. 所以, 拦截器也算是AOP的一种应用. AOP是一种思想, 拦截器是AOP的一种实现. Spring框架实现了这种思想, 提供了拦截器技术的相关接口.

同样地, 统一数据返回格式和同意一场处理, 也是AOP思想的一种实现.

简单来说: AOP是一种思想, 是对某一类事情的集中处理.

什么是Spring AOP?

AOP是一种思想, 它的实现方法有很多, 有Spring AOP, 也有AspectJ, CGLIB等.

Spring AOP是其中的一种实现方式.

学会统一功能之后, 是不是就学会了AOP呢? 当然不是.

因为拦截器的作用维度是URL(一次请求和响应), @ControllerAdvice应用场景主要是全局异常处理(配合自定义异常效果更佳), 数据绑定, 数据预处理. AOP的作用维度更加细致(可以根据包, 类, 方法, 参数等进行拦截), 能够实现更加复杂的业务逻辑.

举个栗子:

我们现在有一个项目, 项目中开发了很多的业务功能:

 

现在有一些业务的执行业务比较低, 耗时比较长, 我们需要对接口进行优化.

第一步就需要定位出执行耗时比较长的业务方法, 再针对该业务方法来进行优化.

如何定位呢? 就需要统计当前项目中每一个业务方法的执行耗时.

如何统计呢? 可以在业务方法运行前和运行后, 记录下方法的开始时间和结束时间, 两者之差就是这个方法的耗时. 

这种方法是可以解决问题的, 但一个项目中会包含很多业务模块, 每个业务模块又有很多接口, 一个接口又包含很多方法, 如果我们要在每个业务方法上都这么整, 哥们愚公都没你能搬.

AOP就可以做到在不改动这些原始方法的基础上, 针对特定的方法进行功能的加强

AOP作用: 在程序运行期间在不修改源代码的基础上对已有方法进行增强(无侵入性:解耦)

Spring AOP快速入门

引入AOP依赖

在pom.xml文件中添加配置:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-aop</artifactId>
  4. </dependency>

编写AOP程序

记录每个Controller每个方法的执行时间(细节后面会讲).

  1. @Slf4j
  2. //需要被Spring管理
  3. @Component
  4. //spring使用了aspectJ的注解, 具体实现是Spring实现的. AspectJ是一个第三方的Jar包
  5. @Aspect
  6. public class TimeRecrodAspect {
  7. /**
  8. * 记录耗时
  9. */
  10. @Around("execution(* com.ashwakeup.demo.library.controller.*.*(..))")
  11. public Object timeRecord(ProceedingJoinPoint joinPoint) throws Throwable { //joinPoint可理解为目标方法
  12. //记录开始时间
  13. long start = System.currentTimeMillis();
  14. //执行目标方法
  15. Object proceed = joinPoint.proceed();
  16. //记录结束时间
  17. long end = System.currentTimeMillis();
  18. //日志打印耗时
  19. log.info("耗时时间: " + (end - start) + "ms");
  20. return proceed;
  21. }
  22. }

运行程序, 观察日志:

 

对程序进行简单讲解:

1.@Aspect: 标识这是一个切面类.

2.@Around: 环绕通知, 在目标方法的前后都会执行. 后面的表达式表示对哪些方法进行增强.

3.ProceedingJoinPoint.proceed()让原始方法执行. 

 我们通过AOP入门程序完成了业务接口的执行耗时统计.

通过上面的程序, 我们也可以感受到AOP面向切面编程的优势:

代码无侵入: 主要是因为不修改原始的业务方法, 主要是对原始业务方法的功能进行增强.

减少代码重复

提高开发效率

维护方便

 Spring AOP详解

Spring AOP核心概念

切点(Pointcut)

Pointcut的作用就是提供一组规则, 告诉程序对哪些方法的功能进行增强.

这里的: @Around("execution(* com.ashwakeup.demo.library.controller.*.*(..))")就是切点表达式.

连接点(Join Point)

满足切点表达式规则的所有方法, 就是连接点. 也就是可以被AOP控制的方法,

比如上图, 就是指所有在com.ashwakeup.demo.library.controller下的所有方法都是连接点.

切点和连接点的关系: 连接点是满足切点表达式的元素. 切点可以看作是保存了众多连接点的一个集合.

eg:

切点表达式: 一班学生

连接点:张三, 李四.......

通知(Advice) 

通知就是指具体的工作, 指哪些重复的逻辑, 也就是共性功能(最终体现为一个方法).

比如上述程序中记录业务方法的耗时时间, 就是通知.

在AOP面向切面编程当中, 我们把这部分重复的代码逻辑抽取出来单独定义, 这部分代码就是通知的内容.

切面(Aspect)

切面(Aspect) = 切点(Pointcut) + 通知(Advice) 

通过切面就能描述当前AOP程序需要针对哪些方法, 在什么时候执行什么样的工作

切面既包含了通知逻辑的定义, 也包含了连接点的定义.

切面所在的类, 我们一般称为切面类(被@Aspect注解标识的类)

通知类型

上面讲了什么是通知, 接下来学习通知的类型. @Around就是其中一种通知类型, 表示环绕通知. Spring AOP的通知类型有以下几种:

@Around: 环绕通知, 此注解标注的通知方法在目标方法前后都会被执行

@Before: 前置通知, 此注解标注的通知方法在目标方法前被执行.

@After: 后置通知, 此注解标注的通知方法在目标方法后执行, 无论是否有异常都会被执行

@AfterReturning: 返回后通知, 此注解标注的通知方法在目标方法后执行, 有异常不会执行.

@AfterThrowing: 异常后通知, 此注解标注的通知方法发生异常后执行.

接下来通过代码来加深对这几个方法的理解:

  1. @Slf4j
  2. @Component
  3. @Aspect
  4. public class AspectDemo {
  5. //前置通知
  6. @Before("execution(* com.ashwakeup.demo.library.controller.TestController.*(..))")
  7. public void doBefore() {
  8. log.info("执行 Before 方法");
  9. }
  10. //后置通知
  11. @After("execution(* com.ashwakeup.demo.library.controller.TestController.*(..))")
  12. public void doAfter() {
  13. log.info("执行 After 方法");
  14. }
  15. //返回后通知
  16. @AfterReturning("execution(* com.ashwakeup.demo.library.controller.TestController.*(..))")
  17. public void doAfterReturning(){
  18. log.info("执行 AfterReturning 方法");
  19. }
  20. //抛出异常后通知
  21. @AfterThrowing("execution(* com.ashwakeup.demo.library.controller.TestController.*(..))")
  22. public void doAfterThrowing() {
  23. log.info("执行 AfterThrowing 方法");
  24. }
  25. //添加环绕通知
  26. @Around("execution(* com.ashwakeup.demo.library.controller.TestController.*(..))")
  27. public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
  28. log.info("Around方法开始时执行");
  29. Object result = joinPoint.proceed();
  30. log.info("Around方法结束后执行");
  31. return result;
  32. }
  33. }

写一个测试程序:

  1. @RestController
  2. @RequestMapping("/test")
  3. public class TestController {
  4. @RequestMapping("/t1")
  5. public String t1() {
  6. return "t1";
  7. }
  8. @RequestMapping("/t2")
  9. public boolean t2() {
  10. int a = 10 / 0;
  11. return true;
  12. }
  13. }

 运行代码, 观察日志:

1.执行t1的情况:

程序正常运行的情况下, @AfterThrowing标识的通知方法并不会执行.

从上图可以看出: 执行逻辑大致为: Around上半-> Before -> AfterReturning -> After -> Around下半.

2.异常时的情况:

 程序发生异常的情况下:

@AfterReturning标识的通知方法不会执行, @AfterThrowing标识的通知方法执行了.

@Around环绕通知中原始方法调用时有异常, 通知中的环绕后的代码逻辑也不会在执行了(因为原始方法调用出异常了). 

执行逻辑: @Around上半 -> @Before -> @AfterThrowing -> @After

注意事项:

@Around环绕通知需要调用ProceedingJoinPoint.proceed()来让原始方法执行, 其它通知不需要考虑目标执行.

@Around环绕通知方法的返回值, 必须指定为Object, 来接收原始方法的返回值, 否则原始方法执行完毕, 是获取不到返回值的.

一个切面类有多个切点. 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/622115
推荐阅读
相关标签
  

闽ICP备14008679号