Spring MVC - Creating a new Custom Formatter Annotation

[Updated: Nov 19, 2016, Created: Feb 27, 2016]

Spring 3 formatter API provides a facility to bind an Annotation to a org.springframework.format.Formatter implementation. When creating a custom Formatter, we can define a corresponding annotation and bind that annotation to our formatter.

In this example we are going to define our own formatter annotation.

To bind an Annotation to a formatter, we have to implement AnnotationFormatterFactory

package org.springframework.format;
public interface AnnotationFormatterFactory<A extends Annotation> {

    Set<Class<?>> getFieldTypes();

    Printer<?> getPrinter(A annotation, Class<?> fieldType);

    Parser<?> getParser(A annotation, Class<?> fieldType);
}


In our last example we created a new formatter AddressFormatter by extending Formatter interface. Here we are going to bind the same formatter to a new annotation @AddressFormat. Doing so will enable us to declare formatting on field level.



Creating Backing object classes

public class Customer {
    private Long id;
    private String name;
    private Address address;

   //getters and setters
}

public class Address {
    private String street;
    private String city;
    private String county;
    private String zipCode;

    //getters and setters
}



Creating our Formatter

import org.springframework.format.Formatter;
import java.text.ParseException;
import java.util.Locale;

public class AddressFormatter implements Formatter<Address> {
    private Style style = Style.FULL;

    public void setStyle (Style style) {
        this.style = style;
    }

    @Override
    public Address parse (String text, Locale locale) throws ParseException {
           .....
        return address;
    }

    @Override
    public String print (Address a, Locale l) {
         ...
        return addressString;
    }

    public enum Style {
        FULL,
        REGION
    }
}



Creating Annotation

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface AddressFormat {

    AddressFormatter.Style style () default AddressFormatter.Style.FULL;
}



Using annotation in our backing object class

package com.logicbig.example;

public class Customer {
    private Long id;
    private String name;
    @AddressFormat(style = AddressFormatter.Style.FULL)
    private Address address;

    //getters and setters
}



Binding our Annotation by implementing AnnotationFormatterFactory

import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Parser;
import org.springframework.format.Printer;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class AddressFormatAnnotationFormatterFactory implements
                    AnnotationFormatterFactory<AddressFormat> {
    @Override
    public Set<Class<?>> getFieldTypes () {
        return new HashSet<>(Arrays.asList(Address.class));
    }

    @Override
    public Printer<?> getPrinter (AddressFormat annotation, Class<?> fieldType) {
        return getAddressFormatter(annotation, fieldType);
    }

    @Override
    public Parser<?> getParser (AddressFormat annotation, Class<?> fieldType) {
        return getAddressFormatter(annotation, fieldType);
    }

    private AddressFormatter getAddressFormatter (AddressFormat annotation,
                                                               Class<?> fieldType) {
        AddressFormatter formatter = new AddressFormatter();
        formatter.setStyle(annotation.style());
        return formatter;
    }
}



Registering The AnnotationFormatterFactory

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@EnableWebMvc
@Configuration
@Import(MyViewConfig.class)
public class MyWebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addFormatters (FormatterRegistry registry) {

        AddressFormatAnnotationFormatterFactory factory = new
                            AddressFormatAnnotationFormatterFactory();

        registry.addFormatterForFieldAnnotation(factory);
    }
  .....
}



Creating Controller

@Controller
@RequestMapping("/customers")
public class CustomerController {
    @Autowired
    private CustomerDataService customerDataService;

    @RequestMapping(method = RequestMethod.GET)
    private String handleRequest (Model model) {
        model.addAttribute("customerList", customerDataService.getAllUsers());
        return "customers";
    }
}




customer.jsp

<%@ page language="java"
    contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<html>
<body>
<h3>Customer List </h3>
<table style="width:100%">
<c:forEach var="customer" items="${customerList}" varStatus="status">
<tr>
<td>
    <spring:eval expression="customer.id" />
</td>
<td>
    <spring:eval expression="customer.name" />
</td>
<td>
   <spring:eval expression="customer.address" />
</td>
</tr>
</c:forEach>
</table>
</body>
</html>


Example Project

Run the web application by using embedded tomcat:

mvn  clean install tomcat7:run-war

Dependencies and Technologies Used :

  • Spring Web MVC 4.2.4.RELEASE: Spring Web MVC.
  • Spring TestContext Framework 4.2.4.RELEASE: Spring TestContext Framework.
  • Java Servlet API 3.0.1
  • javax.servlet:jstl 1.2
  • JUnit 4.12: JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
  • DataFactory 0.8: Library to generate data for testing.
  • JDK 1.8
  • Maven 3.0.4

Spring Custom Formatter Annotation Select All Download
  • spring-custom-formatter-annotation
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • webapp
          • WEB-INF
            • views
      • test
        • java
          • com
            • logicbig
              • example

See Also