Reference manual v1.0
Enabling Spring to use environments
Environment Enabled Spring Contexts provides 2 methods of adding environment support to Spring. you may either use one of the custom application contexts, or alternatively use a standard application context and add the environment support with a bean factory post processor.
Custom application contexts
One way to enable environments is to use one of the custom ApplicationContexts provided by this project. One is tweaked for web applications, the other is a bean post processor.
Embedding Spring in java applications
ApplicationContext ctx = new
EnvironmentAwareClassPathXmlApplicationContext("no/arktekk/stagedspring/ProgramaticlyCreatedContextTest-context.xml");
Use in web applications
When creating web applications, Spring contexts are created somewhat different. The most popular way is by using a Spring ContextListener. When using this to enable environment, you just need to add a context param telling Spring to use the environment aware application context instead of the default one.
<context-param> <param-name>contextClass</param-name> <param-value>no.arktekk.stagedspring.context.EnvironmentAwareXmlWebApplicationContext</param-value> </context-param>
Using the provided bean post processor
If you do not want to create one of the custom application contexts you still can enhance existing Spring contexts by adding the following xml snippet to your Spring configuration.
<bean id="stagedApplicationContextConfigurer"
class="no.arktekk.stagedspring.factory.EnvironmentAwareApplicationContextConfigurer"/>
Note this is the recommended approach. Using the other options are more for convenience during testing. The reason for this is to avoid class loading issues, especially in conjunction with Spring OSGI
Annotation based configuration
In a environment aware Spring application context, you will have the possibility to annotate your classes with a @Environment annotation. When used on classes. Spring auto wiring support will use this as a dynamic qualifier and return the class annotated with the the environment that matches environment the DefaultEnvironmentResolver class states we are running in.
If used on a field, it wil inject the current environment into that field. This must then be of a String or enum type.
Using the @Environment annotation
@Environment(Environment.DEVELOPMENT)
public class DevelopmentEnvironmentService implements EnvironmentService {
As the example above shows, it is relatively simple to annotate your classes.
The @Environment annotation defines 3 default environments.- Environment.DEVELOPMENT
- Environment.TEST
- Environment.PRODUCTION
If you would like to use your own value, you could do that as well like this example:
@Environment("custom")
public class DevelopmentEnvironmentService implements EnvironmentService {
To get the class above injected into a collaborating class you just need to annotate the injection field, constructor or setter if you prefer with the @Autowired annotation like shown below
public class ConfigurationService {
@Autowired EnvironmentService environmentService;
Environment resolving
For the @Environment annotation to work, we need a way of telling Spring what kind of environment we are running the application in. And this is done by a strategy called EnvironmentResolver. This project allows you to create your own custom classes for resolving environments, and if no one defined, it falls back on a default implementation
The DefaultEnvironmentResolver uses simply a system property to tell Spring the current environment. and set with a java -D option, or with a environment property on the machine that is running the application.
The key of this property is : APP_ENV
Using a Java system property
java -DAPP_ENV=development MyApp
Using a system environment property
- On *nix
- export APP_ENV=development
- On Windows
- set APP_ENV=development
If the DefaultEnvironmentResolver could not find the system property, it will fall back on Environment.PRODUCTION
XML based configuration
Some times, auto wiring is not an option, you are using java 1.4 vm that does not support annotations, or you simply does not like the idea of using annotations to wire up your Spring applications. But still you may want to use environment based configuration. Then you may use the xml based configuration using the EnvironmentAwareSingletonFactoryBean provided by this project. This functionality is best described by an example
<bean id="productionBean" class="..ProductionTestBean">
<property name="value" value="production value" />
</bean>
<bean id="developmentBean" class="..DevelopmentTestBean">
<property name="value" value="development value" />
</bean>
<bean id="testBean" class="no.arktekk.stagedspring.factory.EnvironmentAwareSingletonFactoryBean">
<constructor-arg>
<map>
<entry key="production" value-ref="productionBean" />
<entry key="development" value-ref="developmentBean" />
</map>
</constructor-arg>
</bean>
you can also use constructor based injection if you prefer it.
This factory bean uses Environment.PRODUCTION as the default fall back entry if no environment is resolved. But you may override this behavior by setting the defaultPrefix property.
Environment aware properties
As we have seen with the @Environment annotation or the EnvironmentAwareSingletonFactoryBean, you can by using them choose between different implementation of Spring bean at run time. But sometimes it is not the implementation you would like to switch between but the internal configuration of a particular class.
To support this, we have the EnvironmentAwareProperties class. It is a class in where you can register multiple property files to used for looking up environment specific values for a given property key. It used environment as a prefix to separate values.
Property file layout
Each property file may have several entries for one key each prefixed by the environment it should apply. If no value found for the environment we are running in, it falls back on the key with no prefix. As shown in the following example
# # myApp properties # myKey=default value development.myKey=development value test.myKey=test value production.myKey=production value custom.myKey=custom value
You are also allowed to use keys in the values, and it will be resolved and replaced with the value of the corresponding key as shown here :
#
# myApp properties
#
baseUrl=http://localhost
development.baseUrl=http://dev.arktekk.no
test.baseUrl=http://test.arktekk.no
production.baseUrl=http://arktekk.no
customerServiceEndpoint=${baseUrl}/services/customerService
orderServiceEndpoint=${baseUrl}/services/mockCustomerService
Configuring and using environment aware properties in Java
EnvironmentAwareProperties properties = new EnvironmentAwareProperties("myProperties.properties");
String myValue = properties.getProperty("myKey");
There are also constructors taking a list of property files, or you may use the registerProperties method to register more files. The EnvironmentAwareProperties class uses Spring resource loader to load the property files so you can use all the Spring supported urls, as e.g. classpath:myProperties.properties.
Use in Spring beans
In my opinion I do like spring beans that tries to configure them selves as much as possible. I think there are way to much use of Spring configuration files to configure ordinary properties like database url's, service end points and so on. So what I would prefer is that we configure a EnvironmentAwareProperties bean in Spring, and then use the Spring auto wiring support to get it injected unto other spring beans, and then use the InitializingBean inteface or the new support for the @PostConstruct annotation In this way we only need to configure our spring beans, in the case that the default values does not work in our application. This is best explained in the 2 part example below.
Configuring the EnvironmentAwareProperties bean
First we need to configure our properties in Spring by adding this xml snippet
<bean id="stagedProperties" class="no.arktekk.stagedspring.properties.EnvironmentAwareProperties">
<property name="propertyFiles">
<list>
<value>no/arktekk/stagedspring/properties/test1.properties</value>
</list>
</property>
</bean>
Using in beans
Then it is time to get it injected, and utilized in a spring bean
public class SpringBean {
@Autowired private EnvironmentAwareProperties properties;
@PostConstruct
public void initProperties() throws Exception {
band = properties.getProperty("aCoolBand", "Mothers of invention");
}
Note by using afterPropertiesSet or @PostConstruct this will override any configuration done in Spring configuration files, and the only way to change the values is by using the property files. If you would like it to only change if configuration exists in a Spring configuration file use a null check around the retrieval of the property like :
public class SpringBean {
@Autowired private EnvironmentAwareProperties properties;
@PostConstruct
public void initProperties() throws Exception {
if (null == band){
band = properties.getProperty("aCoolBand", "Mothers of invention");
}
}
If you would rather use annotations for retrieving properties you may alternatively use the @Configuration annotation in this fashion :
public class SpringBean {
@Configuration(key="aKey") private String aPropWithoutDefault;
@Configuration(key="anotherKey", defaultValue="aDefault") private String aPropWithDefault;
@Configuration(key="cache.enabled", description="Change this to enable caching") private boolean cacheEnabled;
// your business code here
}
In order for this to work, you will need to enable a auto wiring bean post processor in your Spring configuration file and configure a staged property context as shown in this example :
<bean class="no.arktekk.stagedspring.properties.ConfigurationAnnotationBeanPostProcessor"/>
<bean id="stagedProperties" class="no.arktekk.stagedspring.properties.EnvironmentAwareProperties">
<property name="propertyFiles">
<list>
<value>myProperties.properties</value>
</list>
</property>
</bean>
Overriding properties
EnvironmentAwareProperties uses an ArrayList internally and, in the case of multiple files are registered, you are allowed to use a key in several files. when the EnvironmentAwareProperties class encounters several keys that match, it will always use the key registered last in the class. You may also register a file that does not yet exists, and it will be picked up at next restart of the application
Using environment aware properties in Spring configuration files
Those of you familiar with Spring probably know the PropertyPlaceHolderConfigurer that Spring ships with. To be able to use this kind of configuration and use environments this project provide a EnvironmentAwarePropertyPlaceHolderConfigurer class that are able to use the EnvironmentAwareProperties class to resolve the values. This functionality is best shown with an example
<bean id="properties" class="no.arktekk.stagedspring.properties.EnvironmentAwareProperties">
<property name="propertyFiles">
<list>
<value>myProperties.properties</value>
</list>
</property>
</bean>
<bean class="no.arktekk.stagedspring.properties.EnvironmentAwarePropertyPlaceholderConfigurer">
<property name="properties" ref="properties" />
</bean>
<bean id="testBean" class="..TestBean">
<property name="url" value="${aCoolUrl}" />
</bean>
Creating and configuring Custom Environment Resolvers
Implementing your own EnvironmentResolver, is as simple as implementing the EnvironmentResolver interface. it defines 2 methods
<b>getEnvironment</b> : In here you add your code that finds the running environment. by e.g. custom property files
<b>isEnvironmentDefined</b> : Here you tell the system if the environment info is available or not
All classes in this project looks in the application context looking for a bean that implements this interface, and if available will use it instead of the default implementation.