Spring Boot在为开发人员提供更高层次的封装,进而提高开发效率的同时,也为出现问题时如何进行定位带来了一定复杂性与难度。但Spring Boot同时又提供了一些诊断工具来辅助开发与分析,如spring-boot-starter-actuator。本文分享一个基于actuator与IDEA条件断点来定位自动配置未生效的案例。望对类似问题分析与处理提供参考。

欢迎关注我的微信公众号:jboost-ksxy

问题确认

在前文介绍的 Spring Boot从入门到实战:整合通用Mapper简化单表操作 中,我们对druid连接池做了自动配置,并且注入了druid的监控统计功能,如下

但本地运行后通过 http://localhost:8080/druid/index.html 访问时却出现错误,通过浏览器的开发者工具查看该请求返回404,推测上述代码中定义的StatViewServlet未注入成功。我们用actuator来确认下是否如此。在项目中加入spring-boot-starter-actuator,并且application.yml中添加如下配置

management:
endpoints:
web:
exposure:
include: "*"
exclude: beans,trace
endpoint:
health:
show-details: always

在spring-boot 2.x 版本当中,作为安全性考虑,将actuator 控件中的端口,只默认开放/health 和/info 两个端口,其他端口默认关闭, 因此需要添加如上配置。注意include的值 * 必须加引号,否则无法启动。

重启程序后访问 http://localhost:8080/actuator/conditions 确认上述两个实例化方法未满足@ConditionalOnProperty的条件,从而未执行生效,如图

条件断点

从上面分析确认是因为条件注解 @ConditionalOnProperty(prefix = "spring.datasource.druid", name = "druidServletSettings") 未满足使方法未执行导致。那这个条件为什么没有满足呢,查看application.yml中也做了 spring.datasource.druid.druidServletSettings属性的配置。

当你无法理清头绪,确定问题原因时,那就Debug吧。查看注解@ConditionalOnProperty源码,找到其实现支持类OnPropertyCondition,如下

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
String[] value() default {}; String prefix() default ""; String[] name() default {}; String havingValue() default ""; boolean matchIfMissing() default false;
}

查看OnPropertyCondition源码,了解它是通过getMatchOutcome方法来判断是否满足注解参数所指定的条件的,如下所示

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
metadata.getAllAnnotationAttributes(
ConditionalOnProperty.class.getName()));
List<ConditionMessage> noMatch = new ArrayList<>();
List<ConditionMessage> match = new ArrayList<>();
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
ConditionOutcome outcome = determineOutcome(annotationAttributes,
context.getEnvironment());
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
}
if (!noMatch.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
}
return ConditionOutcome.match(ConditionMessage.of(match));
}

在调用determineOutcome处打断点,调试什么原因导致条件未满足,但是这里是一个for循环,如果for元素过多的话,将可能需要断点阻断很多次才能找到你想要查看的那个元素。所幸IDEA提供了不同类型的断点来处理这类问题,前面 案例解析:使用IDEA异常断点来定位java.lang.ArrayStoreException的问题 我们介绍了异常断点的使用。这里介绍用条件断点来处理这类循环块中的debug问题。

在上述代码for循环中调用determineOutcome行打断点,并在断点上右键,弹出如下窗口

图中Condition框即可输入你要指定的条件,可以直接写java判断表达式代码,并引用该行代码处能访问的变量,如这里我们输入 annotationAttributes.get("name").equals("druidServletSettings"),然后点击Debug窗口的“Resume Program (F9)”按钮,则在不满足指定条件时,断点处将不会被阻断,直到条件满足,这样就能很容易定位到我们想要查看的元素。(当然这里allAnnotationAttributes变量其实只有一个元素,仅仅是为了演示条件变量的使用,当集合元素很多时,使用条件断点就能体会到它的方便之处)

问题定位

通过Debug的方式深入条件注解的判断逻辑(其中循环处可使用条件断点),最终来到如下代码片段

在这里是判断来自所有属性源配置的属性中,是否包含条件注解指定的属性,即spring.datasource.druid.druidServletSettings,由上图可见,spring.datasource.druid.druidServletSettings只是某些属性的前缀,并不存在完全匹配的属性,因此返回false,导致条件不满足。回看注解@ConditionOnProperty的javadoc,

* If the property is not contained in the {@link Environment} at all, the
* {@link #matchIfMissing()} attribute is consulted. By default missing attributes do not
* match.
* <p>
* This condition cannot be reliably used for matching collection properties. For example,
* in the following configuration, the condition matches if {@code spring.example.values}
* is present in the {@link Environment} but does not match if
* {@code spring.example.values[0]} is present.
*

当Environment中不包含该属性时,则看matchIfMissing的值,该值默认为false,如果包含该属性,则再对比属性值与havingValue的值,相等即满足,不等则不满足。并且该条件注解不能用于匹配集合类型属性。上述spring.datasource.druid.druidServletSettings实际上属于一个Map类型,因此不能想当然地认为该注解是只要属性集中某属性名称包含该值即满足。

总结

当难以定位到问题原因时,可以进行Debug,跟踪程序运行的各个步骤,当要在循环中Debug定位到某个元素时,可以用条件断点来实现。@ConditionalOnProperty注解不是存在某属性就行,还需要值相等,并且不适用于集合类型属性。

我的个人博客地址:http://blog.jboost.cn
我的github地址:https://github.com/ronwxy
我的微信公众号:jboost-ksxy

———————————————————————————————————————————————————————————————


欢迎关注我的微信公众号,及时获取最新分享

案例解析:springboot自动配置未生效问题定位(条件断点)的更多相关文章

  1. 小BUG大原理:重写WebMvcConfigurationSupport后SpringBoot自动配置失效

    一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小 BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小 BUG 我了解到不少 ...

  2. 小BUG大原理 | 第一篇:重写WebMvcConfigurationSupport后SpringBoot自动配置失效

    一.背景 公司的项目前段时间发版上线后,测试反馈用户的批量删除功能报错.正常情况下看起来应该是个小BUG,可怪就怪在上个版本正常,且此次发版未涉及用户功能的改动.因为这个看似小BUG我了解到不少未知的 ...

  3. SpringBoot自动配置注解原理解析

    1. SpringBoot启动主程序类: @SpringBootApplication public class DemoApplication { public static void main(S ...

  4. springboot自动配置源码解析

    springboot版本:2.1.6.RELEASE SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfig ...

  5. SpringBoot 系列教程自动配置选择生效

    191214-SpringBoot 系列教程自动配置选择生效 写了这么久的 Spring 系列博文,发现了一个问题,之前所有的文章都是围绕的让一个东西生效:那么有没有反其道而行之的呢? 我们知道可以通 ...

  6. SpringBoot自动配置探究

    @SpringBootApplication @SpringBootApplication表示SpringBoot应用,标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就 ...

  7. 如何编写Spring-Boot自动配置

    摘要 本文主要介绍如何把一个spring的项目(特别是一些公共工具类项目),基于spring boot的自动配置的思想封装起来,使其他Spring-Boot项目引入后能够进行快速配置. AutoCon ...

  8. SpringBoot实战之SpringBoot自动配置原理

    SpringBoot 自动配置主要通过 @EnableAutoConfiguration, @Conditional, @EnableConfigurationProperties 或者 @Confi ...

  9. 源码学习系列之SpringBoot自动配置(篇一)

    源码学习系列之SpringBoot自动配置源码学习(篇一) ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属 ...

随机推荐

  1. Delphi 中的字符串(还解释了shortstring)good

    一.Delphi 2009 之前的字符串(不支持 Unicode): Delphi 2009 之前的字符串分为 3 种:ShortString.AnsiString.WideString. [Shor ...

  2. 基于Geoserver发布时间地图

    Geoserver它是著名的开源GIS其中软件. 地图服务软件也经常使用的物品.基于geoserver和Openlayers它可以建立一个自由的,开放源码GIS工程. Geoserver公布地图的步骤 ...

  3. 跟我学ASP.NET MVC之十一:URL路由

    摘要: 在MVC框架之前,ASP.NET假定在请求的URLs和服务器硬盘文件之间有直接的关系.服务器的职责是接收浏览器请求,从相应的文件发送输出. 这种方法只能工作于Web表单,每一个ASPX页面既是 ...

  4. ehcache hibernate4整合问题

    当在原有hibernate使用ehcache缓存的项目中加入ehcache时,报如下错误时 Caused by: org.hibernate.service.spi.ServiceException: ...

  5. Ubuntu+NDK编译openssl(为了Android上使用libcurl且支持HTTPS协议)

    为了Android上使用libcurl且支持HTTPS协议,需要依赖openssl,因此先来了解一下如何编译OpenSSL1.编译ARM下的共享库(默认的)我使用的是guardianproject的o ...

  6. WPF学习笔记:(二)数据绑定模式与INotifyPropertyChanged接口

    数据绑定模式共有四种:OneTime.OneWay.OneWayToSource和TwoWay,默认是TwoWay.一般来说,完成数据绑定要有三个要点:目标属性是依赖属性.绑定设置和实现了INotif ...

  7. C# 特性的使用

    using ClassLibrary;using System;using System.Collections.Generic;using System.Linq;using System.Refl ...

  8. WPF DataGrid自定义列DataGridTextColumn.ElementStyle和DataGridTemplateColumn.CellTemplate

    <Window x:Class="DataGridExam.MainWindow"        xmlns="http://schemas.microsoft.c ...

  9. WPF中获取鼠标相对于屏幕的位置

    原文:WPF中获取鼠标相对于屏幕的位置 WPF中获取鼠标相对于屏幕的位置                                   周银辉WPF编程时,我们经常使用Mouse.GetPosi ...

  10. wcf中的消息模式

    1请求响应模式 a.wcf中的消息模式默认是请求响应模式 b.返回值是void默认也是请求响应模式,可返回服务端的错误信息 c.客户端在请求后,当前线程停止真到接受收服务器的响应 [Opereatio ...