Lesser Known Spring Features: Automatic Type Conversion
In this first part of the Lesser Known Spring Features series I am going to show you, how Spring’s support for automatic type conversion can be leveraged both for improved readability and safety, when it comes to configuration management of Spring-based applications.
Why one should care about application configuration
In times of DevOps an increased release frequency of (enterprise) software applications is more likely to become our daily business. But even with the upcoming *aaS cloud services and highly automated deployment strategies, there is always a need for software configuration. Starting from the proper binding of JDBC data sources via JNDI to the configuration of individual business functionality using application properties, there are always parts that have not been built statically, but require individual configuration based on things, such as the application stage, size, or customer preferences. Accordingly, it is important that these application properties are carefully defined using meaningful names and descriptions, so that administrators or deployers know how to deal with them.
The Spring framework, then, provides excellent abstractions for managing application properties like the property placeholder configurer or the environment abstraction with its property sources. It is no longer up to the developer to manually load property files or to know, which properties are relevant for the application code in question. Rather, the principle of Inversion of Control is the key to supporting loose coupling between application code and configuration. Hence, the application developer can focus on implementing business logic, while the application context is responsible for providing the correct values, i.e. through bean setters.
Remark: This is one of the reasons, why I prefer XML over Java configuration, at least for production code (not integration tests). Configuration via annotations inevitably ties application code to property keys. This makes it harder to refactor them, e.g. when there is the demand to split or combine different properties accordingly. Furthermore, a certain property may have different meanings to different parts of the code, so why should there be used a common property name rather than a meaningful setter method? In the latter case, the only binding between properties and application code is the XML configuration, which is much cleaner design.
Introducing Spring’s automatic type conversion
The afore mentioned technologies are usually accompanied by the automatic type conversion mechanisms, which are built into Spring’s application context. Out of the box, these include conversions for all kinds of primitive types, as well as some well-known Java classes, e.g. java.lang.String or even java.lang.Class. These enable the application developer to use meaningful types for setters, while the source properties are all strings, in the first place.
What is not commonly known, is that Spring also provides support for extending the so-called conversion service, introducing new types and converter implementations. This can be extremely helpful when you want to combine property values with meaningful units, e.g. 5m for five minutes. Moreover, this relieves the application deployer from converting these values to the appropriate target type, e.g. milliseconds or seconds, leave aside the fact that a developer decides to change its type in the future. A suitable Java class for representing time-based values throughout the application may look as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
package examples.spring; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.springframework.util.StringUtils; /** * Type-safe time representation. */ public final class Duration { private static final Map<String, TimeUnit> UNITS = new HashMap<String, TimeUnit> () { { put ( "ms", TimeUnit.MILLISECONDS ); put ( "s", TimeUnit.SECONDS ); put ( "m", TimeUnit.MINUTES ); put ( "h", TimeUnit.HOURS ); put ( "d", TimeUnit.DAYS ); } }; private static final Pattern PARSE_PATTERN = Pattern.compile ( "([0-9]+)(ms|s|m|h|d)" ); private final long value; private final TimeUnit unit; private Duration ( long value, TimeUnit unit ) { this.value = value; this.unit = unit; } /** * Yields the contained duration in milliseconds. * * @return the number of milliseconds represented by this. */ public long toMilliSeconds () { return this.unit.toMillis ( this.value ); } /** * Yields the contained duration in seconds. * * @return the number of seconds represented by this. */ public long toSeconds () { return this.unit.toSeconds ( this.value ); } /** * Factory method constructing an instance from a string. * * @param s the input string representing the duration * * @return a {@link Duration} instance. * * @throws NumberFormatException in case the string does not represent a valid duration */ public static Duration valueOf ( String s ) throws NumberFormatException { if ( !StringUtils.hasText ( s ) ) { throw new NumberFormatException ( "Null or empty string cannot be converted to duration." ); } Matcher m = PARSE_PATTERN.matcher ( s.trim () ); if ( !m.matches () ) { throw new NumberFormatException ( "Invalid duration format: " + s ); } Long value = Long.valueOf ( m.group ( 1 ) ); String unit = m.group ( 2 ); return new Duration ( value, UNITS.get ( unit ) ); } } |
Instances of this class can be constructed from strings containing values accompanied by a valid time unit, which are parsed using a regular expression. However, throughout the application it will mostly be used to retrieve values in different time units, as needed. For Spring to know, how to deal with this new type, a converter class has to be added as well, as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package examples.spring; import org.springframework.core.convert.converter.Converter; /** * {@link Converter} implementation from string to {@link Duration}. * * @see Duration#valueOf(String) */ public class StringToDurationConverter implements Converter<String, Duration> { @Override public Duration convert ( String source ) { return Duration.valueOf ( source ); } } |
The converter is finally registered with the conversion service, whose bean name is conversionService, as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="examples.spring.StringToDurationConverter"/> </list> </property> </bean> </beans> |
As you can see, the ConversionServiceFactoryBean allow multiple converters to be registered within the application context.
Any Spring bean within the application context is now capable of consuming instances of type Duration, e.g. via its setter methods, storing the reference or converting its value to the desired type or unit, as desired. However, the source values injected into the bean must match the duration format, as represented by the regular expression, no matter if they are hard-coded within the XML or inserted via a property placeholder. The application deployer, on the other hand, can/must choose from a range of suitable time units, when specifying the appropriate value.
Dealing with 3rd party code
While this also represents a crucial improvement with respect to object oriented design and improved type safety, it is not applicable to 3rd party code, however. In the end, there is no such common type as duration within Java. So when it comes to injecting meaningful property values into beans instantiated solely from 3rd party code, including Spring’s very own ones, Spring’s Expression Language has to bridge the gap, e.g. as follows:
1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="someThirdPartyService" class="any.Bean"> <property name="maxInterval" value="#{T(examples.spring.Duration).valueOf('${max.service.interval}').toMilliSeconds()}"/> </bean> </beans> |
Of course, this is is not safe with respect to refactoring. If the internally used time unit is changing over time, the XML configuration has to be adapted manually.
Summary
As shown, Spring’s automatic type conversion can be extended in order to improve the overall readability and type safety of configuration values, and makes it easier for application deployers to apply proper values. Nevertheless, there are some things that should be considered:
- The introduction of units comes at the cost of rounding errors, if the granularity of the target value is higher than the value supplied via the context. Since, most implementation classes, however, stick to fine-grained values, typically milli- or even nanoseconds, this is usually a rare situation. On the other hand, the application deployer is free to supply a meaningful value, such as 5m, without converting it to milliseconds on its own.
- The introduction of typed classes comes at the cost of altering the affected setter methods throughout the application code. Hence, it should be done early in development, so developers get used to these new types. Even if this is not the case, however, Spring’s Expression Language may still be used to bridge the gap here.
I hope the capabilities of Spring’s type conversion, described in this post, will help you in applying type-safe configurations in the future.
Recent posts






Comment article