Spring:如何替换在Application Context中创建的Environment bean

时间:2014-05-13 12:39:01

标签: java spring

我希望用我自己的实现替换Spring使用的Environment bean。这是不好的做法,如果没有,我怎么能干净利落地做到这一点?目前我已经创建了一个实现Environment接口并使用现有Environment bean的bean,但这意味着需要Environment bean的所有配置代码现在必须使用我的自定义Environment bean。我认为用我自己的方式替换Springs Environment bean会更干净,然后没有需要改变的配置。目前我能想到的唯一方法是创建我自己的ApplicationContext,从而将环境设置为我自己的环境,或者创建一些ApplicationContextAware并在那里设置环境。这两件事对我来说似乎都有些蠢。

约束:

  • 我使用的是最新版本的Spring3。
  • 我正在使用基于Java的配置;不是XML

谢谢。

编辑:背景

我想我应该解释为什么我想这样做,以防我的想法有缺陷。我避免这种情况是为了避免非建设性的问题,为什么你要这么做?"响应。

Spring Environment bean在查找属性值时使用一组属性源。典型的堆栈看起来像这样(但不限于):

  • JNDI
  • 系统属性(通过-Dmyprop = foo设置)
  • 环境变量
  • ...

出于安全原因,有必要加密其中一些属性(例如数据库密码)。解决方案是使用Jasypt进行属性加密。但是,Spring / Jasypt只提供了一种将新属性源插入环境的方法。所以:

  • 具有潜在加密值的属性文件
  • JNDI
  • 系统属性(通过-Dmyprop = foo设置)
  • 环境变量
  • ...

然而,这并不理想,因为这意味着属性只能存储在单个文件中以供操作组维护,或者属性将分布在属性文件,环境变量等中。此外,我觉得无论属性来源如何,这些属性都有可能被加密。

所以这让我想到,无论我在哪里尝试从环境中访问它们,我都需要解密代码中的属性,或者我需要创建自己的环境bean来为我做这个。

我欢迎听到建设性的评论和替代方案。

编辑:根据M. Deinum的答案添加解决方案

public class EnvironmentBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    private static final String CONFIGURATION_PROPERTY_PBE_ALGORITHM = "PBE_ALGORITHM";
    private static final String CONFIGURATION_PROPERTY_PBE_PASSWORD = "PBE_PASSWORD";

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        StandardEnvironment environment = (StandardEnvironment) beanFactory.getBean("environment");

        if (environment != null) {
            StringEncryptor encryptor = this.getEncryptor(environment);

            MutablePropertySources mutablePropertySources = environment.getPropertySources();

            for (PropertySource<?> propertySource : mutablePropertySources) {
                mutablePropertySources.replace(
                        propertySource.getName(), 
                        new EncryptablePropertySourcePropertySource(propertySource.getName(), propertySource, encryptor));
            }
        }
    }

    private StringEncryptor getEncryptor(Environment environment) {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();

        String algorithm = environment.getProperty(CONFIGURATION_PROPERTY_PBE_ALGORITHM);
        if (algorithm != null) {
            encryptor.setAlgorithm(algorithm);
        }

        String password = environment.getProperty(CONFIGURATION_PROPERTY_PBE_PASSWORD);
        if (password != null) {
            encryptor.setPassword(password);
        }

        return encryptor;
    }

    private class EncryptablePropertySourcePropertySource extends PropertySource<PropertySource<?>> {

        private StringEncryptor stringEncryptor;

        private TextEncryptor textEncryptor;

        public EncryptablePropertySourcePropertySource(final String name, final PropertySource<?> propertySource, final StringEncryptor encryptor) {
            super(name, propertySource);

            this.stringEncryptor = encryptor;
        }

        public EncryptablePropertySourcePropertySource(final String name, final PropertySource<?> propertySource, final TextEncryptor encryptor) {
            super(name, propertySource);

            this.textEncryptor = encryptor;
        }

        @Override
        public Object getProperty(String name) {
            Object value = this.source.getProperty(name);

            if (value != null && value instanceof String) {
                value = this.decode((String) value);
            }

            return value;
        }

        private String decode(String encodedValue) {
            if (!PropertyValueEncryptionUtils.isEncryptedValue(encodedValue)) {
                return encodedValue;
            }

            if (this.stringEncryptor != null) {
                return PropertyValueEncryptionUtils.decrypt(encodedValue, this.stringEncryptor);

            }

            if (this.textEncryptor != null) {
                return PropertyValueEncryptionUtils.decrypt(encodedValue, this.textEncryptor);
            }

            throw new EncryptionOperationNotPossibleException(
                    "Neither a string encryptor nor a text encryptor exist "
                    + "for this instance of EncryptableProperties. This is usually " 
                    + "caused by the instance having been serialized and then "
                    + "de-serialized in a different classloader or virtual machine, " 
                    + "which is an unsupported behaviour (as encryptors cannot be "
                    + "serialized themselves)");
        }

    }
}

1 个答案:

答案 0 :(得分:2)

  

但是,Spring / Jasypt只提供了一种将新属性源插入环境的方法。

实际上,这是你错的地方,你也可以替换PropertySources。请参阅javadocMutablePropertySources。这也是Spring在内部使用,首先添加一些虚拟PropertySources并在以后替换它们。

您可以做的是创建PropertySource,委托给另一个PropertySource并动态解密该值。这样你可以将所有PropertySources替换为包裹原始文件的MutablePropertySources mps = env.getgetPropertySources(); for (PropertySource ps : env.getgetPropertySources()) { EncryptablePropertySource eps = new EncryptablePropertySource(ps.getName(), ps, encryptor); mps.replace(ps.getName(), eps); }

Properties

您可以使用EncryptablePropertiesPropertySource作为示例,您基本上需要做的是将PropertySource替换为EncryptedPropertySourcesPlaceholderConfigurer

如果SPR-8928被修复会更容易,因为只允许注册{{1}}并且所有属性都会被转换,你不会需要这些黑客。