【问题标题】:Log @ConfigurationProperties in SpringBoot prior to startup?在启动之前在SpringBoot中记录@ConfigurationProperties?
【发布时间】:2016-01-09 10:05:13
【问题描述】:

我正在尝试找到一种方法来在上下文启动之前记录我的应用程序属性(通过 @ConfigurationProperties 注入到我的 bean 中),以便在初始化所有 bean 之前查看确切的属性。

我尝试在 ApplicationEnvironmentPreparedEvent 上创建一个侦听器,但除了一次检索一个单独的属性之外,没有办法检索所有定义的属性。

有没有简单的方法来做到这一点?有什么方法可以先初始化@ConfigurationProperties 并记录其内容,或者在创建上下文之前检索所有应用程序启动属性?

【问题讨论】:

  • 我不确定我明白你想要做什么。无论哪种方式,执行器都可以提供帮助。 /env 端点列出了已加载的配置文件及其内容,/configprops 为您提供了每个 @ConfigurationProperties bean 的实际配置的概述。不知道你在追求什么,但那将是一个好的开始。
  • @StéphaneNic​​oll 我正在尝试在刷新上下文之前记录/显示我的所有配置。例如,我有 JTA bean 等在启动时自动配置,所以如果连接失败,我希望能够查看 bean 初始化之前的配置属性。这不是我们的应用程序,因此上下文路径没有帮助。此外,如果 spring 上下文无法加载(无效的 bean 配置、失败的验证等),我希望能够准确地看到使用了哪些参数。
  • @StéphaneNic​​oll 我查看了配置道具的 JMX 暴露的执行器 bean,但正如我所怀疑的那样,它从应用程序上下文中检索 bean 以显示它们的值。我的问题是我想在刷新上下文之前记录此信息;如果 bean 在实例化过程中由于不完整或不正确的配置值而失败,我想看看这些值是什么。如果我尝试使用 @PostConstruct 方法,也会出现同样的问题 - 在刷新上下文后调用它。我很惊讶没有弹簧记录器可以让我在设置这些值时查看它们。
  • 您可以创建一个问题与团队分享这个想法。

标签: java spring-boot listener


【解决方案1】:

您可以在实现 ApplicationListener 的自定义类的帮助下简单地查看应用程序属性,并将其定义为 spring-factories 条目中的启动类之一,以便它们将在应用程序加载之前执行。步骤如下:-

a) 在资源类路径中创建一个名为 spring.factories 的文件,即 src\main\resources\META-INF\spring.factories,其中包含内容 -

# Application Listeners
org.springframework.context.ApplicationListener=demo.CustomConfigListener

b) 在你的项目中创建一个自定义监听器类,就像这里的 CustomConfigListener

package demo;

import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;


public class CustomConfigListener implements ApplicationListener<ApplicationEvent> {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            for(PropertySource<?> source : ((ApplicationEnvironmentPreparedEvent) event).getEnvironment().getPropertySources()){
                if(source.getName().equals("applicationConfigurationProperties")){              
                    if (source instanceof EnumerablePropertySource) {
                        for(String name : ((EnumerablePropertySource) source).getPropertyNames()){
                            System.out.println(name+" :: "+ ((EnumerablePropertySource) source).getProperty(name));
                        }
                    }
                }
            }           
        }       
    }

}

c) 您的自定义 ConfigurationProperties 类

package demo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(ignoreUnknownFields = false, prefix = "mail")
public class MailProperties {    
    private String host;
    private int port;
    private Smtp smtp;    
    //Getters & Setters

    public static class Smtp {    
        private boolean auth;
        private boolean starttlsEnable;    
        //Getters & Setters
    }
}

d) 最后是你的 application.properties

mail.host=localhost
mail.port=25
mail.smtp.auth=false
mail.smtp.starttls-enable=false

【讨论】:

  • 从清晰的角度来看,实际实现的代码总是比任何简短的参考指南都要好
  • 谢谢!你把我错过的那块给了我。我认为它必须是ApplicationEnvironmentPreparedEvent,但无法弄清楚如何从那时起获取值。虽然我确实觉得有些属性值在枚举中重复了 2 或 3 次很奇怪,但它足以满足我的需要。尽管我会说您的第 1 步是可选的;我可以通过SpringApplication.addListener() 实例化添加监听器。
  • @LoganMzz 我读过文档;我见过听众。正如我在帖子中提到的,我已经创建了一个 Listener,但不知道如何从其中获取值。这个答案给了我我所缺少的确切部分。
  • 如果您碰巧有一个活跃的个人资料或更多。你如何只记录相关属性(即那些被覆盖的属性)?
【解决方案2】:

作为@Avis 回答的后续,我意识到 sn-p 不包含任何命令行参数等,所以我稍微更新了他的概念。我附上了我的记录器类,以防它对将来遇到这个问题的人有价值。

public class ConfigurationLogger implements ApplicationListener<ApplicationEvent> {
    // slf4j logger
    private static final Logger logger = LoggerFactory.getLogger(ConfigurationLogger.class);

    // used to sanitize any password sensitive keys (copied from Spring Boot's Sanitizer() class
    private Sanitizer sanitizer = new Sanitizer();

    // store the config keys in a sorted map
    private Map<String, Object> configurationProperties = new TreeMap<>();


    /**
     * Trigger upon all events during startup.  Both ApplicatoinEnvironmentPrepareEvent and
     * ApplicationPreparedEvent need access to the same configurationProperties object.  Could
     * have done this through separate events, both extending an abstract base class with a static
     * hash map, but not worth the effort.  Instead have the same class listen for all events, and 
     * delegate to the appropriate method.
     */
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            // store the values
            onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
        }else if( event instanceof ApplicationPreparedEvent){
            // display the values
            logConfigurationProperties( (ApplicationPreparedEvent)event);
        }
    }

    /**
     * Store the properties in the hash map for logging once all property sources have been read
     * 
     * @param event
     */
    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        for (PropertySource<?> source : event.getEnvironment().getPropertySources()) {
            if (source instanceof EnumerablePropertySource) {
                for (String key : ((EnumerablePropertySource) source).getPropertyNames()) {
                    Object value = ((EnumerablePropertySource) source).getProperty(key);
                    if (!configurationProperties.containsKey(key)) {
                        configurationProperties.put(key, sanitizer.sanitize(key, value));
                    }
                }
            }
        }
    }

    /**
     * Print all the config properties to the logger
     */
    private void logConfigurationProperties( ApplicationPreparedEvent event) {
        logger.debug("Application started with following parameters: ");
        for( Map.Entry<String, Object> entry : configurationProperties.entrySet()){
            logger.debug("{} :: {}", entry.getKey(), entry.getValue());
        }

    }
}

并且监听器在SpringApplication主类中初始化:

@SpringBootApplication
public class Application{
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setShowBanner(false);
        // add configuration properties logger
        app.addListeners(new ConfigurationLogger());
        app.run(args);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-21
    • 2017-02-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-02
    相关资源
    最近更新 更多