Spring MVC - Annotation-driven Formatting

[Updated: Nov 1, 2016, Created: Feb 25, 2016]

Spring 3 formatter API provides a facility to bind an Annotation to a org.springframework.format.Formatter implementation.

That means while creating a Formatter we can define a corresponding annotation and bind that to our formatter. We will see an example on that in the next tutorial.

In this tutorial, we are going to explore Spring predefined formatter annotations.

Currently there are two predefined such annotations provided by Spring: @NumberFormat and @DateTimeFormat.

Here we are going to modify our last example for using @NumberFormat and @DateTimeFormat instead of @InitBinder method configuration for them.



The backing Object

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;

public class Trade {

    private long tradeId;
    private String buySell;
    private Currency buyCurrency;
    private Currency sellCurrency;

    @NumberFormat(pattern = "#,###,###,###.##")
    private BigDecimal amount;

    @DateTimeFormat(pattern = "MM-dd-yyyy")
    private Date tradeDate;

    //getters and setters
}

Diff with the previous example's Trade class:

  
package com.logicbig.example;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Date;
public class Trade {
private long tradeId;
private String buySell;
private Currency buyCurrency;
private Currency sellCurrency;
@NumberFormat(pattern = "#,###,###,###.##")
private BigDecimal amount;
@DateTimeFormat(pattern = "MM-dd-yyyy")
private Date tradeDate;
....
}
Red=deleted, Green=Inserted



The Controller

In InitBinder method, as compared to our last example, we are not going to remove the formatters registration except for CurrencyStyleFormatter. We don't have a corresponding annotation for CurrencyStyleFormatter. Also this formatter access some dynamic information which will not be possible on annotation level.

@Controller
@RequestMapping("trades")
public class TradeController {

    @Autowired
    private TradeService tradeService;

    @InitBinder
    private void customizeBinding (@PathVariable("tradeId") long tradeId,
                                   WebDataBinder binder) {
        Trade trade = tradeService.getTradeById(tradeId);
        if (trade == null) {
            return;
        }

        CurrencyStyleFormatter currencyFormatter = new CurrencyStyleFormatter();
        currencyFormatter.setCurrency("Buy".equals(trade.getBuySell()) ?
                              trade.getBuyCurrency() : trade.getSellCurrency());
        binder.addCustomFormatter(currencyFormatter, "amount");
    }

    @RequestMapping("/{tradeId:\\d+}")
    public String handleTradeRequest (@PathVariable("tradeId") long tradeId,
                                                                   Model model) {
      ........
    }
}

Diff with the previous example's Controller class:

 
package com.logicbig.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.format.number.CurrencyStyleFormatter;
import org.springframework.format.number.NumberStyleFormatter;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("trades")
public class TradeController {
@Autowired
private TradeService tradeService;
@InitBinder
private void customizeBinding (@PathVariable("tradeId") long tradeId, WebDataBinder binder) {
Trade trade = tradeService.getTradeById(tradeId);
if (trade == null) {
return;
}
DateFormatter dateFormatter = new DateFormatter();
dateFormatter.setPattern("MM-dd-yyyy");
binder.addCustomFormatter(dateFormatter, "tradeDate");
NumberStyleFormatter numberFormatter = new NumberStyleFormatter();
numberFormatter.setPattern("#,###,###,###.##");
binder.addCustomFormatter(numberFormatter, "amount");
CurrencyStyleFormatter currencyFormatter = new CurrencyStyleFormatter();
currencyFormatter.setCurrency(
"Buy".equals(trade.getBuySell()) ? trade.getBuyCurrency() : trade
.getSellCurrency());
binder.addCustomFormatter(currencyFormatter, "amount");
}
@RequestMapping("/{tradeId:\\d+}")
public String handleTradeRequest (@PathVariable("tradeId") long tradeId, Model model) {
Trade trade = tradeService.getTradeById(tradeId);
if (trade == null) {
model.addAttribute("msg", "No trade found");
return "no-trade-page";
}
model.addAttribute("trade", trade);
return "trade-page";
}
}
Red=deleted, Green=Inserted


The rest of the stuff is same as our last example. Also the outcome will be same here.

Example Project

To test controllers run the unit tests in RegistrationControllerTest.

Or you can run the app 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
  • JUnit 4.12: JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
  • JDK 1.8
  • Maven 3.0.4

Spring Format Annotation Example Select All Download
  • spring-format-annotation
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • webapp
          • WEB-INF
            • views
      • test
        • java
          • com
            • logicbig
              • example

See Also