Close

Spring MVC - Mustache View with mustache-spring-view (Spring 5 and Earlier)

This example shows how to use the Mustache template engine as a view technology in Spring MVC (versions before 6.0). Mustache is a logic-less template language: templates contain only tags that are filled in from the model, and no embedded code.

Why Spring 6 and Later Needs a Different Approach

Spring Framework itself has no Mustache support, so this example relies on the third-party library mustache-spring-view, which provides a MustacheViewResolver and a view class for Spring MVC. That library has been a convenient fit for Spring 5 and earlier, but it cannot be used with newer Spring versions:

  • Spring Framework 6.0 moved from the javax.* namespace to jakarta.* (Jakarta EE 9+), and Spring Framework 7 builds on Jakarta EE 11 (Servlet 6.1).
  • mustache-spring-view extends Spring's view classes and uses the servlet API in their method signatures, so it is compiled against javax.servlet. It has not been migrated to the Jakarta namespace, and its last release was 1.4 in November 2016.
  • On Spring 6 or later, the old javax.servlet classes are not on the classpath and the Jakarta types do not match, so the library fails at runtime (for example with ClassNotFoundException: javax.servlet.http.HttpServletRequest). Because the library is no longer maintained, it is not officially supported on these versions, and there is no compatible release to upgrade to.

With Spring 6 and later, use the Mustache engine directly and write a small view class on top of it (a subclass of AbstractTemplateView), or use Spring Boot's Mustache starter. Check out this Spring MVC tutorial and this Spring Boot tutorial.

Example

pom.xml

<dependency>
   <groupId>com.github.spullara.mustache.java</groupId>
   <artifactId>compiler</artifactId>
   <version>0.9.5</version>
</dependency>
<dependency>
   <groupId>com.github.sps.mustache</groupId>
   <artifactId>mustache-spring-view</artifactId>
   <version>1.4</version>
</dependency>

CurrencyRate

package com.logicbig.example;

import java.math.BigDecimal;
import java.time.LocalDateTime;

public class CurrencyRate {
  private String currencyPair;
  private LocalDateTime dateTime;
  private BigDecimal askPrice;
  private BigDecimal bidPrice;
    .............
}

CurrencyRateController

package com.logicbig.example;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Currency;
import java.util.List;

@Controller
@RequestMapping("/")
public class CurrencyRateController {

  @RequestMapping
  public String handleRequest(Model model) {
    model.addAttribute("todayCurrencyRates", getTodayForexRates());
    return "forex-view";
  }

  private List<CurrencyRate> getTodayForexRates() {
    // dummy rates
    List<CurrencyRate> currencyRates = new ArrayList<>();
    LocalDateTime today = LocalDateTime.of(2026, 2, 1, 0, 0, 0);
    List<Currency> currencies = new ArrayList<>(Currency.getAvailableCurrencies());

    int size = currencies.size();
    size = Math.min(size, 10);
    for (int i = 0; i + 1 < size; i += 2) {
      String currencyPair = currencies.get(i) + "/" + currencies.get(i + 1);
      CurrencyRate cr = new CurrencyRate();
      cr.setCurrencyPair(currencyPair);
      cr.setDateTime(today);
      BigDecimal bidPrice = new BigDecimal((i + 1) * 2);
      bidPrice = bidPrice.setScale(3, RoundingMode.CEILING);
      cr.setBidPrice(bidPrice);
      BigDecimal askPrice = new BigDecimal((i + 1) * 3);
      askPrice = askPrice.setScale(3, RoundingMode.CEILING);
      cr.setAskPrice(askPrice);
      currencyRates.add(cr);
    }
    return currencyRates;
  }
}

MyWebConfig

package com.logicbig.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.mustache.MustacheViewResolver;
import org.springframework.web.servlet.view.mustache.java.MustacheJTemplateFactory;

@EnableWebMvc
@Configuration
@ComponentScan
public class MyWebConfig implements WebMvcConfigurer {
    @Autowired
    ResourceLoader resourceLoader;

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        MustacheViewResolver mustacheViewResolver = new MustacheViewResolver();
        mustacheViewResolver.setPrefix("/WEB-INF/views/");
        mustacheViewResolver.setSuffix(".html");
        mustacheViewResolver.setCache(false);

        MustacheJTemplateFactory templateFactory = new MustacheJTemplateFactory();
        templateFactory.setResourceLoader(resourceLoader);
        mustacheViewResolver.setTemplateFactory(templateFactory);
        registry.viewResolver(mustacheViewResolver);
    }
}

Running the example

To try examples, run embedded Jetty (configured in pom.xml of example project below):

mvn jetty:run
$ curl -s "http://localhost:8080"
<html>
<head>
<style>
table {
border-collapse: collapse;
width:100%;
}
table, td, th {
border: 1px solid #999;
padding: 5px;
}
</style>
</head>
<body>
<h2>Currency Rates</h2>
<table>
<thead>
<tr>
<th>
<label>Currency Pair</label>
</th>
<th>
<label>Bid Price</label>
</th>
<th>
<label>Ask Price</label>
</th>
<th>
<label>Date</label>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
BOB/BOV
</td>
<td>
3.000
</td>
<td>
2.000
</td>
<td>
2026-02-01T00:00
</td>
</tr>
<tr>
<td>
LVL/USS
</td>
<td>
9.000
</td>
<td>
6.000
</td>
<td>
2026-02-01T00:00
</td>
</tr>
<tr>
<td>
SSP/CUC
</td>
<td>
15.000
</td>
<td>
10.000
</td>
<td>
2026-02-01T00:00
</td>
</tr>
<tr>
<td>
AFA/OMR
</td>
<td>
21.000
</td>
<td>
14.000
</td>
<td>
2026-02-01T00:00
</td>
</tr>
<tr>
<td>
XDR/VEF
</td>
<td>
27.000
</td>
<td>
18.000
</td>
<td>
2026-02-01T00:00
</td>
</tr>
</tbody>
</table>
</body>
</html>

Example Project

Dependencies and Technologies Used:

  • spring-webmvc 5.0.0.RELEASE (Spring Web MVC)
  • compiler 0.9.5 (Implementation of mustache.js for Java)
  • mustache-spring-view 1.4 (a spring view for the mustache templating language http://mustache.github.com/)
  • javax.servlet-api 3.0.1 (Java Servlet API)
  • guava 20.0 (Guava is a suite of core and expanded libraries that include utility classes, google's collections, io classes, and much much more. Guava has only one code dependency - javax.annotation, per the JSR-305 spec. [Description from guava-20.0.pom])
  • JDK 1.8
  • Maven 3.9.11

spring-mustache-view-example Select All Download
  • spring-mustache-view-example
    • site-swing-dev-tool
      • dataStore
        • preferences
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • MyWebConfig.java
          • webapp
            • WEB-INF
              • views

    See also

    Spring MVC - Mustache View


    Join