One of the improvements made to the Spring Framework in the 3.1 release is in the area of property placeholders. Chris Beams explains how the new unified property management system works in a blog posting on the SpringSource community site.
Several implementations of the new PropertySource interface are provided with the framework. PropertySource implementations backed by Java system properties and operating system environment variables are registered by default. If you specify one or more properties files using the <context:property-placeholder> tag in an XML configuration file or using the @PropertySource annotation on a Java config @Configuration class, the framework registers a PropertySource implementation backed by the specified files.
As Chris described, you can also register your own custom PropertySource implementations. This would be useful if you need bean properties to be stored in an external data source so they can be controlled outside of the application deployment process, or if you need properties to be encrypted or encoded for security reasons.
I decided to develop a custom PropertySource in a sample application. This PropertySource has some non-trivial bootstrapping of its own that needs to be done before it can be used, and Spring beans and dependency injection are used to bootstrap the PropertySource. The custom PropertySource must be instantiated, configured, and registered into to the application context containing the property placeholders before the application context is refreshed to make sure the property placeholders are resolved against the custom PropertySource.
To test this, I created a trivial bean with a few String properties:
and created a Spring XML config file named "app-context.xml" to instantiate the bean and set the properties:
When the GreetingService bean is instantiated, the Spring container will resolve the ${greeting.hello} and ${greeting.welcome} property placeholders and replace them with any specified text.
I then created a very simple Java main to bootstrap a Spring application context and show the properties injected into the bean:
With all this in place, I can run the GreetingApp class and play with setting the ${greeting.hello} and ${greeting.welcome} properties in environment variables, Java system properties, and properties files.
Next comes the custom PropertySource. I decided to use Redis as a backing store for configurable properties. Since Redis is a key-value store, it is a very natural fit for this example. I also decided to use Spring Data Redis to access the Redis data store. With Spring Data Redis, the code for the custom PropertySource was very simple. The PropertySource interface expects implementations to delegate to a "source" object to encapsulate the property values, so I created a RedisPropertySource which delegates to a RedisRepository. The RedisRepository in turn uses one of the RedisTemplate classes provided by Spring Data Redis.
Here is the code for the RedisPropertySource:
and the code for the RedisRepository:
Since the PropertySource bean and all related beans must be instantiated and wired together before the application beans can be instantiated, the PropertySource configuration goes in its own Spring XML config file named property-source-context.xml:
The application bootstrapping code is changed to instantiate the PropertySource bean and register it with the application's context:
The important thing to notice in this second version of the GreetingAppXmlConfig class is in the createAppContext() method. A second parameter is passed to the ClassPathXmlApplicationContext constructor to prevent the application context from being "refreshed" when it is created. This means the XML config file will be read and validated, but the beans defined in the context will not be created yet. The custom PropertySource is then registered with the application context, and the context is manually refreshed.
Of course, a Redis server has to be running and accessible before the GreetingApp main is run. The Redis command line client can be used to set and clear "greeting.hello" and "greeting.welcome" keys in the Redis server to test the RedisPropertySource. This example assumes the Redis server is running on "localhost" on the default port, but those values can be changed by setting properties of the JedisConnectionFactory bean in the property-source-context.xml file.
In the end, the code to make a custom RedisPropertySource work is very simple. The configuration and bootstrapping is also simple once the right sequence of events is understood.
I also worked on registering a custom PropertySource in a JUnit test case, which also took some figuring to get it right. Since this post has gotten pretty long, I'm going to continue that in a Part 2 post.
All the code listed here is available in a runnable project in GitHub. The GitHub project also shows all the configuration using Java config in addition to the XML config shown here.
This comment has been removed by the author.
ReplyDeleteHi,
ReplyDeleteThanks for this worked example! I used it to implement a PropertySource that delegates to an Apache Commons Configuration class. However I found I wasn't able to get it to work without including <context:property-placeholder /> in my main app-context.xml, but I notice you haven't done this. Without it, it would never call into my PropertySource. This isn't present in your XML - were you definitely able to make it work without this?
Hi,
ReplyDeleteGreat write up! I'm trying to load properties on application start-up from an Oracle DB. I currently have Hibernate implemented and it would be nice to load these properties via Hibernate. If I load my Hibernate context once during application startup will the beans (data source/hibernate session) be available to the rest of the application? Also, any pointers on needed environment based properties inside of the Hibernate context?
Thanks,
Jonathan
Nice write up thanks! Is it not possible to add the PropertySource via XML declaration? Is programmatic addition the only inroad?
ReplyDelete