• Spring Aop 面向切面编程 入门实战


    目录

    1. 定义: AOP(Aspect Orient Programming)

    2. 实现原理:  代理(CGLIB代理和JDK代理)

    3. 相关术语: 

    4. 实战 (执行一个方法后查看执行时间)

    5. 自定义注解

    6. 导入依赖 Aop 

    7. 创建切面对象 (创建切面类)

    8. 按照最上面4方法里的controller 进行测试

    ---------> test01

     -----------> test02

    ===> 结束了  最最简单的aop入门 around方法   

    ****  思维拓展: ****

    两个拓展思维

    1. 写一个接口限制(单体项目的熔断策略)

    2. aop+redis实现缓存=> springboot整合的@EnableCaching

    2.1 查询商品列表 查询后存入redis 下次看缓存中是否存在

    2.2 列表更新修改 缓存也需要修改 如何实现!!!


    1. 定义: AOP(Aspect Orient Programming)

    是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程(OOP)的一种补充和完善。实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面理解为一个动态过程(在对象运行时动态织入一些扩展功能或控制对象执行)

    AOP面向切面的运行期代理方式,理解为一个动态过程,可以在对象运行时动态织入一些扩展功能或控制对象执行。

    2. 实现原理:  代理(CGLIB代理和JDK代理)

    AOP可以在系统启动时为目标类型创建子类或兄弟类型对象,这样的对象我们通常会称之为动态代理对象.

    其中,为目标类型(XxxServiceImpl)创建其代理对象方式有两种(先了解):

    第一种方式:借助JDK官方API为目标对象类型创建其兄弟类型对象,但是目标对象类型需要实现相应接口.
    第二种方式:借助CGLIB库为目标对象类型创建其子类类型对象,但是目标对象类型不能使用final修饰.

    3. 相关术语: 

    1. 切面(aspect): 横切面对象,一般为一个具体类对象。
    2. 切入点(pointcut):定义了切入扩展业务逻辑的位置(哪些方法运行时切入扩展业务),一般会通过表达式进行相关定义,一个切面中可以定义多个切入点。
    3. 通知(Advice): 内部封装扩展业务逻辑的具体方法对象,一个切面中可以有多个通知(在切面的某个特定位置上执行的动作(扩展功能)。
    4. 连接点(joinpoint):程序执行过程中,封装了某个正在执行的目标方法信息的对象,可以通过此对象获取具体的目标方法信息,甚至去调用目标方法。连接点与切入点定义如图所示:

    4. 实战 (执行一个方法后查看执行时间)

    随便写一个controller  写两个不同的方法 区分是否使用aop

    详细解释在代码中  基本每行我都写注释了

    1. package cn.pingzhuyan.testAop.controller;
    2. import cn.pingzhuyan.testAop.annotation.TimeCount;
    3. import cn.pingzhuyan.testAop.aspect.TimeCountLogAspect;
    4. import lombok.SneakyThrows;
    5. import org.slf4j.Logger;
    6. import org.slf4j.LoggerFactory;
    7. //import org.springframework.cloud.context.config.annotation.RefreshScope;
    8. import org.springframework.web.bind.annotation.GetMapping;
    9. import org.springframework.web.bind.annotation.RequestMapping;
    10. import org.springframework.web.bind.annotation.RestController;
    11. /**
    12. * 简易aop 模拟
    13. * 每个接口 我现在都需要 请求时间(ms)
    14. * 有一百个接口 难道我要写一百次这个方法吗 当然是不能的
    15. * 根据封装思想 把公共的地方抽出去
    16. *

    17. * 这个时候 继承就可以实现了 但是java是单继承
    18. * 而这个业务 是重要 但是不能影响其他业务, 也就是锦上添花的业务(暂时这么理解)
    19. *

    20. * 使用新技术 aop 面向切面编程
    21. * 定义: 切面编程 切面是一个类 切入点是一个方法 切面类里面有一个切入点方法 切入点方法里面有业务逻辑
    22. *

    23. * around() 最常用(优先级最高) 讲的也是这种 //@Order(2) 注解代表执行优先级 可以自定义一下
    24. * before()
    25. * after()
    26. * afterReturning()
    27. * afterThrowing()
    28. * afterFinally()
    29. *
    30. * @Author pzy
    31. * @Version 0.1.0
    32. * @CreateTime 2022/08/14
    33. */
    34. //@RefreshScope
    35. @RestController
    36. @RequestMapping("/test0101")
    37. public class AopTestController {
    38. private static final Logger log = LoggerFactory.getLogger(TimeCountLogAspect.class);
    39. /**
    40. * 不加aop (java代码实现)
    41. *
    42. * @param
    43. * @return
    44. */
    45. // @SneakyThrows
    46. @GetMapping("/test01")
    47. public String test01() throws InterruptedException {
    48. long startTime = System.currentTimeMillis();
    49. // log.debug("不加aop的测试->{}","true");
    50. System.out.println("开始执行~~~");
    51. //模拟耗时操作
    52. Thread.sleep(3000);
    53. long endTime = System.currentTimeMillis();
    54. System.out.println("不加aop的测试--->" + (endTime - startTime) + "ms");
    55. System.out.println("程序结束~~~");
    56. return "不加aop的测试!!!";
    57. }
    58. /**
    59. * 加aop(注解实现)
    60. *
    61. * @param
    62. * @return
    63. */
    64. @SneakyThrows
    65. @TimeCount(value = "pzy随意写的 看到这个值就行", operation = "加aop的测试 看到我是在哪写的")
    66. @GetMapping("/test02")
    67. public String test02() {
    68. Thread.sleep(3000);
    69. return "加aop的测试!!!";
    70. }
    71. }

    注意:  里面爆红的地方 (先注释掉, 写最简淡的controller 返回一个字符串)

    5. 自定义注解

    创建注解类型,应用于切入点表达式的定义

    详细解释在代码中  基本每行我都写注释了

    1. package cn.pingzhuyan.testAop.annotation;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. /**
    7. * @Author pzy
    8. * @Version 0.1.0
    9. * @CreateTime 2022/08/14
    10. * 自定义注解 时间输出(简单)
    11. */
    12. @Retention(RetentionPolicy.RUNTIME)
    13. @Target(ElementType.METHOD)
    14. public @interface TimeCount {
    15. /**
    16. * 操作信息
    17. * @return
    18. */
    19. String operation();
    20. /**
    21. * value 默认值 可以不写
    22. * @return
    23. */
    24. String value() default "";
    25. }

    6. 导入依赖 Aop 

    spring-boot-starter-aop 跟springboot统一版本一致

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

    7. 创建切面对象 (创建切面类)

    里面的反射可以先不看, 但是只要使用注解内的对象类型 就需要反射 
    (例如次数 过期秒数 熔断策略等)

    详细解释在代码中  基本每行我都写注释了

    1. package cn.pingzhuyan.testAop.aspect;
    2. import cn.pingzhuyan.testAop.annotation.TimeCount;
    3. import org.aspectj.lang.ProceedingJoinPoint;
    4. import org.aspectj.lang.annotation.Around;
    5. import org.aspectj.lang.annotation.Aspect;
    6. import org.aspectj.lang.annotation.Pointcut;
    7. import org.aspectj.lang.reflect.MethodSignature;
    8. import org.slf4j.Logger;
    9. import org.slf4j.LoggerFactory;
    10. import org.springframework.stereotype.Component;
    11. import java.lang.reflect.Method;
    12. /**
    13. * @Author pzy
    14. * @Description: TODO
    15. * @Version 0.1.0
    16. * @CreateTime 2022/08/14
    17. */
    18. @Aspect //切面类 就是声明我是一个切面
    19. @Component //交给 spring bean容器管理 看看我之前总结的注解 没了我博客都有
    20. public class TimeCountLogAspect {
    21. //这个等效于 @Slf4j -> 写了这个 不用写 @Slf4j 知道即可 设计模式: 简单工厂模式
    22. private static final Logger log = LoggerFactory.getLogger(TimeCountLogAspect.class);
    23. /**
    24. * @Pointcut注解用于定义切入点
    25. * @annotation(注解)为切入点表达式,后续由此注解描述的方法为切入 点方法
    26. */
    27. @Pointcut("@annotation(cn.pingzhuyan.testAop.annotation.TimeCount)")
    28. public void doLog() {
    29. }//此方法只负责承载切入点的定义
    30. /**
    31. * @param jp 连接点对象,此对象封装了要执行的目标方法信息.
    32. * 可以通过连接点对象调用目标方法.
    33. * @return 目标方法的执行结果
    34. * @throws Throwable
    35. * @Around注解描述的方法,可以在切入点执行之前和之后进行业务拓展,
    36. */
    37. @Around("doLog()")
    38. public Object doAround(ProceedingJoinPoint jp) throws Throwable {
    39. log.error("========= 区分主动pzy(警示非异常) ========");
    40. long t1 = System.currentTimeMillis();
    41. System.out.println("aop程序开始~~~" + t1);
    42. /*反射获取目标方法的参数 可以不会 之前讲过反射和暴力反射 需要操作类或者方法时使用*/
    43. //获取目标方法的类对象
    44. Class targetCls = jp.getTarget().getClass();
    45. //获取目标方法的反射对象
    46. MethodSignature ms = (MethodSignature) jp.getSignature();
    47. //获取目标方法
    48. Method targetMethod = targetCls.getMethod(ms.getName(), ms.getParameterTypes());
    49. //获取目标方法的注解
    50. TimeCount annotation = targetMethod.getAnnotation(TimeCount.class);
    51. String operation = annotation.operation();
    52. String value = annotation.value();
    53. try {
    54. //执行目标方法(切点方法中的某个方法)
    55. Object result = jp.proceed();
    56. long t2 = System.currentTimeMillis();
    57. log.info("opertime:{}", t2 - t1);
    58. System.out.println("正确执行 -> " + operation + ":" + value + ":" + (t2 - t1));
    59. System.out.println("aop程序执行结束~~~" + t2);
    60. return result;//目标业务方法的执行结果
    61. } catch (Throwable e) {
    62. e.printStackTrace();
    63. long t2 = System.currentTimeMillis();
    64. // log.info("exception:{}", e.getMessage());
    65. System.out.println("错误执行 -> " + operation + ":" + value + ":" + (t2 - t1));
    66. throw e;
    67. }
    68. }
    69. }

    8. 按照最上面4方法里的controller 进行测试

    ---------> test01

    http://localhost:9000/test0101/test01

     -----------> test02

    http://localhost:9000/test0101/test02


    ===> 结束了  最最简单的aop入门 around方法   

    ****  思维拓展: ****

    两个拓展思维

    1. 写一个接口限制(单体项目的熔断策略)

    需求:  (60秒内 一个ip 可以访问10次 , 超过返回error 提示: 稍后重试!!!)aop配合redis  



    2. aop+redis实现缓存=> springboot整合的@EnableCaching

    2.1 查询商品列表 查询后存入redis 下次看缓存中是否存在

    ->  存在显示

    -> 不存在插库然后存redis      循环

    2.2 列表更新修改 缓存也需要修改 如何实现!!!

    pzy 20220814

  • 相关阅读:
    微信小程序 基础 知识点汇总 总结
    LeetCode算法心得——全排列(回溯型排列)
    报表生成器FastReport .Net用户指南:关于脚本(上)
    python如何判断是否是闰年 闰年共有366天 2月份多一天29天
    Webrtc官方Demo公网服务器搭建
    【python 】----Pytest基础知识与进阶知识
    C++ 99 之 容器存取
    c语言 --- 分支结构
    工业级wifi路由器 功能 接口参数
    两台群晖NAS之间使用FTP或SFTP进行数据高速拷贝问题
  • 原文地址:https://blog.csdn.net/pingzhuyan/article/details/126332270