【问题标题】:Initialization order of PropertyPlaceholderConfigurers (Spring 3)PropertyPlaceholderConfigurers 的初始化顺序(Spring 3)
【发布时间】:2012-12-14 00:41:54
【问题描述】:

在我基于 Spring 的 Web 应用程序中,我需要将加密值存储在属性文件中。为此,我对 Spring“PropertyPlaceholderConfigurer”类进行了子类化,并重写了它的“convertProperties”方法,以便在从文件加载属性后,它会解密那些被加密的。这很好用。

现在,这个 PPC 依赖于 Spring 上下文中处理加密/解密任务的另一个 bean。目前,这个 bean 必须在 Spring 上下文 XML 文件中使用“硬编码”的值进行配置。我想通过 PPC 从属性文件中提取这些值,但这样做会产生循环依赖(解密器无法从 PPC 接收需要解密器完成其工作的信息......)。

所以...我想我会创建 2 个属性文件:一个用于加密内容,另一个用于明文内容。然后,我会创建两个 PPC——一个是普通的,一个是我的子类设计来处理加密的内容。这样,我可以将解密器的配置选项放入明文属性文件中!

不幸的是,我似乎对 Spring 初始化项目的顺序有问题。这是我在 XML 中设置的示例:

<bean id="clearTextPlaceholder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreUnresolvablePlaceholders" value="true" />
    <property name="location" value="clear.properties" />
</bean>

<bean id="encryptedPlaceholder" class="com.mycompany.EncryptedPlaceholderConfigurer">
    <property name="ignoreUnresolvablePlaceholders" value="true" />
    <property name="location" value="encrypted.properties" />
    <property name="encryption" ref="encrypter" />
</bean>

<bean id="encrypter" class="com.mycompany.Encrypter">
    <property name="someOption" value="${plain-text-property}" />
</bean>

所以在这种情况下,我希望 Spring 首先初始化“clearTextPlaceholder”bean。然后,使用它读取的属性,初始化“加密器”bean。最后,使用它初始化“encryptedPlaceholder”bean,以供上下文中的所有其他项目使用。

然而,真正发生的是,在启动时,“加密器”bean 被传递了一个文字“${plain-text-property}”字符串,然后,两个 PPC 都初始化(或尝试,在它失败之前)由于加密器 bean 配置错误)。

我尝试将“depends-on”属性添加到相关 bean 以强制执行初始化顺序,但无济于事。似乎 Spring 想要立即创建所有已定义的 PPC,并且由于其中一个依赖于另一个 bean,这意味着它们都必须等到另一个 bean 被初始化。

这有意义吗?有什么我可以在这里做的(除了使用上下文感知的东西来解决问题)来完成这项工作,还是这只是 Spring 的限制?当我在做的时候,有没有更好的方法来解决这个问题?

谢谢,

道格

【问题讨论】:

    标签: java spring properties


    【解决方案1】:

    我复制了您的情况 - 是的,它完全按照您的描述工作。您的两个 PropertyPlaceHolderConfigurer 都是 BeanPostProcessor。在启动期间,Spring 创建所有 BeanPostProcessor bean。然后它调用它们。您可以通过设置order 属性来更改调用顺序。然而,它总是在调用任何处理器之前完成所有处理器的创建——即使是最低优先级。

    通过将encrypter bean 的引用添加到encryptedPlaceHolder,您将encrypter 移动到了这个早期阶段。 encrypter 是在 BeanPostProcessor 创建阶段创建的,该阶段发生在可以调用任何处理器之前 - 即在可以解析任何属性之前。

    据我所知,您不能让 PropertyPlaceHolderConfigurer 处理作为另一个 PropertyPlaceHolderConfigurer 依赖项的 bean 的属性。

    当我在做的时候,有没有更好的方法来解决这个问题?

    [更新]

    一种解决方案是让encrypter 足够聪明,可以直接读取属性文件。一种更“依赖注入友好”的方式是创建一个自定义工厂 bean。参考见http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-factory-class-instance-factory-method

    <bean id="encrypter" factory-bean="encrypterFactory" factory-method="createInstance">
    </bean>
    
    <bean id="encrypterFactory" class="com.mycompany.EncrypterFactory" init-method="init">
       <property name="location" value="clear.properties"/>
    </bean>
    

    工厂 bean 可能看起来像:

    public class EncrypterFactory
    {
       Properties properties;
       File file;
    
       public void setLocation(String fileName)
       {
          this.file = new File(fileName);
       }
       public void init() throws IOException
       {
          properties = new Properties();
          properties.load(new FileReader(file));
       }
    
       public Encrypter createInstance()
       {
          Encrypter encrypter = new Encrypter();
          encrypter.setSomeOption(properties.getProperty("plain-text-property"));
          return encrypter;
       }
    }
    

    即使您必须创建一个新的、特殊用途的工厂 bean,也不需要对现有的加密器类进行任何更改。

    【讨论】:

    • 好的,考虑到结果,我认为这一定是发生了什么。我只是希望我错了!我会像你说的那样让“加密器”bean 足够聪明来处理属性,除了它目前是一个用于许多其他事情的愚蠢的加密/解密管道,我宁愿不那么专门化它。虽然......如果我找不到更好的方法,我可能最终会这样做。
    猜你喜欢
    • 2010-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-23
    • 2018-07-25
    • 1970-01-01
    • 2018-06-20
    • 1970-01-01
    相关资源
    最近更新 更多