Using Spring 3.0 Custom Type Converter


The Trouble with Strings

Data entering or leaving our application is frequently in string format (i.e. XML). As a developer, I would like a way to register custom data type converters for my business entities. Custom business data types might include social security number or US phone number.

Most data types that our applications use are String based when coming into/or leaving the application. For example, data we configure in XML documents, Web Service messages, User Interface collection and UI components are String types.



NEW Spring 3 Features

Validators

Spring 3 includes full JSR-303 Bean Validation support. This provides us with the ability to code or annotate our classes providing custom validation for our bean fields. In addition, Spring’s DataBinder can validate as well as bind our objects. To configure the validator, you can wire into your XML the following:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>



Or, Spring will load this automatically with (Roo configures this for you):

<mvc:annotation-driven/>

To add our own validator implementations see the Spring 3 Validation documentation. There we see how to implement the Java standard javax.validation.ConstraintValidator to write custom class and/or annotations for our specific needs.

Converters

Converters allow us to replace the old PropertyEditor support. Converters allow us to write classes that can convert to & from any types. This can prove very useful when you want to convert our Repository objects to/from our DTOs for example. This is a very power addition to Spring but is not the focus of this article. See Spring 3 Reference documentation for creating and registering converters.

Formatters

Formatters give us the common use-case of converting data to/from Strings. This article focuses on using Formatters.



Automatic Formatters

By default, Spring registers the common data types that we use on a daily basis, allowing us to convert data types to/from string. These common formatters (called “editors” prior to Spring 3) handle data types such as Numbers, Date, Property, Collection, File, Boolean, URL, etc.

How Does Formatting Work?

When we configure property injection in our XML files, we simply put in data in quotes. When Spring is injecting the value into the bean, Spring uses reflection to determine the recipient data type. Spring then looks up the data type in the registered converters & formatters and invokes the appropriate class to unmarshall it from String to the required data type.

If we have a service method setMaximumCount(Integer count) { .... }

We can configure that in XML as:

 <bean id="myService" class="...MyService">
     <property name="maximumCount" value="100" />
 </bean>

In the above example, Spring goes through the following steps:

  1. String value configured in XML file for property
  2. Spring uses reflection inspecting the property type expected in the MyService.setMaximumCount() method which is java.lang.Integer
  3. Spring looks for a registered converter, formatter or PropertyEditor for java.lang.Integer
  4. Spring invokes the class that unmarshalls the value from String, and invokes setMaximumCount()

This is the same approach for Maps, Properties, Lists, Dates, URLs, etc. Very cool & very convenient!

What if we have a custom type like Social Security Number or US Phone Number? We may have a use-case to break the data into smaller chunks providing search by phone area code or SSN prefix.


Where Can We Find the Default Converters, Formatters, etc?

Take a look at org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser specifically the method getConversionService(). This method looks for the attributes conversion-service and validator in our <mvc:annotation-driven/> configuration.


Even cooler!!! This class looks for external conversion types for JSON, RSS, Java XML Binder and JSR-303!


For the full set of existing pre-registered PropertyEditors see: org.springframework.beans.PropertyEditorRegistrySupport in the method createDefaultEditors()

In Spring 3, also see org.springframework.format.support.FormattingConversionServiceFactoryBean where we will find reference to the JodaTime class conversion. This is very cool, if Spring finds it in the classpath, the formatter is registered.



Converting Old School in Spring 2.x

Steps to Setup Property Editors

Before 3.0, we registered custom types with org.springframework.beans.factory.config.CustomEditorConfigurer which is still supported in Spring 3.

  1. Create custom class implementing java.beans.PropertyEditorSupport methods getAsText/setAsText.
  2. Register org.springframework.beans.factory.config.CustomEditorConfigurer in our Spring application context XML file. The customEditors property element that accepts a map with key as our datatype and the value as our custom conversion (PropertyEditorSupport) class.

Although very useful, it lacks the Java 5 Generic support that we all know and love.



Converting New and Improved in Spring 3.x

Spring 3.0 introduced a typed implementation for custom type converters. The typed formatter interface is:

public interface Formatter<T> extends Printer<T>, Parser<T> {
}

Steps to Setup Property Formatters

  1. Create custom class implementing org.springframework.format.Formatter methods parse()/print().
  2. Create an application class that installs our custom types by extending org.springframework.format.support.FormattingConversionServiceFactoryBean and overriding the installFormatters() method
  3. In the installFormatters() method register your conversion type.



Implementing SSN Conversion

In the below example we will use Spring Roo classes with some minor tweaks for JodaTime API.

If you are unfamiliar with Joda time, you should be! JodaTime is THE replacement for the lame-duck that is Java’s date support.

The Employee Entity

Consider that we have an entity class called Employee:

package com.gordondickens.springformatter.entity;

import java.math.BigDecimal;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
import org.hibernate.annotations.Type;
import org.joda.time.DateTime;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.roo.addon.entity.RooEntity;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.tostring.RooToString;

@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Employee {

	@NotNull
	private String firstName;

	@NotNull
	private String lastName;

	@NotNull
	@Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
	@DateTimeFormat(pattern = "MM/dd/yyyy")
	private DateTime hireDate;

	@NotNull
	private BigDecimal salary;

	@Embedded
	private SocialSecurityNumber socialSecurityNumber;
}

Notes for the example above:

  1. The class is marked as a JPA @Entity
  2. The hireDate field is a JodaTime class of DateTime (which you should absolutely be using).
  3. The hireDate field includes the Hibernate @Type annotation indicating the JodaTime class for Hibernate (see the Maven dependency below)
  4. The hireDate field includes the Spring @DateTimeFormat annotation for defining the acceptable field format.
  5. The socialSecurityNumber field is of our SocialSecurityNumber type and is marked as JPA @Embedded
  6. Fields firstName, lastName and salary are annotated for JSR-303 validation with @NotNull

JodaTime/Hibernate Maven Dependency

When configured with Roo, the application configures all of the dependencies for us. However, the addition of JodaTime for the hireDate field with Hibernate requires us to add the following dependency.

  
    <groupId>joda-time</groupId>
    <artifactId>joda-time-hibernate</artifactId>
    <version>1.2</version>



Our Social Security Number Type

This is a simple (non-entity) class that will be embedded within our Employee class.

package com.gordondickens.springformatter.entity;

import javax.persistence.Embeddable;

import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.tostring.RooToString;

@RooJavaBean
@RooToString
@Embeddable
public class SocialSecurityNumber {

	private String prefix;
	private String middle;
	private String suffix;
}

In the above example we have marked this class as a JPA @Embeddable type.



Our Social Security Number Formatter

First step is to write the converter to marshal SocialSecurityNumber to/from String format:

package com.gordondickens.springformatter.util;

import java.text.ParseException;
import java.util.Locale;
import org.apache.commons.lang.StringUtils;
import org.springframework.format.Formatter;
import com.gordondickens.springformatter.entity.SocialSecurityNumber;

public class SsnFormatter implements Formatter {

	@Override
	public String print(SocialSecurityNumber object, Locale locale) {
		return object.toString();
	}

	@Override
	public SocialSecurityNumber parse(String text, Locale locale)
			throws ParseException {
		SocialSecurityNumber securityNumber = new SocialSecurityNumber();
		securityNumber.setPrefix(StringUtils.substringBefore(text, "-"));
		securityNumber.setMiddle(StringUtils.substringBetween(text, "-"));
		securityNumber.setSuffix(StringUtils.substringAfterLast(text, "-"));

		return securityNumber;
	}
}

We see the above SsnFormatter class implements the typed Formatter interface requiring us to implement the “parse” and “print” methods.



Our Custom Converter Installer

Next, we need to register our custom formatter:

package com.gordondickens.springformatter.util;

import org.springframework.format.FormatterRegistry;
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
import com.gordondickens.springformatter.entity.SocialSecurityNumber;
import org.springframework.stereotype.Component;

@Component
public class FormattingConverters extends
		FormattingConversionServiceFactoryBean {

	@Override
	public void installFormatters(FormatterRegistry registry) {
		super.installFormatters(registry);
		registry.addFormatterForFieldType(SocialSecurityNumber.class, new SsnFormatter());
	}
}

Above, we see that the class SocialSecurityNumber is registered. NOTE: remember to call super.installFormatters(...) to get the core Spring converters. Also, notice that we are registering this class as a @Component so we need to use in our application context file.



Wiring our Converter In

The last step is to configure Spring to use our FormattingConverters class. This is done by attaching it to the directive.

	<!-- Turns on support for mapping requests to Spring MVC @Controller methods
		Also registers default Formatters and Validators for use across all @Controllers -->
	<mvc:annotation-driven conversion-service="formattingConverters"/>



Summary

What we have seen is an improvement in the Spring type conversion which now supports Java 5 Generics. We connect this to the super very powerful <mvc:annotation-driven/> Spring directive. Let me know your thoughts.



Git the Code

Download the full code sample from:

git clone git://github.com/gordonad/Spring-3-Converters-Demo.git



Additional Reading

  • Spring 3.x Formatter & Converter Classes

    • FormattingConversionServiceFactoryBean – Installs Default Formatters
    • ConversionServiceFactory – Installs Default Converters
    • FormattingConversionService – Installs Annotated Field Formatters
  • Spring 2.x Formatter & Converter Classes – a.k.a Property Editors

    • ResourceEditorRegistrar – Registers Resource Types (File, URL, etc) in PropertyEditor
    • PropertyEditorRegistrarSupport – Creates Default PropertyEditors for Common Types (primitives, arrays, Collections, etc.)
  • Spring 3.x Validator Classes

    • SpringValidatingAdapter – Adapter for JSR-303 – Base for LocalValidatorFactoryBean
    • LocalValidatorFactoryBean – JSR-303 Setup, Implements SpringValidatingAdapter
    • Registering Custom Validators
  • Spring 2.x Validator Classes

  • SpringSource Blog – Spring 3 Type Conversion and Validation

About Gordon

Technology enthusiast primarily focused on Java and Open Source projects. Spring Certified Professional and Trainer. http://twitter.com/gdickens http://linkedin.com/in/gordondickens http://github.com/gordonad
This entry was posted in Hibernate, JodaTime, JPA, Roo, Spring and tagged , , , , , , , , , . Bookmark the permalink.

5 Responses to Using Spring 3.0 Custom Type Converter

  1. Pingback: Tweets that mention Technophile Blog » Using Spring 3.0 Custom Type Converter -- Topsy.com

  2. Marc Stock says:

    This article is useful but its title is incorrect since you’re not demonstrating Spring 3 converters at all but Formatters which are a different animal. Additionally, Converters replace PropertyEditors — not Formatters. Formatters are strictly oriented around formatting output in various ways are designed to support things like localization.

  3. Krishna says:

    Good article for learning spring formatters

  4. Pingback: Enterprise Spring Best Practices – Part 2 – Application Architecture | Technophile Blog

  5. I personally seem to go along with every little thing that ended up being authored within
    “Using Spring 3.0 Custom Type Converter | Technophile Blog”.
    Thank you for pretty much all the info.Thanks a lot-Kent

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>