【问题标题】:What's the best way to add a new property source in Spring?在 Spring 中添加新属性源的最佳方法是什么?
【发布时间】:2016-02-16 07:38:32
【问题描述】:

我想添加一个新的属性源,可用于读取应用程序中的属性值。我想用 Spring 来做到这一点。我在@Configuration 类中有一段这样的代码:

@Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {        
    PropertySourcesPlaceholderConfigurer properties = new PropertySourcesPlaceholderConfigurer();
    MutablePropertySources sources = new MutablePropertySources();
    MyCustomPropertySource propertySource = new MyCustomPropertySource("my custom property source");
    sources.addFirst(propertySource);
    properties.setPropertySources(sources);
    return properties;
}

这似乎工作得很好。但是,它也在做的是覆盖我不想覆盖的其他属性值(例如,spring boot 使用的 application.properties 文件中的 server.port 属性)。所以基本问题是添加此属性源但不让它覆盖其他属性的最佳方法是什么。有什么方法可以获取现有的属性源并简单地添加到它?

【问题讨论】:

    标签: spring configuration


    【解决方案1】:

    我通过向我的 Spring Boot 应用程序添加自定义初始化程序来实现此功能:

    @SpringBootApplication
    public class MyApp {
    
        public static void main(String[] args) {
            new SpringApplicationBuilder(MyApp.class)
                .initializers(new MyContextInitializer())  // <---- here
                .run(args);
        }
    
    }
    

    其中MyContextInitializer 包含:-

    public class MyContextInitializer implements
        ApplicationContextInitializer<ConfigurableApplicationContext> {
    
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
    
            // Create map for properites and add first (important)
            Map<String, Object> myProperties = new HashMap<>();
            myProperties.put("some-prop", "custom-value");
            environment.getPropertySources().addFirst(
                new MapPropertySource("my-props", myProperties));
        }
    
    }
    

    注意,如果您的 application.yaml 包含:-

    some-prop: some-value
    another-prop: this is ${some-prop} property
    

    然后initialize 方法会将some-prop 更新为custom-value,并且当应用加载时,它将在运行时具有以下值:

    some-prop: custom-value
    another-prop: this is custom-value property
    

    注意,如果initialize 方法执行了简单的System.setProperty 调用,即

        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
            System.setProperty("some-prop", "custom-value");
        }
    

    ...那么another-prop 将等于this is some-value property,这不是我们通常想要的(并且我们失去了 Spring config 属性解析的能力)。

    【讨论】:

    • 对于新的资源来源,最好使用OriginTrackedMapPropertySource而不是HashMap
    • 你能解释一下为什么吗?
    【解决方案2】:

    尝试将 IgnoreUnresolvablePlaceholders 设置为 TRUE。我有一个类似的问题,我可以通过这种方式解决。就我而言,我有另一个占位符配置器,它正在工作 - 但除非我将此属性设置为 TRUE,否则不会解析第二个中的属性。

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {        
        PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
        propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
        propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(Boolean.TRUE);
        return propertySourcesPlaceholderConfigurer;
    }
    

    【讨论】:

      【解决方案3】:

      另一种可能性(经过大量实验,这对我有用)是在ApplicationContextInitializer 中声明您的PropertySource,然后将其注入您的SpringBootServletInitializer:

      public class MyPropertyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
          private static final Logger logger = LoggerFactory.getLogger(ApplicationPropertyInitializer.class);
      
          @Override
          public void initialize(ConfigurableApplicationContext applicationContext) {
              MyPropertySource ps = new MyPropertySource();
              applicationContext.getEnvironment().getPropertySources().addFirst(ps);
          }
      
      }
      
      public class MyInitializer extends SpringBootServletInitializer{
      
          @Override
          protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
              return super.configure(builder.initializers(new MyPropertyInitializer()));
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        您也许可以在初始化后将您的 propertySource 直接添加到环境中。

        编辑:因为这是在处理类之后完成的,所以您不能指望 @Value 注释从同一个 @Configuration 类中的这个特定 PropertySource 中获取任何东西 - 或任何其他之前加载过。

        @Configuration
        public class YourPropertyConfigClass{
        
            @Value("${fromCustomSource}")
            String prop; // failing - property source not yet existing
        
            @Autowired
            ConfigurableEnvironment env;
        
            @PostConstruct
            public void init() throws Exception {
                env.getPropertySources().addFirst(
                     new MyCustomPropertySource("my custom property source"));
            }
        }
        
        @Configuration
        @DependsOn("YourPropertyConfigClass")
        public class PropertyUser {
        
            @Value("${fromCustomSource}")
            String prop; // not failing
        }
        

        您可以将@PostConstruct 移动到单独的@Configuration 类并使用这些属性@DependOn("YourPropertyConfigClass") 标记其他类(这可行 - 但也许有更好的方法来强制配置顺序?)。

        无论如何 - 如果不能简单地使用 @PropertySource("file.properties") 注释添加 MyCustomPropertySource,这将是值得的 - 这将解决简单属性文件的问题。

        【讨论】:

        • 我试了一下,但似乎没有用。我刚刚用这个方法替换了我的方法,但是我的所有 @value 注释解析开始失败。我认为这是正确的轨道。
        • 您是在配置期间解析 .@Value 注释(在 @Configuration 类中) - 还是稍后完成?
        • 在配置类中。
        • 非常感谢您的编辑。我也试试看。
        【解决方案5】:

        如果您这样实现 PropertySourceFactory:

        import org.springframework.core.env.PropertySource;
        import org.springframework.core.io.support.EncodedResource;
        import org.springframework.core.io.support.PropertySourceFactory;
        
        public class CustomPropertySourceFactory implements PropertySourceFactory {
            @Override
            public PropertySource<?> createPropertySource(String name, EncodedResource resource) {
                ...
            }
        }
        

        您可以使用以下属性来源:

        @PropertySource(name="custom-prop-source", value="", factory=CustomPropertySourceFactory.class)
        

        有点hack-ish,但它有效。

        【讨论】:

          猜你喜欢
          • 2020-11-26
          • 1970-01-01
          • 2016-01-30
          • 2018-03-24
          • 1970-01-01
          • 2013-08-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多