Reference manual v1.1

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;

You may also specify more than one environment to qualify more than one target environment for your class. To do this you can use the "list" attribute as shown in the example below :

@Environment(list={"development","test"}) 
public class DevelopmentAndTestEnvironmentService implements 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.

When configured like the example above, the container will fail to load if no environment is set on start up, or a bean without a corresponding key for the current environment is found. If you want to specify a default bean (much like a class with no @Environment annotation in the annotation based approach) you can do this by adding another constructor-arg with a reference to the default bean or set the "defaultBean"property as in the example below :

<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>
  <property name="defaultBean" ref="developmentBean" />
</bean>

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

ConfigurationProvider 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 ConfigurationProvider 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 ConfigurationProvider 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.

New file

Also available in: HTML TXT