首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >@Value在运行时

@Value在运行时
EN

Stack Overflow用户
提问于 2015-09-30 23:54:50
回答 1查看 803关注 0票数 3

如何在运行时动态访问@Value机制?

我以为环境可能就是我要找的,但是它

代码语言:javascript
复制
@Component
public class SpringConfiguration implements ConfigurationI {
    @Autowired
    private Provider<Environment> env;

    @Override
    public String get(String key) {
        try {
            return env.get().getRequiredProperty(key);
        } catch (IllegalStateException e) {
            return null;
        }
    }
}

不幸的是,这不能访问我们的PropertyPlaceholderConfigurer bean公开的值。

编辑:为了解释我的用例:这是通过将spring特定的批注切换为JSR330 (javax.inject)批注,使包含大量spring特定部分(一堆较旧的Spring应用程序所依赖的)的库可以从较新的Guice应用程序中使用。我希望通过提供一个很好的入口点来避免在我们所有的Spring应用程序中重写所有的PropertyPlaceholderConfigurer内容。如果有其他更好的方法(也许使用@Named?)那我就洗耳恭听。

EDIT2:这是一个(经过清理的)示例,说明调用这个库的应用程序中存在什么样的PropertyPlaceholderConfigurer。

代码语言:javascript
复制
@Bean
public PropertyPlaceholderConfigurer placeholderConfigurer() {
    return new PropertyPlaceholderConfigurer() {
        @Override
        protected String resolvePlaceholder(String placeholder, Properties props) {
            // Some code to parse and cleanup key here
            String result = getPropertyFromLocalAppSpecificConfig(key);
            if (result == null) {
                result = super.resolvePlaceholder(placeholder, props);
            }
            // Some more random app specific logic for missing defaults
            return result;
        }
    };
}
EN

回答 1

Stack Overflow用户

发布于 2015-10-01 01:36:08

PropertyPlaceholder和朋友不会将属性放入您的Environment中(主要是因为向后兼容的原因)。相反,它们使用环境和它自己的内部Properties对象来解析@Value属性,这些对象通常从类路径中的属性文件中收集。因此,从PropertyPlaceholder加载的属性不能被动态获取(即没有getProperty(String..))。有些人创建定制的PropertyPlaceholder来公开存储属性(通过getter或其他方式),但我认为这完全破坏了Spring新的统一环境配置处理。

你真正想要的可能是@PropertySource,它仍然很糟糕,因为它不是动态的(因为它是一个注释,你不能改变文件的加载位置),但它会将属性加载到Environment中。我一直打算向Spring Source提交关于这方面的困惑的问题。

无论如何,您可以在这里查看我的解决方案:Manually add a @PropertySource: Configuring Environment before context is refreshed

基本上,您需要获取ConfigurableEnvironment并通过创建PropertySources将属性加载到其中。这方面的API非常强大,但不是很直观。你可以使用ApplicationContextInitializers来获取Environment,它有它自己恼人的问题(参见链接),或者你可以像我在下面做的那样。

代码语言:javascript
复制
public class ConfigResourcesEnvironment implements 
    ResourceLoaderAware, EnvironmentAware, BeanDefinitionRegistryPostProcessor, EnvironmentPropertiesMapSupplier {

    private Environment environment;
    private Map<String, String> environmentPropertiesMap;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        if (environment instanceof ConfigurableEnvironment) {
            ConfigurableEnvironment env = ((ConfigurableEnvironment) this.environment);
            List<PropertySource> propertySources;
            try {
                propertySources = loadPropertySources(); //Your custom method for propertysources
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //Spring prefers primacy ordering so we reverse the order of the sources... You may not need to do this.
            reverse(propertySources);
            for (PropertySource rp : propertySources) {
                env.getPropertySources().addLast(rp);
            }
            environmentPropertiesMap = ImmutableMap.copyOf(environmentPropertiesToMap(env));
        }
        else {
            environmentPropertiesMap = ImmutableMap.of();
        }
    }


    public static Map<String,String> environmentPropertiesToMap(ConfigurableEnvironment e) {
        Map<String, String> properties = newLinkedHashMap();
        for (String n : propertyNames(e.getPropertySources())) {
            String v = e.getProperty(n);
            if (v != null)
                properties.put(n, v);
        }
        return properties;
    }

    public static Iterable<String> propertyNames(PropertySources propertySources) {
        LinkedHashSet<String> propertyNames = new LinkedHashSet<String>();
        for (PropertySource<?> p : propertySources) {
            if (p instanceof EnumerablePropertySource) {
                EnumerablePropertySource<?> e = (EnumerablePropertySource<?>) p;
                propertyNames.addAll(asList(e.getPropertyNames()));
            }
        }
        return propertyNames;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //NOOP
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }


    public Map<String, String> getEnvironmentPropertiesMap() {
        return environmentPropertiesMap;
    }

}

一旦加载了ConfigurableEnvironment,您就可以使用EnvironmentAware接口来处理需要环境的事情,也可以创建自己的接口。

下面是一个自定义接口,您可以使用它来处理需要动态属性的东西(上面的类实现了它):

代码语言:javascript
复制
public interface EnvironmentPropertiesMapSupplier {
    public Map<String, String> getEnvironmentPropertiesMap();

}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32870590

复制
相关文章

相似问题

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