简体   繁体   中英

Initialization order of PropertyPlaceholderConfigurers (Spring 3)

In my Spring-based web application, I need to store encrypted values in a properties file. To this end, I have subclassed the Spring "PropertyPlaceholderConfigurer" class, and overridden its "convertProperties" method so that after loading properties from a file, it decrypts those that are encrypted. This works great.

Now, this PPC depends on another bean in the Spring context that handles the encryption/decryption duties. Presently, this bean has to be configured with values "hard coded" in the Spring context XML file. I wanted to have these values pulled from the properties file via the PPC, but then doing so creates a circular dependency (the decrypter can't receive info from the PPC that needs the decrypter to do its job...).

SO... what I thought I would do is create 2 properties files: one for encrypted stuff and the other for clear text stuff. Then, I would create two PPCs - one normal, and one of my subclassed design to handle the encrypted contents. That way, I could put the decrypter's configuration options into the clear text properties file!

Unfortunately, I seem to be having a problem with the order in which items are initialized by Spring. Here's an example of my setup in 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>

So in this case, I'd like Spring to first initialize the "clearTextPlaceholder" bean. Then, using the properties that it reads in, initialize the "encrypter" bean. And finally, using that, initialize the "encryptedPlaceholder" bean for use by all other items in the context.

However, what really happens is that at startup, the "encrypter" bean gets passed a literal "${plain-text-property}" string, and THEN, both of the PPCs initialize (or try to, before it fails due to a badly configured encrypter bean).

I have tried adding the "depends-on" attribute to the relevant beans to enforce an initialization order, but to no avail. It seems like Spring wants to go ahead and create ALL of the defined PPCs at once, and since ONE of them depends on another bean, that means BOTH of them have to wait until that other bean is initialized.

Does that make sense? Is there anything I can do here (short of getting down in the weeds with context-aware stuff) to make this work, or is this just a limitation of Spring? And while I'm at it, is there a better way to go about this that I'm not seeing?

Thanks,

Doug

I reproduced your situation - and yes it works exactly as you described. Both of your PropertyPlaceHolderConfigurers are BeanPostProcessors. During startup, Spring creates all the BeanPostProcessor beans. Then it invokes them. You can change the order of invocation by setting the order property. However it always finishes the creation of all the processors - even the lowest priority - before invoking any of them.

By adding a reference of the encrypter bean to the encryptedPlaceHolder you moved encrypter into this earlier phase. encrypter is created during the BeanPostProcessor creation phase which happens before any processor can be invoked - that is, before any properties can be resolved.

As far as I can see, you cannot have a PropertyPlaceHolderConfigurer process the properties of a bean which is a dependency of another PropertyPlaceHolderConfigurer.

And while I'm at it, is there a better way to go about this that I'm not seeing?

[update]

One solution is to make encrypter smart enough to read the property file directly. A more "dependency injection friendly" way would be to create a custom factory bean. For reference see 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>

The factory bean might look like:

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

Even though you would have to create a new, special purpose factory bean, no changes are required to your existing encrypter class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM