如何在 bean 实例化之前记录 Spring Boot 应用程序的所有活动属性?

Posted

技术标签:

【中文标题】如何在 bean 实例化之前记录 Spring Boot 应用程序的所有活动属性?【英文标题】:How to log all active properties of a spring boot application before the beans instantiation? 【发布时间】:2018-06-21 02:51:29 【问题描述】:

已经有一个question 要求记录活动配置,有一个正确的answer,但问题是只有在正确实例化所有bean 时才会记录配置。即使(主要)应用程序在启动时崩溃,我也想记录所有属性。我的问题更具体:

如何在 bean 实例化之前记录 Spring Boot 应用程序的所有活动属性?

【问题讨论】:

【参考方案1】:

为此,您需要注册ApplicationListener。根据文档,要捕获的事件是ApplicationPreparedEvent

ApplicationPreparedEvent 是 SpringApplication 启动时发布的事件,并且 ApplicationContext 已完全准备好但未刷新。豆子 定义将被加载,环境已准备好使用 这个阶段。

主要方法如下所示:

public static void main(String[] args) 
        SpringApplication springApplication = new SpringApplication(MyApplication.class);
        springApplication.addListeners(new PropertiesLogger());
        springApplication.run(args);        

我已经重用了当前问题中引用的答案的代码,但是我已经对其进行了修改,因为您获得的上下文尚未刷新,并且环境的结构与应用程序启动后的结构不完全相同.我还按属性源打印了属性:一个用于系统环境,一个用于系统属性,一个用于应用程序配置属性,等等...还要注意ApplicationPreparedEvent 可以多次触发,并且属性仅在第一次打印。详情请见Spring Boot issue #8899。

package com.toto.myapp.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;

import java.util.LinkedList;
import java.util.List;

public class PropertiesLogger implements ApplicationListener<ApplicationPreparedEvent> 
  private static final Logger log = LoggerFactory.getLogger(PropertiesLogger.class);

  private ConfigurableEnvironment environment;
  private boolean isFirstRun = true;

  @Override
  public void onApplicationEvent(ApplicationPreparedEvent event) 
    if (isFirstRun) 
      environment = event.getApplicationContext().getEnvironment();
      printProperties();
    
    isFirstRun = false;
  

  public void printProperties() 
    for (EnumerablePropertySource propertySource : findPropertiesPropertySources()) 
      log.info("******* " + propertySource.getName() + " *******");
      String[] propertyNames = propertySource.getPropertyNames();
      Arrays.sort(propertyNames);
      for (String propertyName : propertyNames) 
        String resolvedProperty = environment.getProperty(propertyName);
        String sourceProperty = propertySource.getProperty(propertyName).toString();
        if(resolvedProperty.equals(sourceProperty)) 
          log.info("=", propertyName, resolvedProperty);
        else 
          log.info("= OVERRIDDEN to ", propertyName, sourceProperty, resolvedProperty);
        
      
    
  

  private List<EnumerablePropertySource> findPropertiesPropertySources() 
    List<EnumerablePropertySource> propertiesPropertySources = new LinkedList<>();
    for (PropertySource<?> propertySource : environment.getPropertySources()) 
      if (propertySource instanceof EnumerablePropertySource) 
        propertiesPropertySources.add((EnumerablePropertySource) propertySource);
      
    
    return propertiesPropertySources;
  

【讨论】:

一个重要的警告:这个书面的输出记录了来自各种来源的属性的名称,但显示的值是覆盖后的最终值。为了清楚起见,我建议用这个块替换主 log.info: 查看我之前的评论: if(environment.getProperty(propertyName).equals(propertySource.getProperty(propertyName).toString())) log.debug("=", propertyName , environment.getProperty(propertyName)); else log.debug("= OVERRIDEN to ", propertyName, propertySource.getProperty(propertyName), environment.getProperty(propertyName)); 我打算在所有属性都被覆盖后显示这些属性的值,因为这些值将被应用程序使用。 我只是觉得它令人困惑,因为它似乎显示了哪些值来自哪些文件,并且看起来我设置的值没有被正确读取(而不是被覆盖)。在我发现它显示有效值之前,我浪费了大量时间检查我的编辑。 好的,我理解这种困惑,我已将您的建议添加到答案中。感谢您的提交;)

以上是关于如何在 bean 实例化之前记录 Spring Boot 应用程序的所有活动属性?的主要内容,如果未能解决你的问题,请参考以下文章

spring加载bean实例化顺序

Spring源码分析-bean创建(循环依赖)

Spring源码分析-bean创建(循环依赖)

String注解驱动开发如何按照条件向Spring容器中注册bean?这次我懂了!!

聊聊spring之bean对象的实例化过程

聊聊spring之bean对象的实例化过程