The methodology of "Twelve Factor Applications" - built to be innately deployable, scaleable and maintainable in Cloud environments, works well with the Spring Cloud Framework.
A full description of the "Twelve Factor Application" methodology can be found here: https://12factor.net/. Twelve Factor applications keep configuration separate from code and are strictly separated into build, release and run stages. Another of their key feature's is dev/prod parity - any local deploys of the application should be as similar to the production environment as possible, thereby facilitating continuous deployment.
Twelve factor apps should be robust to crashes and they should be scaleable. Stateless applications are easier to scale horizontally. Robustness to crashes is aided by using a 'Crash Only' design (https://lwn.net/Articles/191059/ ) - essentially there should be no special 'clean-up' stages that assume the application always has a clean shutdown. Obviously, important data should not be held in memory on a single instance of the application. Therefore avoid using 'sticky sessions' in web applications and any data that is stored in session state (instead of in a database) should use cache replication throughout the cluster.
One way to externalise configuration, recommended by the Twelve Factor Application manifesto, is to use OS environmental variables. An obvious downside is the need to maintain these on each machine where the application runs.
Older versions of the Spring Framework have provided externalisation of configuration in the form of the PropertyPlaceholderConfigurer
bean which will read properties files specified by a locations
attribute. Since Spring 3.1, the PropertySourcesPlaceholderConfigurer
bean is intended to replace PropertyPlaceholderConfigurer
; this introduces the concept of an Environment bean which not only loads property files but also external environmental variables - such as the OS value for line.separator
, for example. Properties loaded in this way can be directly assigned to fields in Spring Beans using the @Value annotation:
@Value("${name}")
private String name;
With the introduction of Spring Boot (see https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html), some further improvements to this include convention over configuration: an 'application.properties' file on the classpath is now used by default as a source of configuration properties. An 'application.properties' file external to the bundled application will take precedence over an internal file. Lastly, if the application is run with a specific Spring profile (for example 'dev') then Spring Boot will look for a file called application-dev.properties
. Note, however, that extensive use of Spring profiles to activate different sets of beans and properties in 'dev' and 'prod' is a violation of the Twelve Factor methodology.
Spring Boot also introduces type safety to property configuration - a class annotated with @ConfigurationProperties
will have all of its Java Bean style properties mapped to values (and validated using JSR-303 annotations) from whatever PropertySources have been configured. Boot also normalises properties configured as environmental variables with properties from a property file or as Java System properties. Using this 'relaxed binding', '$DB_NAME
', '-Ddb.name
' or a property file containing 'db.name
' as a key will all be interpreted by Boot as a single property key called 'db.name
'. A bean with a field dbName
that was annotated with @ConfigurationProperties
would have a value directly assigned to that field without any need for any @Value
annotation on the field. (Note also that the normalisation feature does not work with @Value annotations). External configuration always supersedes internal so the environmental variable value of '$DB_NAME' would be used in this case. See https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html for the full details of Spring Boot property config.
The Spring Cloud project provides a framework for building distributed systems, see: http://projects.spring.io/spring-cloud/spring-cloud.html. One of the sub projects of Spring Cloud is Spring Cloud Config : https://cloud.spring.io/spring-cloud-config/.
If you are new to Spring Boot, you might want to get started in creating a project using the Spring Initializr projects mechanism: http://start.spring.io/. Type 'config server' into the 'search for dependencies input and then click on the 'generate project' button.
This will generate a Spring Boot project that will serve properties to be used in the configuration of other applications. An alternative approach is to simply add the @EnableConfigServer
annotation to an @SpringBootApplication
annotated class and also explicitlty add the config-server dependencies to your pom file.
The Configuration Server accesses properties (by default) stored in GitHub then serves them as Json files. Therefore we now can access a full history of the development of the property files. This is all the more useful as these files often end up being added to .gitignore when the property files are stored inside applications to prevent sensitive passwords etc being leaked.
Configuring the Configuration server itself is a simple as specifying the Git location it will find property files, its application name and the port it should serve on.
Spring Boot projects that declare a dependency on spring-cloud-config-client and on Spring Boot Actuator will automatically access the Configuration Server. The Maven/Boot way of doing this can simply declare a dependency:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
This is a Spring Boot Starter dependency - for those unfamiliar with this concept it is an 'opinionated' dependency that will introduce a a set of related dependencies with versions controlled by the Spring Boot developers. See http://docs.spring.io/spring-boot/docs/1.4.3.RELEASE/reference/htmlsingle/#using-boot-starter for full details.
Having declared the above dependency, client projects will be injected with an Environment object that will contain properties served by the Config Server. This happens transparently as far as clients are concerned. Spring-Cloud clients must use a file called bootstrap.properties
; all this need contain is the
http://<server>:<port>/<application-name>
All clients of the config-server will receive the values stored in Git in an global 'application.properties' file. Since all requests to the Configuration Server must include the
Traditionally, the only way for a running Java application to reload property values is to restart that application. With Spring Cloud live reload is possible. To achieve this two changes are required: beans which will consume values provided by the Configuration server need to be annotated with @RefreshScope
and the Spring-Boot-Actuator starter project needs to be added to the service as a dependency. Spring Cloud includes a full RabbitMQ
event-bus for propagating changes to multiple services, however, a simple post request to the service itself on the /refresh
endpoint will now result in all @RefreshScope
annotated beans reading their values anew from the Configuration Server.
Storing property values in remote Github locations potentially opens up a route to hacking; therefore sensitive information should be encrypted. Values prefixed with {cipher}
are decrypted before being served by the Configuration Server. Spring Cloud Configuration server also exposes /encrypt and /decrypt endpoints that may be used for editing encrypted property files.