Master Compile-Time Mustache Templates with JStachio in Spring Boot 3

This article introduces compile-time template concepts, explains the JStachio library and its extensive Mustache support, compares its performance, and provides step‑by‑step Maven and Spring Boot integration examples with full code snippets and result screenshots.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Compile-Time Mustache Templates with JStachio in Spring Boot 3

1. Introduction

Traditional template engines load and parse templates at runtime, which adds overhead and can hide format errors until execution. JStachio compiles Mustache templates into Java classes during the build, allowing the compiler to catch template mistakes early and eliminating runtime reflection, making it suitable for GraalVM native images.

2. Core Features of JStachio

Full support for Mustache v1.3.0 non‑optional requirements, including whitespace handling.

Optional inheritance and Lambda support (with static‑friendly differences).

Static value binding with compile‑time checks.

Ability to reference methods, fields, and getters inside templates.

Friendly error messages with context.

Zero configuration – works with standard javac and any IDE or build system.

Support for non‑HTML templates and customizable output types.

Mustache inheritance for layout composition.

ServiceLoader‑based fallback rendering (JMustache/mustache.java) for development.

Optional runtime rendering fallback for quick edits.

Customizable toString handling to avoid unsafe calls.

Extension points via @JStacheInterfaces.

Strong Lambda support, including Map<String, ?> and Optional<?>.

Compatibility with JMustache and Handlebars list extensions ( -first, -last, -index).

One of the fastest Java Mustache engines.

No external dependencies besides JStachio itself.

Zero‑runtime‑dependency option for minimal native image size.

First‑class Spring framework integration via a dedicated starter.

3. Performance Comparison

The following chart shows JStachio’s runtime speed compared with other Mustache implementations.

4. Practical Example

4.1 Quick Start

Add the JStachio dependency to your pom.xml:

<dependency>
  <groupId>io.jstach</groupId>
  <artifactId>jstachio</artifactId>
  <version>1.3.7</version>
</dependency>

Configure the Maven compiler plugin to use the annotation processor:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.8.1</version>
  <configuration>
    <source>21</source>
    <target>21</target>
    <annotationProcessorPaths>
      <path>
        <groupId>io.jstach</groupId>
        <artifactId>jstachio-apt</artifactId>
        <version>1.3.7</version>
      </path>
    </annotationProcessorPaths>
  </configuration>
</plugin>

4.2 Full Example

A JUnit test demonstrating template rendering:

public class TemplateTest {
  @JStache(template = """
      {{#people}}
      {{message}} {{name}}!你{{#ageInfo}}{{age}}{{/ageInfo}}岁了!
      {{#-last}}
      目前就这些啦!
      {{/-last}}
      {{/people}}
      """)
  public record HelloWorld(String message, List<Person> people) implements AgeLambdaSupport {}

  public record Person(String name, LocalDate birthday) {}

  public record AgeInfo(long age, String date) {}

  public interface AgeLambdaSupport {
    @JStacheLambda
    default AgeInfo ageInfo(Person person) {
      long age = ChronoUnit.YEARS.between(person.birthday(), LocalDate.now());
      String date = person.birthday().format(DateTimeFormatter.ISO_DATE);
      return new AgeInfo(age, date);
    }
  }

  @Test
  public void testPerson() throws Exception {
    Person rick = new Person("张三", LocalDate.now().minusYears(70));
    Person morty = new Person("李四", LocalDate.now().minusYears(14));
    Person beth = new Person("王五", LocalDate.now().minusYears(35));
    Person jerry = new Person("赵六", LocalDate.now().minusYears(35));
    String actual = JStachio.render(new HelloWorld("你好,外星人", List.of(rick, morty, beth, jerry)));
    System.err.println(actual);
  }
}

Result screenshot:

5. Integration with Spring Boot

5.1 Spring Boot Starter Dependency

<dependency>
  <groupId>io.jstach</groupId>
  <artifactId>jstachio-spring-boot-starter-webmvc</artifactId>
  <version>1.3.7</version>
</dependency>

5.2 Template File (templates/user.mustache)

<ul>
  <li>姓名: {{name}}</li>
  <li>年龄: {{age}}</li>
  <li>邮箱: {{email}}</li>
</ul>

5.3 Model Definition

@JStache(path = "templates/user.mustache")
public record UserModel(String name, Integer age, String email) {}

5.4 Controller

@Controller
@RequestMapping("/jstach")
public class JstachController {
  @GetMapping("/user")
  public View user() {
    return JStachioModelView.of(
        new UserModel("Pack_xg", 33, "[email protected]"),
        "text/html;charset=UTF-8");
  }
}

Rendered page screenshot:

6. Compile‑Time Error Detection

If a template reference is misspelled (e.g., {{list2}} instead of {{list}}), the build fails with a clear compilation error, preventing runtime failures.

JavaperformanceSpring BootMustacheCompile-time TemplatesJStachio
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.