Spring MVC Custom Formatter

In this Spring MVC tutorial, we delve into the concept of custom formatter and their role in data binding within Spring MVC applications. While Spring MVC‘s basic formatters handle fundamental data type formatting like String/Date, they fall short when dealing with more complex data formatting. This is where custom formatters come into play, enabling us to manage complex data transformations effectively.

Understanding The Situation

The Problem

Consider a User class with attributes such as first name, last name, email, and salary (of type Salary). In our registration form (registration.jsp), we aim to capture user details, including their salary. However, presenting the salary as “4000 dollars” or “4000 euros” directly in the form leads to an issue during data binding. Spring MVC lacks the intrinsic knowledge of how to map this textual representation(“4000 euros” ) to the corresponding Salary object (currency + amount fields) in our application.

  • User Class
public class User {

    private String firstName = "John";
    private String lastName = "Doe";
    private String email = "john@javalaunchpad.com";
    private Salary salary;
    // getters & setters 
}
  • registration.jsp form
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Registration Form</title>
    <style>
          // css  => refer to the code example on github
    </style>
</head>
<body>

<div class="container">
    <h2>Registration Form</h2>
    <form:form action="register" method="post" modelAttribute="user">
        <label for="first_name">First Name:</label>
        <form:input type="text" id="first_name" path="firstName" />
        <label for="last_name">Last Name:</label>
        <form:input type="text" id="last_name" path="lastName" />
        <label for="email">Email:</label>
        <form:input type="email" id="email" path="email" />
        <label for="salary">Salary:</label>
        <form:input type="text" id="salary" path="salary" />
        <button type="submit">Sign Up</button>
    </form:form>
</div>

</body>
</html>

The Solution

To bridge this gap and ensure seamless data binding, we need to educate Spring MVC on parsing our textual input into the desired object representation. This is achieved through a custom data binder, commonly known as a formatter. By defining a custom formatter, we provide Spring with the necessary instructions to convert the input string (“4000 dollars” or “4000 euro”) into a valid Salary object.

What is a Formatter In Spring MVC?

Formatters in Spring MVC play an important role in the data binding process, bridging the gap between string representations of data and specific Java object types. They are implementations of the Formatter<> interface provided by Spring MVC and are essential components for converting user input, often from form fields, into Java objects and vice versa.

The Formatter<T> interface extends both the Printer<T> and Parser<T> interfaces:

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

}

// Printer.java
@FunctionalInterface
public interface Printer<T> {
    String print(T object, Locale locale);
}

// Parser.java
@FunctionalInterface
public interface Parser<T> {
    T parse(String text, Locale locale) throws ParseException;
}

Let’s break down the key components of formatters and their functionality:

Parsing and Printing Operations

  • Parsing: Parsing is the action of taking a string input, typically from user interfaces like form submissions, and converting it into the corresponding Java object.
  • Printing: On the flip side, printing involves taking a Java object and converting it back into its string representation. Formatters excel in this task by transforming Java objects into strings that can be displayed or communicated effectively through user interfaces.

Implementing A Custom Formatter

Project set up

Before delving into the implementation of SalaryFormatter, it’s crucial to have your project configured with essential dependencies, notably the Spring Web MVC dependency. If you’re working on a non-Spring Boot project, refer to this article for guidance on configuring your project, including setting up DispatcherServlet, deploying to Tomcat, and other necessary configurations.

However, if you’re using a Spring Boot project, you can proceed with confidence, as Spring Boot’s autoconfiguration capabilities handle much of the setup for you. This includes configuring DispatcherServlet, deploying your application to an embedded Tomcat server, and handling other configuration aspects automatically.

SalaryFormatter Example

  • Salary Class

To begin, create a Java class named Salary that represents salary information and add it as a field in the User class

public class User {

    private String firstName = "John";
    private String lastName = "Doe";
    private String email = "john@javalaunchpad.com";
    private Salary salary;
    // getters & setters 
}

// Salary.java
public class Salary {
    private String currency;
    private BigDecimal amount;
}
  • Create the Custom Formatter

Now, let’s implement the SalaryFormatter class, which serves as a custom formatter for the Salary class. The SalaryFormatter class implements the Formatter<Salary> interface, providing methods for parsing and printing salary data.

public class SalaryFormatter implements Formatter<Salary> {

    @Override
    public Salary parse(String text, Locale locale) throws ParseException {
        String ammount =  text.substring(0 ,text.indexOf(' '));
        String currency = text.substring(text.indexOf(' ') + 1);
        Salary salary = new Salary();
        salary.setCurrency(currency);
        salary.setAmount(Double.parseDouble(ammount));
        return salary;
    }

    @Override
    public String print(Salary object, Locale locale) {
        System.out.println("object = " + object);
        return object.getCurrency() + " " + object.getAmount();
    }
}

Custom Formatter Execution Workflow

If you’re wondering when this formatter gets executed, let’s simplify the process with the diagram below:

Customizing Data Binding in Spring MVC: Workflow
  • The DispatcherServlet calls the handle() method on the handlerAdapter object.
  • The handle() method then invokes the invokeAndHandle() method of the ServletInvocableHandlerMethod object used by the HandlerAdapter.
  • The HandlerMethodArgumentResolver kicks in to resolve the method arguments, retrieving them from the request.
  • If any arguments require conversion or formatting, the DataBinder (WebDataBinder implementation) steps in to handle it.
  • The DataBinder consults the registered converters and formatters to perform the necessary conversion or formatting.
  • Once the arguments are resolved, the ServletInvocableHandlerMethod invokes the handler (controller method) with those arguments.
  • The handlerAdapter constructs the ModelAndView and returns it to the DispatcherServlet, which then passes it to the ViewResolvers for further processing.”

Having seen how formatters are executed by the DataBinder, let’s now explore how to register them so that they can be utilized by the DataBinder.

Custom Formatter Registration

To register a custom formatter (e.g., SalaryFormatter) simply use a configuration class, which implements WebMvcConfigurer and override the addFormatters() method.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new SalaryFormatter());
    }
}

This code snippet demonstrates how to register the SalaryFormatter with the FormatterRegistry in your WebMvcConfig class. This ensures that the formatter is available for use by the DataBinder during conversion and formatting operations.

Consider adding the @EnableWebMvc annotation to your configuration class if you’re working with a non-Spring Boot application.

Usage Example

Consider the following example showcasing how our custom formatter (SalaryFormatter ), within a simple Spring MVC application:

@GetMapping("/form")
public String showForm(Model model) {
        User user = new User();
        model.addAttribute("user", user);
        return "registration";
}



@PostMapping("/register")
public String register(User user , Model model) {
        model.addAttribute("user", user);
        return "success";
}

In this example, the /register method accepts the User object as an argument, which includes the Salary field. When a client invokes this endpoint by sending User data via a POST request, the salary value will arrive as a string (ex: 1000 $). During the data binding process, as we discussed earlier, this string value will be formatted into our Salary object format (using SalaryFormatter formatter ).

you can find the working code example on Github.

Conclusion

In this article, we have explored the limitations of Spring MVC’s default formatters and discussed how to implement custom formatters. If you’re curious about the distinction between a formatter and a converter, please let me know in the comments, and we can cover it in a separate article.

Leave a Reply

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

Join the Tribe