• Springboot Aop使用


    1、什么是AOP

    AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一(两外两个:IOC-控制反转、DI-依赖注入)

    个人理解:Aop是在业务流程中增加新的通用流程时,如添加日志,统计方法运行时间等时,做一个拦截,在方法执行前后,都可以做额外的动作,可以做一些无关业务流程的工作如添加日志,也可以对业务流程进行干涉,如对参数进行修改,返回值进行修改等。类似python的装饰器的作用。

    2、Aop工作流程

    1、定义一个切点Pointcut,切点主要是用来定义在哪里切入,比如对所有的get请求进行请求前添加日志,那么就需要针对所有@GetMapping注解的方法进行拦截

    2.定义一个针对切点的处理方法Advice,这个主要来定义什么时候进行处理,方法执行前,方法执行后(专业术语:前置和后置处理)

    3.代码实现

    1、引入依赖

    1. org.springframework.boot
    2. spring-boot-starter-aop

    2、创建AOP类

     使用@Aspect进行注解,来标记是一个切面类

    3、定义切点方法

    @Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")

    可以使用上边注解直接加切入点表达式的方式,也可以使用下边方式进行切入点表达式重用

    使用@Pointcut进行方法注解,注解参数表明是对什么方法或类等进行拦截

    该注解有两种类型的参数:一个是使用 execution(),另一个是使用 annotation()

    execution表达式可以详细参考

    execution表达式详解

    execution (* com.sample.service.impl..*.*(..))

    annotation() 方式是针对某个注解来定义切点

    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")

    4、定义处理方法

    有5个注解

    1.@Before  标注的方法在切面切入目标方法之前执行

    2.@After   标注的方法在切面切入目标方法之后执行

    3.@Arround  可以自由选择增强动作与目标方法的执行顺序,也就是说可以在增强动作前后,甚至过程中执行目标方法,因为被@Arround标注的方法第一个形参必须是ProceedingJoinPoint类型,调用ProceedingJoinPoint参数的procedd()方法才会执行目标方法,这个方法的调用时机自己可以进行控制,没有调用ProceedingJoinPointproceed方法,则目标方法不会执行。

    @Around可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值

    调用ProceedingJoinPointproceed方法时,还可以传入一个Object[ ]对象,该数组中的值将被传入目标方法作为实参,所以可以改变执行目标方法的参数。传入的Object[ ]数组长度与目标方法所需要的参数必须相等。

    4、@AfterReturning 注解和 @After 有些类似,区别在于 @AfterReturning 注解可以用来捕获切入方法执行完之后的返回值,对返回值进行业务逻辑上的增强处理

    5、当被切方法执行过程中抛出异常时,会进入 @AfterThrowing 注解的方法中执行,在该方法中可以做一些异常的处理逻辑

    6、多个

    5、示例

    1. // @Aspect表示这个类是一个切面类
    2. @Aspect
    3. // @Component注解保证这个切面类能够放入IOC容器
    4. @Component
    5. public class LogAspect {
    6. @Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
    7. public void beforeMethod(JoinPoint joinPoint){
    8. String methodName = joinPoint.getSignature().getName();
    9. String args = Arrays.toString(joinPoint.getArgs());
    10. System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
    11. }
    12. @After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
    13. public void afterMethod(JoinPoint joinPoint){
    14. String methodName = joinPoint.getSignature().getName();
    15. System.out.println("Logger-->后置通知,方法名:"+methodName);
    16. }
    17. @AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
    18. public void afterReturningMethod(JoinPoint joinPoint, Object result){
    19. String methodName = joinPoint.getSignature().getName();
    20. System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
    21. }
    22. @AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
    23. public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
    24. String methodName = joinPoint.getSignature().getName();
    25. System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
    26. }
    27. @Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
    28. public Object aroundMethod(ProceedingJoinPoint joinPoint){
    29. String methodName = joinPoint.getSignature().getName();
    30. String args = Arrays.toString(joinPoint.getArgs());
    31. Object result = null;
    32. try {
    33. System.out.println("环绕通知-->目标对象方法执行之前");
    34. //目标对象(连接点)方法的执行
    35. result = joinPoint.proceed();
    36. System.out.println("环绕通知-->目标对象方法返回值之后");
    37. } catch (Throwable throwable) {
    38. throwable.printStackTrace();
    39. System.out.println("环绕通知-->目标对象方法出现异常时");
    40. } finally {
    41. System.out.println("环绕通知-->目标对象方法执行完毕");
    42. }
    43. return result;
    44. }
    45. }
    1. package com.ljx.splearn.AopDemo;
    2. import org.aspectj.lang.JoinPoint;
    3. import org.aspectj.lang.ProceedingJoinPoint;
    4. import org.aspectj.lang.Signature;
    5. import org.aspectj.lang.annotation.*;
    6. import org.springframework.core.annotation.Order;
    7. import org.springframework.stereotype.Component;
    8. import java.util.HashMap;
    9. import java.util.Map;
    10. /**
    11. * @author lijianxi
    12. * @date 2022年11月09日 10:58 上午
    13. */
    14. @Order(0)
    15. //标注该类是切面类
    16. @Aspect
    17. //交给spring容器
    18. @Component
    19. public class LogAdvice {
    20. //@Pointcut 注解定义什么时机进行拦截,要拦截的是什么东西,一个是使用 execution(),另一个是使用 annotation()。
    21. //annotation() 方式是针对某个注解来定义切点,execution指明哪些类或包或方法被执行的表达式
    22. //表明什么时机进行切入,本例中是被getmapping注解到的方法被调用时(参数是一个注解,表示被该注解标注的方法调用时进行切入)
    23. @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    24. private void pointCut(){}
    25. public long t1 ;
    26. public long t2;
    27. //指定的方法在切面切入目标方法之前执行,注入后做什么动作,参数是切点方法
    28. @Before("pointCut()")
    29. public void logAdvice(JoinPoint joinPoint){
    30. // 获取签名
    31. Signature signature = joinPoint.getSignature();
    32. // 获取切入的包名
    33. String declaringTypeName = signature.getDeclaringTypeName();
    34. // 获取即将执行的方法名
    35. String funcName = signature.getName();
    36. System.out.println("执行前开始记录");
    37. t1 = System.currentTimeMillis();
    38. }
    39. //指定的方法在切面切入目标方法之后执行
    40. @After("pointCut()")
    41. public void logAdvice1(JoinPoint joinPoint) throws Throwable {
    42. Object[] args = joinPoint.getArgs();
    43. String name =(String) args[0];
    44. System.out.println("结束记录1"+name);
    45. t2=System.currentTimeMillis();
    46. System.out.println("执行时间"+(t2-t1));
    47. }
    48. //自由选择增强动作与目标方法的执行顺序
    49. @Around("pointCut()")
    50. //方法参数必须是ProceedingJoinPoint,而不能是JoinPoint
    51. public Object logAdvice2(ProceedingJoinPoint joinPoint) throws Throwable {
    52. //获取请求参数
    53. Object[] args = joinPoint.getArgs();
    54. String name =(String) args[0];
    55. System.out.println("结束记录2"+name);
    56. t2=System.currentTimeMillis();
    57. System.out.println("执行时间1"+(t2-t1));
    58. //修改参数
    59. args[0]="王五";
    60. joinPoint.proceed(args);
    61. //修改返回值
    62. return "hello ,zhangsan";
    63. }
    64. /**
    65. * 在上面定义的切面方法返回后执行该方法,可以捕获返回对象或者对返回对象进行增强
    66. * @param joinPoint joinPoint
    67. * @param result result
    68. * 属性 returning 的值必须要和参数保持一致
    69. */
    70. @AfterReturning(pointcut = "pointCut()", returning = "result")
    71. public void doAfterReturning(JoinPoint joinPoint, Object result) {
    72. Signature signature = joinPoint.getSignature();
    73. String classMethod = signature.getName();
    74. System.out.println((String.format("方法%s执行完毕",classMethod)));
    75. System.out.println(result);
    76. // 实际项目中可以根据业务做具体的返回值增强
    77. System.out.println("对返回参数进行业务上的增强:{}"+result + "增强版");
    78. }
    79. /**
    80. * 在上面定义的切面方法执行抛异常时,执行该方法
    81. * @param joinPoint jointPoint
    82. * @param ex ex
    83. */
    84. @AfterThrowing(pointcut = "pointCut()", throwing = "ex")
    85. public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
    86. Signature signature = joinPoint.getSignature();
    87. String method = signature.getName();
    88. // 处理异常的逻辑
    89. System.out.println((String)(String.format("执行方法{}出错,异常为:{}", method, ex)));
    90. }
    91. }
    1. package com.ljx.splearn.controller;
    2. import org.springframework.stereotype.Controller;
    3. import org.springframework.web.bind.annotation.GetMapping;
    4. import org.springframework.web.bind.annotation.Mapping;
    5. import org.springframework.web.bind.annotation.RequestMapping;
    6. import org.springframework.web.bind.annotation.RestController;
    7. import java.sql.Time;
    8. /**
    9. * @author lijianxi
    10. * @date 2022年11月09日 10:55 上午
    11. */
    12. @RestController
    13. @RequestMapping("/demo")
    14. public class DemoController {
    15. @GetMapping("/hello")
    16. String sayHello(String name) throws InterruptedException {
    17. System.out.println("传递过来参数是"+name);
    18. Thread.sleep(2000);
    19. return "hello"+name;
    20. }
    21. }

    发起请求,参数是lisi,中间对参数修改传递到controller层时是王五,最终结果被@Around修改

    最终返回是zhangsan

     日志打印

  • 相关阅读:
    【算法基础】(一)基础算法 --- 快速排序
    POJ 2155 Matrix 树状数组
    如何让 Docker 在没有缓存的情况下重建镜像
    Cheat Engine CE v7.5 安装教程(专注于游戏的修改器)
    IB DP 语言怎么选?
    工业智能网关BL110使用技巧之49:实现电力行业 DT/L645 接入Modbus TCP Server云平台
    外包干了三年,真废了。。。
    Spring Boot简介
    9.28栈、队列&状态压缩&双向搜索
    C# 图解教程 第5版 —— 第10章 语句
  • 原文地址:https://blog.csdn.net/lingxiyizhi_ljx/article/details/127775861