如何对Spring Boot属性进行后处理?

huangapple go评论72阅读模式
英文:

How to post process Spring boot properties?

问题

我在主类中定义了一些外部属性,如下所示:

@PropertySources({
        @PropertySource(value = "classpath:application.properties", ignoreResourceNotFound = true),
        @PropertySource(value = "file:/opt/app/conf/database.properties", ignoreResourceNotFound = true),
        @PropertySource(value = "file:/opt/app/conf/overrides.properties", ignoreResourceNotFound = true)
})

对于其中的一些属性,我想在实际被任何Bean使用之前进行一些后处理,例如加密或丰富。其中一个这样的属性是 spring.datasource.password

我已经完成了以下步骤:

  1. 编写了一个 ApplicationContextInitializer<ConfigurableApplicationContext> 并尝试在 initialize() 方法中处理这些属性。
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        String value = environment.getProperty("spring.datasource.password");
        System.out.println("初始化器中的值为:" + value);
        System.out.println("初始化器中的数据库 URL 为:" + environment.getProperty("spring.datasource.url"));
        System.out.println("初始化器中的数据库用户名为:" + environment.getProperty("spring.datasource.username"));
    // .. 其他代码
    }
}

并将上述类包含在 META-INF/spring.factories 中,如下所示:

org.springframework.context.ApplicationContextInitializer=com.myapp.MyApplicationContextInitializer

我在打印的所有上述属性中看到 null 值,尽管 database.propertiesoverrides.properties 都存在。它们是在打印横幅之前(甚至在横幅之前)打印的第一个语句。

  1. 我尝试的另一种方法是 org.springframework.boot.env.EnvironmentPostProcessor,并在 META-INF/spring.factories 中添加:
org.springframework.boot.env.EnvironmentPostProcessor=com.myapp.PropertiesProcessor

但仍然得到相同的null值。

有趣的是,当我在执行war之前通过-Dspring.config.location="file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties"系统属性传递时,它能正常工作,即这些值被打印出来。

但我不想在运行时手动传递系统属性。有没有一种方法来实现呢?我的代码或方法有什么问题?在实际创建Bean之前是否还有其他进行后处理的方法?

英文:

I have defined some external properties in my main class as follows:

@PropertySources({
        @PropertySource(value = &quot;classpath:application.properties&quot;, ignoreResourceNotFound = true),
        @PropertySource(value = &quot;file:/opt/app/conf/database.properties&quot;, ignoreResourceNotFound = true),
        @PropertySource(value = &quot;file:/opt/app/conf/overrides.properties&quot;, ignoreResourceNotFound = true)
})

For some of the properties I would like to do some post processing, for example encrypting or enrichment before they are actually used by any beans. One such property is spring.datasource.password

I have done the following:

  1. Write an ApplicationContextInitializer&lt;ConfigurableApplicationContext&gt; and tried to process those properties in the initialize() method
public class MyApplicationContextInitializer implements ApplicationContextInitializer&lt;ConfigurableApplicationContext&gt; {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        String value = environment.getProperty(&quot;spring.datasource.password&quot;);
        System.out.println(&quot;The value in initializer is &quot; + value);
        System.out.println(&quot;The database url in initializer is &quot; + environment.getProperty(&quot;spring.datasource.url&quot;));
        System.out.println(&quot;The database username in initializer is &quot; + environment.getProperty(&quot;spring.datasource.username&quot;));
    // .. other code
    }
}

and included the above class in the META-INF/spring.factories as follows:

org.springframework.context.ApplicationContextInitializer=com.myapp.MyApplicationContextInitializer

I am seeing null values in all of the above properties that are printed though both database.properties and overrides.properties are present. They are the very first statements to be printed (even before the banner)

  1. Another approach I tried is org.springframework.boot.env.EnvironmentPostProcessor and adding in META-INF/spring.factories as
org.springframework.boot.env.EnvironmentPostProcessor=com.myapp.PropertiesProcessor

But still, I get the same null value.

> Interestingly, when I pass in the
> -Dspring.config.location=&quot;file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties&quot;
> system property before executing the war, it works i.e. the values are printed.

But I do not want to manually pass in the system property at runtime. Is there a way to do it?
What is wrong with my code or approach. Are there any other ways of doing the post-processing before actually creating the beans?

答案1

得分: 3

我通过添加具有最高优先级的属性源来解决了类似的问题。

  1. 添加 META-INF/spring.factories 文件,内容如下:
org.springframework.boot.env.EnvironmentPostProcessor=\
  com.example.SettingsPropertiesAddingPostProcessor
  1. SettingsPropertiesAddingPostProcessor 的实现:
public class SettingsPropertiesAddingPostProcessor implements EnvironmentPostProcessor {
    private static final String SETTINGS_CONFIG_PATH = "/tmp/settings.properties";

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

        File settingsFile = new File(SETTINGS_CONFIG_PATH);
        if (!settingsFile.exists()) {
            log.debug("Config file not found, skipping adding custom property source");
            return;
        }

        log.debug("Config file found, adding custom property source");
        Properties props = loadProperties(settingsFile);
        MutablePropertySources propertySources = environment.getPropertySources();
        propertySources.addFirst(new PropertiesPropertySource("settings-source", props));

    }

    private Properties loadProperties(File f) {
        FileSystemResource resource = new FileSystemResource(f);
        try {
            return PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException ex) {
            throw new IllegalStateException("Failed to load local settings from " + f.getAbsolutePath(), ex);
        }
    }
}

以上就是全部内容。

英文:

I solved similar problem by adding property source with highest precedence.

  1. Add META-INF/spring.factories with content:
org.springframework.boot.env.EnvironmentPostProcessor=
  com.example.SettingsPropertiesAddingPostProcessor

  1. SettingsPropertiesAddingPostProcessor implementation:
public class SettingsPropertiesAddingPostProcessor implements EnvironmentPostProcessor {
    private static final String SETTINGS_CONFIG_PATH = &quot;/tmp/settings.properties&quot;;

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

        File settingsFile = new File(SETTINGS_CONFIG_PATH);
        if (!settingsFile.exists()) {
            log.debug(&quot;Config file not found, skipping adding custom property source&quot;);
            return;
        }

        log.debug(&quot;Config file found, adding custom property source&quot;);
        Properties props = loadProperties(settingsFile);
        MutablePropertySources propertySources = environment.getPropertySources();
        propertySources.addFirst(new PropertiesPropertySource(&quot;settings-source&quot;, props));

    }

    private Properties loadProperties(File f) {
        FileSystemResource resource = new FileSystemResource(f);
        try {
            return PropertiesLoaderUtils.loadProperties(resource);
        } catch (IOException ex) {
            throw new IllegalStateException(&quot;Failed to load local settings from &quot; + f.getAbsolutePath(), ex);
        }
    }
}

That should be all.

答案2

得分: 1

根据 @M. Deinum 的建议,关于使用 &quot;spring.config.additional-location&quot;,我已经制定了以下解决方案:

@SpringBootApplication
@EnableSwagger2
public class MyApp extends SpringBootServletInitializer {

    public static void main(String[] args) {
        System.setProperty("spring.config.additional-location", "file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties");
        SpringApplication springApplication = new SpringApplication(MyApp.class);
        springApplication.run(args);
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        System.out.println("在 onStartup 方法中设置属性");
        System.setProperty("spring.config.additional-location", "file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties");
        super.onStartup(servletContext);
    }
}

我在 SpringBootServletInitializeronStartup() 方法中调用了 System.setProperty(),通过以上的方式进行了覆写,然后调用了父类的 onStartup() 方法。

后半部分,也就是在 onStartup 方法中设置系统属性,有助于当应用程序在像 Tomcat 这样的 Web 容器中部署时。

注意:我们还可以将属性追加spring.config.additional-location,而不仅仅是设置,以便在运行时还可以添加其他附加位置。

英文:

Following the advice of @M. Deinum, regarding using &quot;spring.config.additional-location&quot;, I have made a workaround as follows:

@SpringBootApplication
@EnableSwagger2
public class MyApp extends SpringBootServletInitializer {

    public static void main(String[] args) {
        System.setProperty(&quot;spring.config.additional-location&quot;, &quot;file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties&quot;);
        SpringApplication springApplication = new SpringApplication(MyApp.class);
        springApplication.run(args);
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        System.out.println(&quot;Setting properties onStartup&quot;);
        System.setProperty(&quot;spring.config.additional-location&quot;, &quot;file:/opt/app/conf/database.properties, file:/opt/app/conf/overrides.properties&quot;);
        super.onStartup(servletContext);
    }
}

I have called the System.setProperty() in the onStartup() method of the SpringBootServletInitializer by overriding it as above and then invoked the super class' onStartup()

The latter part i.e. setting system property in onStartup method helps when the application is deployed in a web container like Tomcat.

Note: We can also append the properties to spring.config.additional-location instead of set so that other additional locations can also be added during runtime.

huangapple
  • 本文由 发表于 2020年8月19日 20:31:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/63486977.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定