Enterprise Java

Using Compile-Time Templates with Spring Boot

Template engines are a foundational part of server-side web development. While runtime engines such as Thymeleaf and FreeMarker are widely used, they rely on parsing templates at runtime and resolving variables dynamically—often leading to runtime errors and additional overhead. Compile-time template engines take a different approach: templates are compiled into Java code during the build, producing strongly typed classes that can be rendered with minimal runtime cost. Let us delve into understanding Spring compile-time templates and how they streamline application development.

1. What Are Compile-Time Templates?

Compile-time templates are template engines that generate Java code during compilation rather than interpreting templates at runtime. This approach offers several advantages, including faster execution, early detection of errors, type-safe access to template variables, and reduced runtime overhead. By generating fully-typed Java classes ahead of time, compile-time templates can eliminate common runtime errors and improve maintainability. These templates are particularly useful in web applications, microservices, and server-side rendering scenarios where performance and reliability are critical. They can also simplify integration with frameworks like Spring, allowing developers to leverage dependency injection, REST controllers, and reactive programming while using strongly-typed templates.

Popular compile-time template engines include:

  • JStachio: A lightweight Java template engine that generates code at compile time for high performance. It is designed to minimize boilerplate while providing compile-time checks for template variables and expressions.
  • Rocker: A fast, Java-centric template engine that produces strongly-typed Java classes from templates. Rocker supports incremental compilation and can generate templates for both server-side and static content efficiently.
  • JTE: Generates Java code from templates and integrates seamlessly with Spring Boot for server-side rendering. It emphasizes simplicity, type safety, and supports advanced features like layouts, includes, and parameterized templates.

Compile-time templates are especially valuable in projects that prioritize performance, maintainability, and type safety, making them a natural fit for Spring applications and other enterprise-grade Java systems.

1.1 Compile-Time Template Engines: Benefits, Use Cases, and Performance

The table below summarizes the key advantages, typical use cases, and performance characteristics of popular compile-time template engines:

Template EngineBenefitsUse CasePerformance
JStachioLightweight, type-safe, minimal boilerplate, compile-time error checksServer-side rendering, microservices, projects needing strong typingHigh – generates Java code, low runtime overhead
RockerStrongly-typed templates, incremental compilation, reusable template classesDynamic server-side pages, static site generation, scalable web appsVery high – precompiled Java classes allow fast template rendering
JTEType-safe, simple API, layouts & includes, Spring Boot integrationSpring Boot applications, enterprise web apps, parameterized templatesHigh – compile-time code generation reduces runtime parsing

1.2 Compile-Time vs Runtime Templates in Spring

Unlike runtime template engines such as Thymeleaf and FreeMarker, which parse templates and resolve variables during request handling, compile-time template engines validate templates during the build phase. This means missing variables, invalid expressions, or mismatched parameters fail fast at compile time rather than causing runtime errors. While runtime engines offer greater flexibility for dynamic templates, compile-time engines prioritize correctness, performance, and maintainability, making them well suited for high-throughput and production-critical Spring Boot applications.

2. Using Multiple Template Engines in Spring Boot

2.1 Configuring Maven Dependencies for Template Engines

<dependencies>

    <!-- JTE -->
    <dependency>
        <groupId>gg.jte</groupId>
        <artifactId>jte-spring-boot-starter</artifactId>
        <version>stable__jar__version</version>
    </dependency>

    <!-- JStachio -->
    <dependency>
        <groupId>org.jstach</groupId>
        <artifactId>jstachio-spring-boot-starter</artifactId>
        <version>stable__jar__version</version>
    </dependency>

    <!-- Rocker -->
    <dependency>
        <groupId>com.fizzed</groupId>
        <artifactId>rocker-runtime</artifactId>
        <version>stable__jar__version</version>
    </dependency>

    <dependency>
        <groupId>com.fizzed</groupId>
        <artifactId>rocker-compiler</artifactId>
        <version>stable__jar__version</version>
        <scope>provided</scope>
    </dependency>

</dependencies>

This Maven <dependencies> section configures three compile-time template engines in a Spring Boot application. The JTE dependency (jte-spring-boot-starter) enables integration with the JTE template engine, allowing .jte templates to be compiled into Java classes during the build and rendered efficiently at runtime through Spring’s auto-configuration. The JStachio dependency (jstachio-spring-boot-starter) adds support for annotation-driven, Mustache-style templates that generate type-safe renderers at compile time based on annotated Java records or classes. The Rocker setup consists of two dependencies: rocker-runtime, which provides the runtime APIs needed to render templates, and rocker-compiler, which is marked with provided scope to ensure it is used only during compilation to generate strongly typed Java classes from .rocker.html templates. Together, these dependencies enable compile-time validation, early error detection, and high-performance template rendering without runtime template parsing.

2.2 Spring Boot Application

@SpringBootApplication
public class CompileTemplatesApplication {

    public static void main(String[] args) {
        SpringApplication.run(CompileTemplatesApplication.class, args);
    }
}

This class is the entry point of the Spring Boot application. The @SpringBootApplication annotation enables component scanning, auto-configuration, and configuration support, allowing Spring Boot to automatically detect and configure beans such as controllers and template engines defined in the project. The main method delegates to SpringApplication.run(), which bootstraps the Spring context, starts the embedded web server, and initializes the application lifecycle, making the compile-time template engines available for rendering requests once the application is running.

2.3 JTE: Compile-Time Templates

2.3.1 Template

<!-- src/main/jte/greeting.jte -->
<h1>Hello, ${name}! (JTE)</h1>

This JTE template defines a simple HTML heading that references the name variable using JTE’s expression syntax. During the build process, JTE compiles this .jte file into a Java class, validating that all referenced variables exist and are used correctly. If the name variable is missing, misspelled, or syntactically invalid, the compilation fails, preventing the application from starting with an invalid template.

2.3.2 Controller Usage

@RestController
@RequestMapping("/jte")
class JteController {

    private final JteTemplateEngine templateEngine;

    JteController(JteTemplateEngine templateEngine) {
        this.templateEngine = templateEngine;
    }

    @GetMapping
    public String render() {
        return templateEngine.render(
            "greeting.jte",
            Map.of("name", "Spring Developer")
        );
    }
}

This Spring REST controller demonstrates how a compiled JTE template is rendered at runtime. The JteTemplateEngine is injected by Spring Boot and used to render the precompiled greeting.jte template by passing a model map containing the name value. Because the template has already been validated and compiled, rendering simply executes the generated Java code, resulting in fast, type-safe HTML output without any runtime template parsing.

2.4 JStachio: Annotation-Driven Compile-Time Rendering

2.4.1 Model

<!-- src/main/resources/templates/greeting.mustache -->
<h1>Hello, {{name}}! (JStachio)</h1>

This Mustache-style template defines a simple greeting that references the name property using JStachio’s placeholder syntax. At compile time, JStachio validates this template against the associated Java model annotated with @JStache, ensuring that the name field exists and matches the model’s structure. Any mismatch between the template placeholders and the model fields results in a compilation error, providing strong type safety before the application is run.

2.4.2 Controller Usage

@RestController
@RequestMapping("/jstachio")
class JStachioController {

    private final JStachioTemplateEngine engine;

    JStachioController(JStachioTemplateEngine engine) {
        this.engine = engine;
    }

    @GetMapping
    public String render() {
        return engine.execute(new GreetingModel("Spring Developer"));
    }
}

This REST controller shows how a JStachio template is rendered using a compile-time–generated renderer. The JStachioTemplateEngine is injected by Spring Boot and executes the renderer associated with the GreetingModel record, passing the model instance directly to the template. Because the renderer is generated during compilation, rendering involves executing precompiled Java code, resulting in fast output generation with no runtime template parsing and guaranteed alignment between the template and the model.

2.5 Rocker: Strongly Typed Generated Java Templates

2.5.1 Template

<!-- src/main/rocker/views/greeting.rocker.html -->
@args String name
<h1>Hello, @name! (Rocker)</h1>

This Rocker template declares its required inputs using the @args directive, specifying that the template expects a single String name parameter. During compilation, the Rocker compiler transforms this template into a strongly typed Java class with a corresponding factory method that enforces the declared arguments. If the argument list is changed or the template is invoked with incorrect parameters, the build fails, ensuring strict compile-time validation and eliminating an entire class of runtime rendering errors.

2.5.2 Controller Usage

import views.greeting;

@RestController
@RequestMapping("/rocker")
class RockerController {

    @GetMapping
    public String render() {
        return greeting
            .template("Spring Developer")
            .render()
            .toString();
    }
}

This Spring REST controller demonstrates direct usage of the Java class generated from the Rocker template. The generated views.greeting class exposes a type-safe template() method that requires the exact arguments defined in the template, ensuring compile-time correctness. Rendering the template executes precompiled Java code and produces the final HTML output without relying on a runtime template engine or parsing step, resulting in very fast and predictable rendering behavior.

2.6 Code Run and Code Output

After setting up the Maven dependencies, templates, and Spring Boot application, you can run the application using your IDE or via the command line. All three engines perform template code generation during the standard Maven “compile” phase, meaning no additional build steps or custom plugins are required beyond declaring the dependencies.

mvn clean compile


mvn spring-boot:run

After the application is running, each template engine can be accessed through its dedicated HTTP endpoint, allowing you to verify the rendered output produced by the precompiled templates:

  • /jte – Renders the JTE template using the precompiled .jte file and a model map, demonstrating Spring Boot–managed compile-time template rendering.
  • /jstachio – Executes a JStachio renderer generated at compile time from the annotated model and Mustache template, ensuring strict alignment between the template and its data model.
  • /rocker – Invokes the Java class generated by the Rocker compiler directly, showing how strongly typed templates can be rendered without any runtime template parsing or engine abstraction.

Fig. 1: Demo Output
Fig. 1: Demo Output

2.7 When Compile-Time Templates May Not Be Ideal

While compile-time templates provide strong guarantees and performance benefits, they may not be ideal in scenarios where templates must be modified frequently at runtime, loaded dynamically from external sources, or edited by non-developers. In such cases, runtime template engines may offer greater flexibility at the cost of reduced type safety and runtime validation.

3. Conclusion

Compile-time template engines eliminate an entire class of runtime errors by shifting validation and rendering logic into the build phase. JTE, JStachio, and Rocker all demonstrate how strongly typed, precompiled templates can improve correctness, performance, and maintainability in Spring Boot applications.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button