【问题标题】:How can I override Spring Boot application.properties programmatically?如何以编程方式覆盖 Spring Boot application.properties?
【发布时间】:2015-05-18 07:40:11
【问题描述】:

我有从外部配置 web 服务获取的 jdbc 属性文件 在 Spring Boot 中,为了设置 mysql 属性,很容易将它们添加到 application.properties:

spring.datasource.url=jdbc:mysql://localhost/mydb
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

如何在我的应用程序中以编程方式覆盖这些内容?

Spring-batch 道具也是如此:

database.driver=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhost/mydv
database.username=root
database.password=root

【问题讨论】:

    标签: spring spring-boot


    【解决方案1】:

    您可以在响应 ApplicationEnvironmentPrepared 事件的生命周期侦听器中添加其他属性源。

    类似的东西:

    public class DatabasePropertiesListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
      public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        Properties props = new Properties();
        props.put("spring.datasource.url", "<my value>");
        environment.getPropertySources().addFirst(new PropertiesPropertySource("myProps", props));
      }
    }
    

    然后在 src/main/resources/META-INF/spring.factories 中注册类:

    org.springframework.context.ApplicationListener=my.package.DatabasePropertiesListener
    

    这对我有用,但是,您此时可以做的事情有点有限,因为它在应用程序启动阶段相当早,您必须找到一种方法来获得您需要的值而不依赖在其他春豆等上。

    【讨论】:

    • 正是我需要的。如果它对任何人有帮助,您可以使用 'environment.getProperty("")' 从 application.properties 获取现有属性,并在您的代码中进一步使用它。我最终通过从我的属性文件中定义的整数填充 spring.hornetq.embedded.queues 属性来构建动态数量的 hornetqueus。
    • 我认为如果我侦听在运行时触发的自定义事件并从数据库中读取属性,这也可以工作?
    • 只有这个对我有用,谢谢!还要提一提的是,如果有人要通过配置中的 java 代码添加此侦听器,则需要将其添加到 SpringApplicationBuilder 中。
    【解决方案2】:

    只是为这个线程提供另一个选项以供参考,因为当我开始为我的要求寻找答案时,它在搜索列表中排名靠前,但没有涵盖我的用例。

    我希望在启动时以编程方式设置 spring boot 属性,但不需要使用 spring 支持的不同 XML/Config 文件。

    最简单的方法是在定义 SpringApplication 时设置属性。下面的基本示例将 tomcat 端口设置为 9999。

    @SpringBootApplication
    public class Demo40Application{
    
        public static void main(String[] args){
            SpringApplication application = new SpringApplication(Demo40Application.class);
    
            Properties properties = new Properties();
            properties.put("server.port", 9999);
            application.setDefaultProperties(properties);
    
            application.run(args);
        }
    }
    

    【讨论】:

    • 这不会覆盖内部属性,而是添加额外属性的好方法
    【解决方案3】:
    【解决方案4】:

    从 Spring Boot 2.0.X 开始,您可以使用自定义 ApplicationContextInitializer 和 ContextConfiguration 注释的组合来动态覆盖各个属性(例如,在单元测试中)。

    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.test.context.PortTest.RandomPortInitailizer;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.context.support.TestPropertySourceUtils;
    import org.springframework.util.SocketUtils;
    
    import static org.assertj.core.api.Assertions.assertThat;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @ContextConfiguration(initializers = RandomPortInitializer.class)
    public class PortTest {
        @Autowired
        private SomeService service;
    
        @Test
        public void testName() throws Exception {
            System.out.println(this.service);
            assertThat(this.service.toString()).containsOnlyDigits();
        }
    
        @Configuration
        static class MyConfig {
    
            @Bean
            public SomeService someService(@Value("${my.random.port}") int port) {
                return new SomeService(port);
            }
        }
    
        static class SomeService {
            private final int port;
    
            public SomeService(int port) {
                this.port = port;
            }
    
            @Override
            public String toString() {
                return String.valueOf(this.port);
            }
        }
    
        public static class RandomPortInitializer
                implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
            @Override
            public void initialize(ConfigurableApplicationContext applicationContext) {
                int randomPort = SocketUtils.findAvailableTcpPort();
                TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext,
                        "my.random.port=" + randomPort);
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      这可能很简单:

      @SpringBootApplication
      public class SampleApplication {
      
        public static void main(String[] args) {
          new SpringApplicationBuilder(SampleApplication.class)
              .properties(props())
              .build()
              .run(args);
        }
      
        private static Properties props() {
          Properties properties = new Properties();
          properties.setProperty("MY_VAR", "IT WORKS");
          return properties;
        }
      }
      

      application.yml

      test:
        prop: ${MY_VAR:default_value}
      

      【讨论】:

      • 这是一个很酷的解决方案,但是当与 application.properties 中的属性发生冲突时,它似乎不起作用。
      • 什么意思?
      【解决方案6】:

      如果您出于测试目的需要这样做:从 spring-test 5.2.5 开始,您可以使用@DynamicPropertySource

          @DynamicPropertySource
          static void setDynamicProperties(DynamicPropertyRegistry registry) {
              registry.add("some.property", () -> some.way().of(supplying).a(value) );
          }
      

      优先于几乎所有其他提供属性的方式。该方法必须是静态的。

      【讨论】:

        【解决方案7】:

        如果您正在运行 Spring Boot 应用程序,这是在启动期间设置属性的方法。

        最简单的方法是在启动应用之前设置属性。

        @SpringBootApplication
        public class Application {
        
            public static void main(String[] args) {
                SpringApplication application = new SpringApplication(Application.class);
                ConfigurableEnvironment env = new ConfigurableEnvironment();
                env.setActiveProfiles("whatever");
        
                Properties properties = new Properties();
                properties.put("server.port", 9999);
                env.getPropertySources()
                    .addFirst(new PropertiesPropertySource("initProps", properties));
        
                application.setEnvironment(env);
                application.run(args);
            }
        }
        

        【讨论】:

        • 糟糕,我的答案似乎与 Roger Thomas 提供的答案非常相似。
        • 因为 Properties 继承自 Hashtable,所以 putputAll 方法可以应用于 Properties 对象。 强烈建议不要使用它们,因为它们允许调用者插入键或值不是字符串的条目。应该改用 setProperty 方法。 docs.oracle.com/javase/7/docs/api/java/util/Properties.html
        【解决方案8】:

        在您的配置中使用此方法,您可以设置默认属性。

        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
                return application.sources(Application.class)
                      .properties("propertyKey=propertyValue");
        }
        

        【讨论】:

          【解决方案9】:

          如果需要,您可以通过这种方式以编程方式覆盖 application.properties。

          public static void main(String[] args) {
              SpringApplication app = new SpringApplication(Restdemo1Application.class);
              app.setAdditionalProfiles("dev"); 
              // overrides "application.properties" with  "application-dev.properties"
              app.run(args);
          
          }
          

          【讨论】:

            【解决方案10】:

            在 META-INF 文件夹下创建这个文件夹和文件: 弹簧>批处理>覆盖>数据源上下文.xml 并在您的 xml 文件中确保覆盖您想要的参数,如下所示:

            <bean id="dataSource"
                class="org.springframework.jdbc.datasource.DriverManagerDataSource">
                <property name="driverClassName" value="${loader.jdbc.driver}" />
                <property name="url" value="${loader.jdbc.url}" />
                <property name="username" value="${loader.jdbc.username}" />
                <property name="password" value="${loader.jdbc.password}" />
            </bean>
            
            <bean id="transactionManager"
                class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource" />
            </bean>
            

            或者在 xml 文件中使用这样的 jndi 来访问你的外部配置文件,比如 catalina.properties

            <jee:jndi-lookup id="dataSource"
                jndi-name="java:comp/env/jdbc/loader-batch-dataSource" lookup-on-startup="true"
                resource-ref="true" cache="true" />
            

            【讨论】:

              猜你喜欢
              • 2021-05-10
              • 1970-01-01
              • 2014-06-27
              • 2019-08-20
              • 2020-03-04
              • 2020-06-22
              • 2016-02-12
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多