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

这篇具有很好参考价值的文章主要介绍了spring-boot-starter-aop及其使用场景说明。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

如今,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 自定义的自动配置模块用来解决“应用性能数据采集”的问题。

spring-boot-starter-aop及其使用场景说明
图 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。文章来源地址https://www.toymoban.com/news/detail-434955.html

到了这里,关于spring-boot-starter-aop及其使用场景说明的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 使用Spring Boot AOP实现日志记录

    使用Spring Boot AOP实现日志记录

    目录 介绍 1.1 什么是AOP 1.2 AOP体系与概念 AOP简单实现 2.1 新建一个SpringBoot项目,无需选择依赖 2.2 设置好本地Maven配置后,在pom.xml文件里添加添加maven依赖 2.3 创建一个业务类接口 2.4 在实体类实现接口业务  2.5 在单元测试运行结果 2.6 创建切面类 2.7 再次运行测试  总结 1.

    2024年02月14日
    浏览(15)
  • spring boot 使用AOP实现是否已登录检测

            前后端分离的开发中,用户http请求应用服务的接口时, 如果要求检测该用户是否已登录。可以实现的方法有多种, 本示例是通过aop 的方式实现,简单有效。         约定:前端http的post 请求 1、在pom.xml 引用 2、创建插入标记 3、实现切入类 4 建立api接口,在需要检

    2024年02月20日
    浏览(15)
  • Java使用Milo实现OPC UA客户端,封装spring boot starter

    Java使用Milo实现OPC UA客户端,封装spring boot starter

    最新版本更新日志查看:https://github.com/kangaroo1122/milo-spring-boot-starter/blob/main/UPDATE.md、https://gitee.com/vampire001/milo-spring-boot-starter/blob/master/UPDATE.md,此处不再更新 由eclipse开源,地址:https://github.com/eclipse/milo,可以基于此开发OPC UA客户端或者服务端。 本文介绍基于milo 封装的sp

    2024年02月09日
    浏览(19)
  • 使用easypoi-spring-boot-starter 4.1.1导入excel报错NoSuchMethodError和NoSuchMethodError

    使用easypoi进行excel的导入遇到的错误以及解决办法 easypoi项目地址:https://gitee.com/lemur/easypoi easypoi的Maven依赖: 报错描述: 解决办法: XmlOptions.setEntityExpansionLimit() 错误,是 jar 包版本引起的,3.0 版本以下的 xmlbeans 中根本没有该方法,需要将jar升级到 3.0+ 版本才可以。另外

    2024年02月08日
    浏览(11)
  • 【Elasticsearch】spring-boot-starter-data-elasticsearch的使用以及Elasticsearch集群的连接

    【Elasticsearch】spring-boot-starter-data-elasticsearch的使用以及Elasticsearch集群的连接

    更多有关博主写的往期Elasticsearch文章 标题 地址 【ElasticSearch 集群】Linux安装ElasticSearch集群(图文解说详细版) https://masiyi.blog.csdn.net/article/details/131109454 基于SpringBoot+ElasticSearch 的Java底层框架的实现 https://masiyi.blog.csdn.net/article/details/121534307 ElasticSearch对标Mysql,谁能拔得头筹

    2024年02月11日
    浏览(12)
  • Spring Boot Starter Data Redis使用Lettuce客户端报错:NOAUTH Authentication required

    Spring Boot Starter Data Redis使用Lettuce客户端报错:NOAUTH Authentication required

    Spring Boot版本升级为:2.6.14 redis依赖: redis配置不变,还是带password的: 项目启动后,获取redis连接时,报错:NOAUTH Authentication required spring-boot-starer-data-redis支持使用Jedis和Lettuce作为redis客户端,如果配置不指定则默认使用Lettuce。 不管是Lettuce还是还是Jedis,核心是构建RedisCo

    2024年01月25日
    浏览(15)
  • Springbootg整合RocketMQ ——使用 rocketmq-spring-boot-starter 来配置发送和消费 RocketMQ 消息

           本文解析将 RocketMQ Client 端集成为 spring-boot-starter 框架的开发细节,然后通过一个简单的示例来一步一步的讲解如何使用这个 spring-boot-starter 工具包来配置,发送和消费 RocketMQ 消息。 添加maven依赖: 修改application.properties 注意: 请将上述示例配置中的 127.0.0.1:9876 替换

    2024年03月22日
    浏览(13)
  • Spring Boot - spring-boot-starter

    spring-boot-starter 当学习Spring Boot时,可以通过一个完整的案例来理解和实践其基本概念和功能。以下是一个简单的Spring Boot Starter完整案例,展示了如何创建一个基本的Web应用程序: 首先,创建一个名为pom.xml的Maven项目文件,添加以下内容:idea或其他直接创建直接跳过!

    2024年02月09日
    浏览(18)
  • Spring Boot Starters

    Spring Boot Starters 概述 Spring Boot Starters是一系列为特定应用场景预设的依赖管理和自动配置方案。每个Starter都是为了简化特定类型的项目构建和配置。例如, spring-boot-starter-web 是为创建基于Spring MVC的Web应用程序而设计的。 Starter的结构 一个典型的Starter包含以下部分: pom.xml

    2024年01月25日
    浏览(35)
  • Spring Boot Starter Parent

    Spring Boot Starter Parent

    在这,您将学习了解 Spring Boot Starter Parent, 它是 Spring Boot 提供的父级 Pom 文件,旨在提供自动版本依赖管理,帮助我们轻松快速地进行 Spring Boot 开发。 通过 Spring Boot Starter Parent, 我们可以进行简单便捷地包依赖管理。在 Spring Boot 每一个发行版中, 均提供了该版本所兼容的依

    2024年02月08日
    浏览(9)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包