java - @运行时的值

标签 java spring

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

我认为环境可能就是我正在寻找的东西,但它

@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 特定部分(一堆旧 Spring 应用程序所依赖)的库的一部分,可以通过切换 JSR 330 的 Spring 特定注释来从较新的 Guice 应用程序中使用( javax.inject)的。我希望通过提供这样一个很好的入口点,避免重写所有 Spring 应用程序中的所有 PropertyPlaceholderConfigurer 内容。如果有另一种更好的方法来做到这一点(也许使用@Named?)那么我洗耳恭听。

EDIT2:这是调用此库的应用程序中存在哪种 PropertyPlaceholderConfigurer 的(清理后的)示例。

@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;
        }
    };
}

最佳答案

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

您真正想要的可能是 @PropertySource ,它仍然非常糟糕,因为它不是动态的(因为它是一个注释,您无法更改文件的加载位置),但它会将属性加载到环境。我一直想向 Spring Source 提出关于这种困惑的问题。

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

基本上,您需要获取 ConfigurableEnvironment 并通过创建 PropertySources 将属性加载到其中。该 API 非常强大,但不是很直观。您可以使用 ApplicationContextInitializers 来获取 Environment,它有自己烦人的问题(请参阅链接),或者您可以执行我下面所做的操作。

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接口(interface)来处理需要环境的事物或创建您自己的接口(interface)。

这是一个自定义接口(interface),您可以将其用于需要动态属性的事物(上面的类实现了它):

public interface EnvironmentPropertiesMapSupplier {
    public Map<String, String> getEnvironmentPropertiesMap();

}

关于java - @运行时的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32870590/

相关文章:

java - Jasperreports-6.3.0 Webapp示例编译抛出异常

java - 在 Java 中完全卸载 JDBC 驱动程序

java - Spring 启动: order of instantiation

java - 如何在 fragment 的方法中膨胀布局

java - 使用命名空间解码 XML 响应

java - @MatrixVariable Spring 3.2 返回 Null

java - 记录直接从数据库中获取,而不是从缓存文件中获取

spring - Intellij IDEA 解析自定义 bean - 找不到命名空间的自定义处理程序

java - 如何拦截 spring REST Controller 中的所有请求?

java - 无法使用 graphql-spring 初始化代理