首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在bean实例化之前记录spring引导应用程序的所有活动属性?

如何在bean实例化之前记录spring引导应用程序的所有活动属性?
EN

Stack Overflow用户
提问于 2018-01-11 17:26:49
回答 2查看 14.6K关注 0票数 21

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

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-01-11 17:28:29

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

ApplicationPreparedEvent是SpringApplication启动时发布的事件,ApplicationContext已经做好充分准备,但没有刷新。bean定义将被加载,并且环境已经准备好在此阶段使用。

主要的方法如下所示:

代码语言:javascript
运行
复制
public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(MyApplication.class);
        springApplication.addListeners(new PropertiesLogger());
        springApplication.run(args);        
}

我已经重用了当前问题中引用的答案的代码,但是我修改了它,因为您得到的上下文还没有刷新,并且环境的结构与应用程序启动后的结构不完全相同。我还按属性源打印了属性:一个用于系统环境,一个用于系统属性,一个用于应用程序配置属性,等等……还请注意,可以多次触发ApplicationPreparedEvent,并且只在第一次打印属性。详情请参见春靴第8899期

代码语言:javascript
运行
复制
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;
  }
}
票数 34
EN

Stack Overflow用户

发布于 2022-02-22 22:12:42

在应用程序准备就绪之前显示属性

  • 在我的例子中,我需要在加载上下文之前显示这些属性。在调试应用程序时,我想记录所有的属性以便我知道发生了什么.

☕Kotlin实现

正如在ConfigFileApplicationListener.loadPostProcessors().中所描述的,在通过使用EnvironmentPostProcessor加载上下文之前可以收集属性,EnvironmentPostProcessor被实例化为调用EnvironmentPostProcessor的Spring工厂的一部分此时,您可以收集所有属性并以任何特定方式显示。

注意到:在此事件期间加载属性时,上下文还没有准备好。伐木工人也是。因此,可以在App之前加载这些属性(如果有的话)

  • 另外,弹簧工厂的条目必须存在,所以首先创建它。
代码语言:javascript
运行
复制
org.springframework.boot.env.EnvironmentPostProcessor=\
cash.app.PropertiesLoggerEnvironmentPostProcessor
  • 然后,创建记录器。
代码语言:javascript
运行
复制
package cash.app

import org.springframework.boot.SpringApplication
import org.springframework.boot.env.EnvironmentPostProcessor
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.core.env.EnumerablePropertySource
import java.util.*

/**
 * This is to log the properties (config and system) before the app loads. This way, we know what will be loaded
 * on the app.
 * Note that we can't use the logger because the context hasn't built yet at the time it loads the properties twice.
 *
 * As an event consumer, the method ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent is called
 * while the context is building. The process is described at https://www.baeldung.com/spring-boot-environmentpostprocessor
 * and one important aspect is that this class is an EnvironmentPostProcessor, only loaded before the App is loaded
 * with the assistance of the "src/main/resources/META-INF/spring.factories". It is loaded by the
 * ConfigFileApplicationListener.loadPostProcessors(), which looks for the list of classses in the factories.
 *
 * https://www.baeldung.com/spring-boot-environmentpostprocessor explains how to create AutoConfiguration classes for
 * shared libraries. For the case of config, the reload of properties is detailed and explained on the docs at
 * https://www.baeldung.com/spring-reloading-properties
 *
 * TODO: We need to hide the secrets, if they are defined here.
 *
 * @author Marcello.DeSales@gmail.com
 */
@Order(Ordered.LOWEST_PRECEDENCE)
class PropertiesLoggerEnvironmentPostProcessor : EnvironmentPostProcessor {

    companion object {
        /**
         * Sharing is started immediately and never stops.
         */
        private var numberOfPasses: Int = 0
        private var systemProperties: MutableMap<String, String> = mutableMapOf()
    }

    override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) {
        for (propertySource in findPropertiesPropertySources(environment)) {
            // Avoid printing the systemProperties twice
            if (propertySource.name.equals("systemProperties")) {
                numberOfPasses = numberOfPasses?.inc()

            } else {
                System.out.println("******* \" + ${propertySource.getName()} + \" *******" )
            }

            // Adaptation of https://stackoverflow.com/questions/48212761/how-to-log-all-active-properties-of-a-spring-boot-application-before-the-beans-i/48212783#48212783
            val propertyNames = propertySource.propertyNames
            Arrays.sort(propertyNames)
            for (propertyName in propertyNames) {
                val resolvedProperty = environment!!.getProperty(propertyName!!)
                val sourceProperty = propertySource.getProperty(propertyName).toString()

                if (resolvedProperty == sourceProperty) {
                    if (propertySource.name.equals("systemProperties")) {
                        systemProperties.put(propertyName, resolvedProperty)
                    } else {
                        System.out.println( "${propertyName}=${resolvedProperty}" )
                    }

                } else {
                    if (propertySource.name.equals("systemProperties")) {
                        systemProperties.put(propertyName, resolvedProperty ?: "")

                    } else {
                        System.out.println( "${propertyName}=${sourceProperty} ----- OVERRIDDEN =>>>>>> ${propertyName}=${resolvedProperty}" )
                    }
                }
            }
        }

        // The system properties show up twice in the process... The class is called twice and we only print it in the end.
        if (numberOfPasses == 2) {
            System.out.println("******* \" System Properties \" *******")
            val sysPropertyNames = systemProperties.keys.sorted()
            for (sysPropertyName in sysPropertyNames) {
                val sysPropertyValue = systemProperties!!.get(sysPropertyName!!)
                System.out.println( "${sysPropertyName}=${sysPropertyValue}" )
            }
        }
    }

    private fun findPropertiesPropertySources(environment: ConfigurableEnvironment): List<EnumerablePropertySource<*>> {
        val propertiesPropertySources: MutableList<EnumerablePropertySource<*>> = LinkedList()
        for (propertySource in environment!!.propertySources) {
            if (propertySource is EnumerablePropertySource<*>) {
                if (propertySource.name.equals("systemProperties") || propertySource.name.contains("applicationConfig:")) {
                    propertiesPropertySources.add(propertySource)
                }
            }
        }

        return propertiesPropertySources.asReversed()
    }
}

示例日志

  • 下面是我的一个服务引导过程中的记录器
代码语言:javascript
运行
复制
/Users/marcellodesales/.gradle/jdks/jdk-14.0.2+12/Contents/Home/bin/java -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always 
....
....
2022-02-22T21:24:39  INFO [app=springAppName_IS_UNDEFINED,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74720 --- [  restartedMain] o.s.b.devtools.restart.ChangeableUrls    : The Class-Path manifest attribute in /Users/marcellodesales/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-core/2.2.7s-codec-1.11.jar
2022-02-22T21:24:39  INFO [app=springAppName_IS_UNDEFINED,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74720 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
******* " + applicationConfig: [classpath:/application.yaml] + " *******

management.endpoint.health.show-details=always
management.endpoints.web.base-path=/actuator ==========>>>>>> OVERRIDDEN =========>>>>>> management.endpoints.web.base-path=/orchestrator/actuator
management.endpoints.web.exposure.include=*
management.metrics.web.server.request.autotime.enabled=true
spring.application.name=orchestrator-service
spring.boot.admin.client.enabled=false ==========>>>>>> OVERRIDDEN =========>>>>>> spring.boot.admin.client.enabled=true
spring.cloud.discovery.client.composite-indicator.enabled=false

spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
******* " + applicationConfig: [classpath:/application-ppd_dev.yaml] + " *******
spring.datasource.url=jdbc:postgresql://localhost:6433/supercash?createDatabaseIfNotExist=true ==========>>>>>> OVERRIDDEN 

=========>>>>>> spring.datasource.url=jdbc:postgresql://localhost:6433/supercash?createDatabaseIfNotExist\=true
spring.devtools.livereload.enabled=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.test-connection=true

******* " System Properties " *******
LOG_LEVEL_PATTERN=%5p [,%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]
PID=74720
com.sun.management.jmxremote=
file.encoding=UTF-8
ftion
java.vm.specification.version=14
java.vm.vendor=AdoptOpenJDK
java.vm.version=14.0.2+12
jboss.modules.system.pkgs=com.intellij.rt
jdk.debug=release
line.separator=

os.arch=x86_64
os.name=Mac OS X
os.version=10.16

user.name=marcellodesales
user.timezone=America/Los_Angeles

2022-02-22T21:25:16 DEBUG [app=orchestrator-service,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74720 --- [  restartedMain] o.s.b.c.c.ConfigFileApplicationListener  : Activated activeProfiles observed,db,ppd_dev

   _____                        _____          _     
  / ____|                      / ____|        | |    
 | (___  _   _ _ __   ___ _ __| |     __ _ ___| |__  


2022-02-22T20:41:08  INFO [app=orchestrator-service,prof=observed,db,ppd_dev][tid=,sid=,sxp=][uid=] 74181 --- [  restartedMain] 
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48212761

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档