spring-boot-starter-aop及其使用场景说明

news2024/11/14 4:03:57

如今,AOP(Aspect Oriented Programming)已经不是什么崭新的概念了,在经历了代码生成、动态代理、字节码增强甚至静态编译等不同时代的洗礼之后,Java 平台上的 AOP 方案基本上已经以 SpringAOP 结合 AspectJ 的方式稳固下来(虽然大家依然可以自己通过各种字节码工具偶尔“打造一些轮子”)。

现在 Spring 框架提供的 AOP 方案倡导了一种各取所长的方案,即使用 SpringAOP 的面向对象的方式来编写和组织织入逻辑,并使用 AspectJ 的 Pointcut 描述语言配合 Annotation 来标注和指明织入点(Jointpoint)。

原则上来说,我们只要引入 Spring 框架中 AOP 的相应依赖就可以直接使用 Spring 的 AOP 支持了,不过,为了进一步为大家使用 SpringAOP 提供便利,SpringBoot 还是“不厌其烦”地为我们提供了一个 spring-boot-starter-aop 自动配置模块。

spring-boot-starter-aop 自动配置行为由两部分内容组成:

  1. 位于 spring-boot-autoconfigure的org.springframework.boot.autoconfigure.aop.AopAutoConfiguration 提供 @Configuration 配置类和相应的配置项。
  2. spring-boot-starter-aop 模块自身提供了针对 spring-aop、aspectjrt 和 aspectjweaver 的依赖。

一般情况下,只要项目依赖中加入了 spring-boot-starter-aop,其实就会自动触发 AOP 的关联行为,包括构建相应的 AutoProxyCreator,将横切关注点织入(Weave)相应的目标对象等,不过 AopAutoConfiguration 依然为我们提供了可怜的两个配置项,用来有限地干预 AOP 相关配置:

  • spring.aop.auto=true
  • spring.aop.proxy-target-class=false

对我们来说,这两个配置项的最大意义在于:允许我们投反对票,比如可以选择关闭自动的 aop 配置(spring.aop.auto=false),或者启用针对 class 而不是 interface 级别的 aop 代理(aop proxy)。

AOP 的应用场景很多,我们不妨以当下最热门的 APM(Application Performance Monitoring)为实例场景,尝试使用 spring-boot-starter-aop 的支持打造一个应用性能监控的工具原型。

spring-boot-starter-aop 在构建 spring-boot-starter-metrics 自定义模块中的应用

对于应用性能监控来说,架构逻辑上其实很简单,基本上就是三步走(如图 1 所示)。

本节暂时只构建一个 spring-boot-starter-metrics 自定义的自动配置模块用来解决“应用性能数据采集”的问题。


图 1 应用性能监控关键环节示意图

 

在此之前,有几个原则我们需要先说明一下:

虽然说采集应用性能数据可以帮助我们更好地分析和改进应用的性能指标,但这不意味着可以借着 APM 的名义对应用的核心职能形成侵害,加上应用性能数据采集功能一定会对应用的性能本身带来拖累,你拿到的所谓性能数据是分摊了你的数据采集方案带来的负担,所以,一般情况下,最好把应用性能数据采集模块的性能损耗控制在 10% 以内甚至更小。

SpringAOP 其实提供了多种横切逻辑织入机制(Weaving),性能损耗上也是各有差别,从运行期间的动态代理和字节码增强 Weavng,到类加载期间的 Weaving,甚至高冷的 AspectJ 二次静态编译 Weaving,大家可以根据情况灵活把握。

针对应用性能数据的采集,最好对应用开发者是透明的,通过配置外部化的形式,可以最大限度地剥离这部分对应用开发者来说非核心的关注点,只在部署和运行之前确定采集点并配置上线即可。

虽然本节实例采用基于 @Annotation 的方式来标注性能采集点,但不意味着这是最优的方式,更多是基于技术方案(SpringAOP)的现状给出的一种实践方式。

下面我们正式着手构建 spring-boot-starter-metrics 自定义的自动配置模块的设计和实现方案。

笔者一向是只在有必要的时候才重新“造轮子”,绝不会为了炫技而去“造轮子”,所以,本次的主角我们选择 Java 中的 Dropwizard Metrics 这个类库作为打造我们 APM 原型的起点。

Dropwizard Metrics 为我们提供了多种不同类型的应用数据度量方案,且通过相应的数据处理算法在性能和批量状态的管理上做了很优秀的工作,只不过,如果我们直接用它的 API 来对自己的应用代码进行度量的话,那写起来代码太多,而且这些性能代码混杂在应用的核心逻辑执行路径上,一个是界面不友好,另外一个就是不容易维护:

  1. public class MockService implements InitializingBean {
  2. @Autowired
  3. MetricRegistry metricRegistry;
  4. private Timer timer;
  5. private Counter counter;
  6. // define more other metrics...
  7. public void doSth() {
  8. counter.inc();
  9. Timer.Context context = timer.time();
  10. try {
  11. System.out.println("just do something.");
  12. } finally {
  13. context.stop();
  14. }
  15. }
  16. @Override
  17. public void afterPropertiesSet() throws Exception {
  18. timer = metricRegistry.timer("timerToProfilingDoSthMethod");
  19. counter = metricRegistry.counter("counterForDoSthMethod");
  20. }
  21. }

所以,对于这些非功能性的性能度量代码,我们可以使用 AOP 的方式剥离到相应的 Aspect 中单独维护,而为了能够将这些性能度量的 Aspect 挂接到指定的待度量代码上,基于现有的方案选型。

可以使用 metrics-annotation 提供的一系列 Annotation 来标注织入位置,这样,开发者只要在需要度量的代码位置上标注相应的 Annotation,我们提供的 spring-boot-starter-metrics 自定义的自动配置模块就会自动地收集这些位置上指定的性能度量数据。

首先,我们通过 http://start.spring.io/ 构建一个 SpringBoot 的脚手架项目,选择以 Maven 编译(选择用 Gradle 的同学自行甄别后面的配置如何具体进行),然后在创建好的 SpringBoot 脚手架项目的 pom.xml 中添加如下必要配置:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  5. http://maven.apache.org/xsd/maven-4.0.0.xsd">
  6. <modelVersion>4.0.0</modelVersion>
  7. <groupId>com.keevol</groupId>
  8. <artifactId>spring-boot-starter-metrics</artifactId>
  9. <version>0.0.1-SNAPSHOT</version>
  10. <packaging>jar</packaging>
  11. <name>spring-boot-starter-metrics</name>
  12. <description>auto configuration module for dropwizard metrics</description>
  13. <parent>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-parent</artifactId>
  16. <version>1.3.0.RELEASE</version>
  17. <relativePath /> <!-- lookup parent from repository -->
  18. </parent>
  19. <properties>
  20. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  21. <java.version>1.8</java.version>
  22. <metrics.version>3.1.2</metrics.version>
  23. </properties> <!--其他配置 -->
  24. <dependencies>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter</artifactId>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-aop</artifactId>
  32. </dependency>
  33. <dependency>
  34. <groupId>org.springframework.boot</groupId>
  35. <artifactId>spring-boot-starter-actuator</artifactId>
  36. </dependency>
  37. <dependency>
  38. <groupId>io.dropwizard.metrics</groupId>
  39. <artifactId>metrics-core</artifactId>
  40. <version>
  41. ${metrics.version}
  42. </version>
  43. </dependency>
  44. <dependency>
  45. <groupId>io.dropwizard.metrics</groupId>
  46. <artifactId>metrics-annotation</artifactId>
  47. <version>${metrics.version}</version>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.aspectj</groupId>
  51. <artifactId>aspectjrt</artifactId>
  52. <version>1.8.7</version>
  53. </dependency>
  54. </dependencies>
  55. </project>

pom.xml 中有几个关键配置需要关注:

  • 继承了 spring-boot-starter-parent,用于加入 springboot 的相关依赖。
  • 添加了 spring-boot-starter-aop 依赖。
  • 添加了 io.dropwizard.metrics 下相应的依赖,用来引入 dropwizard metrics 类库和必要的 Annotations。
  • 添加了 spring-boot-starter-actuator,这个自动配置模块教程后面会跟大家进一步介绍,在这里我们主要是引入它对 dropwizard metrics 和 JMX 的一部分自动配置逻辑,比如针对 MetricRegistry 和 MBeanServer 的自动配置,这样我们就可以直接 @Autowired 来注入使用 MetricRegistry 和 MBeanServer。

至于 aspectjrt,是使用了最新的版本,原则上spring-boot-starter-aop已经有依赖,这里可以不用明确添加配置。

如果单单是一个提供必要依赖的自动配置模块,那么到这里其实就可以结束了,但我们的 spring-boot-starter-metrics 需要使用 AOP 提供相应的横切关注点逻辑。

所以,还需要编写并提供一些必要的代码组件,因此,最少我们先要提供一个 @Configuration 配置类,用于将我们即将提供的这些 AOP 逻辑暴露给使用者:

  1. @Configuration
  2. @ComponentScan({ "com.keevol.springboot.metrics.lifecycle",
  3. "com.keevol.springboot.metrics.aop" })
  4. @AutoConfigureAfter(AopAutoConfiguration.class)
  5. public class DropwizardMetricsMBeansAutoConfiguration {
  6. @Value("${metrics.mbeans.domain.name:com.keevol.metrics}")
  7. String metricsMBeansDomainName;
  8. @Autowired
  9. MBeanServer mbeanServer;
  10. @Autowired
  11. MetricRegistry metricRegistry;
  12. @Bean
  13. public JmxReporter jmxReporter() {
  14. JmxReporter reporter = JmxReporte.forRegistry(metricRegistry)
  15. .inDomain(metricsMBeansDomainName).registerWith(mbeanServer)
  16. .build();
  17. return reporter;
  18. }
  19. }

然后就是将这个配置类添加到 META-INF/spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.keevol.springboot.metrics.autocfg.DropwizardMetricsMBeansAuto-ConfigurationOK,
不要认为将 spring-boot-starter-metrics 打包作为类库发布出去就可以了,AOP 相关的代码还没写。

我们回头来看 DropwizardMetricsMBeansAutoConfiguration 配置类,这个配置类的实现很简单,注入了 MBeanServer 和 MetricRegistry 的实例,并开放了一个 metrics.mbeans.domain.name 配置属性(默认值 com.keevol.metrics)便于使用者指定自定义的 MBean 暴露和访问的命名空间。

当然,以上给这些其实都不是重点,因为它们都只是为了将我们要采集的性能数据指标以 JMX 的形式暴露出去而服务的,重点在于 DropwizardMetricsMBeansAutoConfiguration 头顶上的那几顶“帽子”:

  • @Configuration 自然不必说了,这是一个 JavaConfig 配置类。
  • @ComponentScan({“com.keevol.springboot.metrics.lifecycle”,”com.keevol.springboot.metrics.aop”}),为了简便,让 @ComponentScan 把这两个 java package 下的所有组件都加载到 IoC 容器中,这些组件就包括我们要提供的一系列与 AOP 和 Dropwizard Metrics 相关的实现逻辑。
  • @AutoConfigureAfter(AopAutoConfiguration.class)告诉 SpringBoot:“我希望 DropwizardMetricsMBeansAutoConfiguration 在 AopAutoConfiguration 完成之后进行配置”。

现在,最后的秘密就隐藏在 @ComponentScan 背后的两个 java package 之下了。

首先是 com.keevol.springboot.metrics.aop,在这个 java package 下面,我们只提供了一个 AutoMetricsAspect,其定义如下:

  1. @Component
  2. @Aspectpublic
  3. class AutoMetricsAspect {
  4. protected ConcurrentMap<String, Meter> meters = new ConcurrentHashMap<>();
  5. protected ConcurrentMap<String, Meter> exceptionMeters = new ConcurrentHashMap<>();
  6. protected ConcurrentMap<String, Timer> timers = new ConcurrentHashMap<>();
  7. protected ConcurrentMap<String, Counter> counters = new ConcurrentHashMap<>();
  8. @Autowired
  9. MetricRegistry metricRegistry;
  10. @Pointcut(value = "execution(public * *(..))")
  11. public void publicMethods() {
  12. }
  13. @Before("publicMethods() && @annotation(countedAnnotation)")
  14. public void instrumentCounted(JoinPoint jp, Counted countedAnnotation) {
  15. String name = name(jp.getTarget().getClass(), StringUtils.hasLength(countedAnnotation.name()) ? countedAnnotation.name() : jp.getSignature().getName(), "counter");
  16. Counter counter = counters.computeIfAbsent(name, key -> metricRegistry.counter(key));
  17. counter.inc();
  18. }
  19. @Before("publicMethods() && @annotation(meteredAnnotation)")
  20. public void instrumentMetered(JoinPoint jp, Metered meteredAnnotation) {
  21. String name = name(jp.getTarget().getClass(), StringUtils.hasLength(meteredAnnotation.name()) ? meteredAnnotation.name() : jp.getSignature().getName(), "meter");
  22. Meter meter = meters.computeIfAbsent(name, key -> metricRegistry.meter(key));
  23. meter.mark();
  24. }
  25. @AfterThrowing(pointcut = "publicMethods() && @annotation(exMe-teredAnnotation)", throwing = "ex")
  26. public void instrumentExceptionMetered(JoinPoint jp, Throwable ex, ExceptionMetered exMeteredAnnotation) {
  27. String name = name(jp.getTarget().getClass(), StringUtils.hasLength(exMeteredAnnotation.name()) ? exMeteredAnnotation.name() : jp.getSignature().getName(), "meter", "exception");
  28. Meter meter = exceptionMeters.computeIfAbsent(name, meterName -> metricRegistry.meter(meterName));
  29. meter.mark();
  30. }
  31. @Around("publicMethods() && @annotation(timedAnnotation)")
  32. public Object instrumentTimed(ProceedingJoinPoint pjp, Timed timedAnnotation) throws Throwable {
  33. String name = name(pjp.getTarget().getClass(), StringUtils.hasLength(timedAnnotation.name()) ? timedAnnotation.name() : pjp.getSignature().getName(), "timer");
  34. Timer timer = timers.computeIfAbsent(name, inputName -> metricRegistry.timer(inputName));
  35. Timer.Context tc = timer.time();
  36. try {
  37. return pjp.proceed();
  38. } finally {
  39. tc.stop();
  40. }
  41. }
  42. }

@Aspect+@Component 的目的在于告诉 Spring 框架:“我是一个 AOP 的 Aspect 实现类并且你可以通过 @ComponentScan 把我加入 IoC 容器之中。”当然,这不是重点。

io.dropwizard.metrics:metrics-annotation 这个依赖包为我们提供了几个有趣的 Annotation:

  • Timed
  • Gauge
  • Counted
  • Metered
  • ExceptionMetered

这些语义良好的 Annotation 定义可以用来标注相应的 AOP 逻辑扩展点,比如,针对同一个 MockService,我们可以将性能数据的度量和采集简化为只标注一两个 Annotation 就可以了:

  1. @Component
  2. public class MockService {
  3. @Timed
  4. @Counted
  5. public void doSth() {
  6. System.out.println("just do something.");
  7. }
  8. }

但是,Annotation 注定只是 Annotation,它们只是一些标记信息,要让它们发挥作用,需要有“伯乐”的眷顾,所以,AutoMetricsAspect 在这里就是这些 Dropwizard Metrics Annotation 的“伯乐”。

通过拦截每一个 public 方法并检查方法上是否存在某个 metrics annotation,我们就可以根据具体的 metrics annotation 的类型,为匹配的方法注入相应性能数据采集代码逻辑,从而完成整个基于 AOP 和 dropwizard metrics 的应用性能数据采集方案的实现。

受限于 SpringAOP 自身的一些限制,并不是所有 AOP 的 Joinpoint 类型都支持,而且,以上原型代码方向也不见得是性能最优的方案,大家需要结合自己的目标和手上可用的技术手段,根据自己的具体应用场景具体分析和权衡。

至此,整个基于 spring-boot-starter-aop 的 spring-boot-starter-metrics 自定义自动配置模块宣告完工,对于想了解更多细节,或者想寻找直接可用方案的读者,可以参考开源项目 https://github.com/ryantenney/metrics-spring。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/67303.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于独立分量分析进行模态分解(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

[附源码]计算机毕业设计基于Springboot在线教育系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Mysql详细安装步骤

目录 1、解压服务端Mysql安装包 2.复制改变my.ini文件 3、安装MySQL服务 4、启动mysql服务 6.记录初始密码&#xff0c;利用初始密码登录 &7.改变MySQL链接密码 1、解压服务端Mysql安装包 解压之后的目录就是以上这样的。 2.复制改变my.ini文件 把my.ini文件添加…

【计算机图形学入门】笔记3:变换Transformation(二维与三维)

第三章.Transformation变换&#xff08;二维与三维&#xff09;1.为什么要学习变换&#xff1f;2.变换的几种形式1.缩放2.相对于y轴翻转3.Shear Matrix 切片4.旋转3.齐次坐标1.平移变换2.齐次坐标的引入3.使用齐次坐标表示上述变换4.组合变换5.分解变换6.三维空间中的变换1.三维…

QT 系统学习 day03 了解各种控件,文件操作,消息框,windows应用界面的 生成菜单栏, 状态栏,中心部件,工具栏,

1. 控件&#xff0c; 旋钮 &#xff0c;进度条&#xff0c; 我也不知道叫啥&#xff0c; 相关的代码&#xff1b; 首先是函数 1.旋钮函数 &#xff08;槽函数都有说明&#xff09;&#xff08;Dial&#xff09; ui->dial->setRange(0,100);//设置旋钮的范围ui->di…

Three.js一学就会系列:01 第一个3D网站

文章目录前言一、Three.js是什么&#xff1f;官网官网示例效果尝鲜二、使用步骤1.引入three.js库2.使用方法创建一个场景创建一个透视摄像机将渲染器添加到页面上创建一个立方体渲染场景立方体动起来效果总结前言 最近开始入坑前端3D建站&#xff0c;跟大家一起慢慢深入three.…

Android 面试拒收Offer篇,这样做对吗?

作者&#xff1a;如梦 如梦朦胧 朋友们的劝说下&#xff0c;有了换工作的躁动,然后投了某度的Android岗位,本以为像我这种非211、985没工作经验的渣渣只能被直接pass,结果却意外的收到了电话,真是受宠若惊.经过电面,技术三面,然后就是等通知到最后拿到了OFFER,如梦一般,真是挺…

性能优化:Redis使用优化(1)

参考资料&#xff1a; 《Redis为什么变慢了&#xff1f;一文讲透如何排查Redis性能问题 | 万字长文》 相关文章&#xff1a; 《Redis&#xff1a;内存淘汰机制》 《Redis&#xff1a;持久化RDB与AOF》 《Redis&#xff1a;主从复制》 写在开头&#xff1a;本文为学习后的总…

Spring中事务失效的场景

文章目录1 抛出检查异常导致事务不能正确回滚1.1 异常演示1.2 解决办法2 业务方法内自己 try-catch 异常导致事务不能正常回滚1.1 异常演示1.2 解决办法3 aop切面顺序导致事务不能正确回滚3.1 异常演示3.2 解决办法4 非 public 方法导致事务的失效4.1 异常演示4.2 解决办法5 父…

深度学习-全卷积神经网络(FCN)

1. 简介 全卷积神经网络&#xff08;Fully Convolutional Networks&#xff0c;FCN&#xff09;是Jonathan Long等人于2015年在Fully Convolutional Networks for Semantic Segmentation一文中提出的用于图像语义分割的一种框架&#xff0c;是深度学习用于语义分割领域的开山之…

【目标检测】【DDPM】DiffusionDet:用于检测的概率扩散模型

文章目录摘要一、Introduction二、相关工作三、方法1.准备工作2.架构3、训练4.预测过程四. 实验1.训练策略2.main property3.消融实验五、代码分析1.测试 demo.py2.训练 train-net.py总结摘要 我们提出了扩散det&#xff0c;一个新的框架&#xff0c;将目标检测作为一个从噪声…

OpenGL基础程序结构

用OpenGL编写的程序结构类似于用其他语言编写的程序。实际上&#xff0c;OpenGL是一个丰富的三维图形函数库&#xff0c;编写OpenGL程序并非难事&#xff0c;只需在基本C语言中调用这些函数&#xff0c;用法同Turbo C、Microsoft C等类似&#xff0c;但也有许多不同之处。   …

[附源码]Python计算机毕业设计Django校园招聘系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

功能测试求职难,现在不懂自动化测试连外包都进不去了?

功能测试求职难 最近因为公司政策原因&#xff0c;部分外包被裁员&#xff0c;其中不乏能力还不错&#xff0c;工作也挺踏实的&#xff0c;比较可惜&#xff0c;为了帮助他们尽快找到下家&#xff0c;我这边也开始帮他们关注招聘情况&#xff0c;发现一个挺让我意外的事情。在…

Java中CAS详解

一、什么是CAS 什么是CAS机制 CAS机制是一种数据更新的方式。在具体讲什么是CAS机制之前&#xff0c;我们先来聊下在多线程环境下&#xff0c;对共享变量进行数据更新的两种模式&#xff1a;悲观锁模式和乐观锁模式。 悲观锁更新的方式认为&#xff1a;在更新数据的时候大概率…

Android 动画和过渡

Android 动画和过渡 &#xff08;一&#xff09; 动画 JetpackCompose提供了强大且可扩展的API&#xff0c;可以轻松地在应用程序的UI中实现各种动画。下面描述了如何使用这些API以及根据动画场景使用哪些API。 动画在现代移动应用程序中至关重要&#xff0c;以实现流畅和可理解…

【语音处理】基于自适应差分脉冲编码调制(ADPCM)的实现研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

目标检测算法——安全帽识别数据集(附下载链接)

&#x1f384;&#x1f384;近期&#xff0c;小海带在空闲之余收集整理了一批安全帽识别数据集供大家参考。 整理不易&#xff0c;小伙伴们记得一键三连喔&#xff01;&#xff01;&#xff01;&#x1f388;&#x1f388; 目录 一、安全帽佩戴数据集 二、SHWD安全帽佩戴检测…

五、分页总结

文章目录一、分页的几种方式1、使用关键字实现分页&#xff08;limit&#xff09;2、通过 RowBounds 类实现分页查询3、使用分页插件实现分页分页的作用&#xff1a;在进行查询时如果数据量庞大的话会造成大量的内存压力&#xff0c;让程序卡顿。这时候使用分页减少数据的处理量…

基于SpringBoot编写starter,自己写个类库引入使用

我们在做springboot项目的时候经常会创建项目后先引入各种starter&#xff0c;比如操作数据库的mybatis-plus-boot-starter&#xff0c;数据源的druid-spring-boot-starter等等。引入starter的好处就是可以将第三方的依赖库快速的整合到我们自己的springboot项目中&#xff0c;…