FreeMarker tutorial

This is an introductory tutorial of the FreeMarker Java template engine. We introduce the FreeMarker template engine and create several console and web applications. Maven is used to build our examples. NetBeans is used to manage the applications.

Table of contents

FreeMarker is a template engine for the Java programming language. Templates are written in the FreeMarker Template Language (FTL). FreeMarker's home page is freemarker.org.

FreeMarker template engine

A template engine combines static data with dynamic data to produce content. A template is an intermediate representation of the content; it specifies how the output will be generated.

The advantages of a template engine are:

A FreeMarker template file has by convention a .ftl extension.

FreeMarker is not restricted to templates for HTML pages; it can be used to generate e-mails, configuration files, source code etc.

FreeMarker console applications

The first two applications are console applications. We create new Maven Java Applications in NetBeans. They use the following Maven build file:

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.23</version>
</dependency>       

In the Maven pom.xml file, we specify the FreeMarker dependency.

FreeMarker interpolations

Interpolations are expressions put between the ${ } characters. FreeMarker will replace an interpolation in the output with the actual value of the expression inside the curly brackets.

In the following example, we use a FreeMarker template file to generate simple text output.

Java console project structure in NetBeans
Figure: Java console project structure in NetBeans

This is the project structure in NetBeans.

FreeMarkerConsoleEx.java
package com.zetcode;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

public class FreeMarkerConsoleEx {

    public static void main(String[] args) throws IOException,
            TemplateException {

        Configuration cfg = new Configuration(new Version("2.3.23"));

        cfg.setClassForTemplateLoading(FreeMarkerConsoleEx.class, "/");
        cfg.setDefaultEncoding("UTF-8");

        Template template = cfg.getTemplate("test.ftl");

        Map<String, Object> templateData = new HashMap<>();
        templateData.put("msg", "Today is a beautiful day");

        try (StringWriter out = new StringWriter()) {
            
            template.process(templateData, out);
            System.out.println(out.getBuffer().toString());

            out.flush();
        }
    }
}

The example prints a simple text to the console. The final text was processed by a template engine.

Configuration cfg = new Configuration(new Version("2.3.23"));

Configuration is used to set the FreeMarker settings; it takes the version of the FreeMarker library as a parameter.

cfg.setClassForTemplateLoading(FreeMarkerConsoleEx.class, "/");

The setClassForTemplateLoading() sets the class whose method will be used to load templates.

Template template = cfg.getTemplate("test.ftl");

With the getTemplate() method, we retrieve the test.ftl template file.

Map<String, Object> templateData = new HashMap<>();
templateData.put("msg", "Today is a beautiful day");

The data model is created. The data from the model will be dynamically placed into the FreeMarker template file.

try (StringWriter out = new StringWriter()) {
    
    template.process(templateData, out);
    System.out.println(out.getBuffer().toString());

    out.flush();
}

The process() method executes the template, using the provided data model and writing the generated output to the supplied writer.

test.ftl
The message is: ${msg}

The test.ftl template file contains one interpolation; it will be replaced with the generated string.

The message is: Today is a beautiful day

This is the output of the application.

Listing a collection with FreeMarker

The #list directive lists a collection of data.

The next example produces a list of cars.

Car.java
package com.zetcode.bean;

public class Car {

    private String name;
    private int price;

    public Car() {
    }

    public Car(String name, int price) {

        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

We have a Car bean. It has two attributes: name and price.

FreeMarkerConsoleEx2.java
package com.zetcode;

import com.zetcode.bean.Car;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class FreeMarkerConsoleEx2 {

    public static void main(String[] args) throws IOException,
            TemplateException {

        Configuration cfg = new Configuration(new Version("2.3.23"));

        cfg.setClassForTemplateLoading(FreeMarkerConsoleEx2.class, "/");
        cfg.setDefaultEncoding("UTF-8");

        Template template = cfg.getTemplate("test.ftl");

        Map<String, Object> templateData = new HashMap<>();

        Car c1 = new Car("Audi", 52642);
        Car c2 = new Car("Volvo", 29000);
        Car c3 = new Car("Skoda", 9000);
        
        List<Car> cars = new ArrayList<>();
        cars.add(c1);
        cars.add(c2);
        cars.add(c3);        

        templateData.put("cars", cars);

        try (StringWriter out = new StringWriter()) {

            template.process(templateData, out);
            System.out.println(out.getBuffer().toString());

            out.flush();
        }
    }
}

This example is a Java console program, which uses FreeMarker to dynamically create a text output containing a list of cars.

Map<String, Object> templateData = new HashMap<>();

Car c1 = new Car("Audi", 52642);
Car c2 = new Car("Volvo", 29000);
Car c3 = new Car("Skoda", 9000);

List<Car> cars = new ArrayList<>();
cars.add(c1);
cars.add(c2);
cars.add(c3);        

templateData.put("cars", cars);

Here we create an ArrayList of Car objects and put it into the data model.

test.ftl
<#list cars as car>
${car.name}: ${car.price}
</#list>  

The template file contains a #list directive which prints the attributes of the car objects; the attributes are accessed with the dot character.

Audi: 52,642
Volvo: 29,000
Skoda: 9,000

This is the output of the example.

FreeMarker directives

FreeMarker directives are special tags that perform an action. There are two kinds of directives: built-in and custom.

The <#assign> tag creates a new plain variable. It can be accessed with the ${} construct. The variable is created in the template. If there is an equally named variable in the data model, the template variable hides it.

assignment.ftl
<#assign name = "Robert">

His name is ${name}.

The <#assign> directive creates a new name variable. The value of the variable is printed with the ${name} syntax.

His name is Robert.

The example prints this line.

Conditional processing of template sections can be done with he <#if>, <#elseif>, and <#else> directives.

conditions.ftl
<#assign value = 4>

<#if value < 0>
  The number is negative
<#elseif value == 0>  
  The number is zero
<#else>
  The number is positive
</#if>

The example creates a new value variable and uses conditional directives to test the value.

The number is positive

This is the output.

The <#list> directive is used for traversing a sequence.

listing.ftl
<#assign colours = ["red", "green", "blue", "yellow"]>

<#list colours as col>
${col}
</#list>

In the example, we assing a new sequence of colour names to the colours variable. The <#list> directive goes through the collection and prints each item.

red
green
blue
yellow

The example gives this output.

listing2.ftl
<#assign items = {"pens": 3, "cups": 2, "tables": 1}>

<#list items?values as v>
${v}
</#list>

<#list items?keys as k>
${k}
</#list>

In this example, we create a hash variable and use the <#list> to output the values and the keys of the hash.

3
2
1

pens
cups
tables

The example gives this output.

The <#compress> directive removes superfluous white-space when we use a white-space insensitive format (e.g. HTML or XML)

compressing.ftl
<#assign value="\t\tweather\n\n">

<#compress>
${value}
        Today is a wonderful day.
   1 2   3       4     5     
</#compress>

We have text with spaces, tabs, and new lines.

weather
Today is a wonderful day.
1 2 3 4 5

The program removed all superfluous white-space.

FreeMarker servlet example

In the following example, we use FreeMarker in a standard Java web application. The application is packed into a war file and deployed on the NetBeans's build-in Tomcat server.

In NetBeans, we create a new Maven Web Application.

FreeMarker servlet project structure in NetBeans
Figure: FreeMarker servlet project structure in NetBeans

This is the project structure of the FreeMarker servlet example in NetBeans.

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.23</version>
</dependency>

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-web-api</artifactId>
    <version>7.0</version>
    <scope>provided</scope>
</dependency>

In the pom.xml file, we have these two dependencies.

context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/FreemarkerServletEx"/>

This is the context.xml file.

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    
    <servlet>
        <servlet-name>freemarker</servlet-name>
        <servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>

        <init-param>
            <param-name>TemplatePath</param-name>
            <param-value>/</param-value>
        </init-param>
        <init-param>
            <param-name>NoCache</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>ResponseCharacterEncoding</param-name>
            <param-value>fromTemplate</param-value>
        </init-param>
        <init-param>
            <param-name>ExceptionOnMissingTemplate</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>incompatible_improvements</param-name>
            <param-value>2.3.23</param-value>
        </init-param>
        <init-param>
            <param-name>template_exception_handler</param-name>
            <param-value>html_debug</param-value>
        </init-param>
        <init-param>
            <param-name>template_update_delay</param-name>
            <param-value>0 s</param-value>
        </init-param>
        <init-param>
            <param-name>default_encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>output_encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>locale</param-name>
            <param-value>en_US</param-value>
        </init-param>
        <init-param>
            <param-name>number_format</param-name>
            <param-value>0.##########</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>freemarker</servlet-name>
        <url-pattern>*.ftl</url-pattern>
    </servlet-mapping>
    
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>FreeMarker MVC Views</web-resource-name>
            <url-pattern>*.ftl</url-pattern>
        </web-resource-collection>
        <auth-constraint>
        </auth-constraint>
    </security-constraint>
        
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

In the web.xml file, we set up and configure the FreeMarker servlet. Refer to the FreeMarker documentation for the explanation of each of the options.

Car.java
package com.zetcode.bean;

public class Car {

    private String name;
    private int price;

    public Car() {
    }

    public Car(String name, int price) {

        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

This is the Car bean having basic data about a car object.

MyServlet.java
package com.zetcode.web;

import com.zetcode.bean.Car;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "MyServlet", urlPatterns = {"/"})
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=UTF-8");

        Car c1 = new Car("Audi", 52642);
        Car c2 = new Car("Volvo", 29000);
        Car c3 = new Car("Skoda", 9000);

        List<Car> cars = new ArrayList<>();
        cars.add(c1);
        cars.add(c2);
        cars.add(c3);

        request.setAttribute("cars", cars);

        request.getRequestDispatcher("/index.ftl").forward(request, response);

    }
}

We set up the servlet and dispatch to the template file. We create an ArrayList of cars and set it to the request.

index.ftl
<!DOCTYPE html>
<html>
    <head>
        <title>FreeMarker test</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
    <body>
        
        <table>
            <tr>
                <th>Name</th>  
                <th>Price</th>
            </tr>        

            <#list cars as car>
                <tr>
                    <td>${car.name}</td> 
                    <td>${car.price}</td>
                </tr>
            </#list>        
        </table>                
    </body>
</html>

The index.ftl file is located in the src/main/webapp directory. With the #list directive, we show all the elements of the cars collection.

FreeMarker servlet example
Figure: FreeMarker servlet example

We show the application output in the Opera web browser. The built-in Tomcat in NetBeans operates on the 8084 port.

FreeMarker with Spark

Spark is a simple and lightweight Java web framework built for rapid development. Spark runs on an embedded Jetty web server by default, but can be configured to run on other webservers. To integrate FreeMarker with Spark, we use spark-template-freemarker, the Freemarker Template Engine implementation for Spark.

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           └── SparkFreeMarker.java
    │   └── resources
    │       └── views
    │           └── hello.ftl
    └── test
        └── java

This is the project structure.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
        http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>SparkFreeMarker</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
        
        <dependency>
            <groupId>com.sparkjava</groupId>
            <artifactId>spark-template-freemarker</artifactId>
            <version>2.5.5</version>
        </dependency>
        
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.24</version>
        </dependency>
        
        <dependency>
            <groupId>com.sparkjava</groupId>
            <artifactId>spark-core</artifactId>
            <version>2.5.5</version>
        </dependency>

    </dependencies>
    
    <build>
        <finalName>SparkFreeMarker</finalName>
    
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>com.zetcode.SparkFreeMarker</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins> 
    
    </build>
    
</project>

The pom.xml file contains dependencies for Spark modules and FreeMarker.

SparkFreeMarker.java
package com.zetcode;

import freemarker.template.Configuration;
import freemarker.template.Version;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import spark.ModelAndView;
import spark.Request;
import spark.Response;
import static spark.Spark.get;
import spark.template.freemarker.FreeMarkerEngine;

public class SparkFreeMarker {

    public static void main(String[] args) throws IOException {
        
        Configuration conf = new Configuration(new Version(2, 3, 23));
        conf.setClassForTemplateLoading(SparkFreeMarker.class, "/views");

        get("/hello/:name", SparkFreeMarker::message, new FreeMarkerEngine(conf));
    }

    public static ModelAndView message(Request req, Response res) {

        Map<String, Object> params = new HashMap<>();
        params.put("name", req.params(":name"));
        return new ModelAndView(params, "hello.ftl");
    }
}

We set up the same application for FreeMarker.

Configuration conf = new Configuration(new Version(2, 3, 23));
conf.setClassForTemplateLoading(SparkFreeMarker.class, "/views");

We configure FreeMarker with the Configuration class. The template files are going to be placed into the views directory, which must be located on the classpath.

get("/hello/:name", SparkFreeMarker::message, new FreeMarkerEngine(conf));

The FreeMarkerEngine is passed to the get() method.

public static ModelAndView message(Request req, Response res) {

    Map<String, Object> params = new HashMap<>();
    params.put("name", req.params(":name"));
    return new ModelAndView(params, "hello.ftl");
}

The ModelAndView is used to set the name of the view and the model object to be rendered.

hello.ftl
<!DOCTYPE html>
<html>
    <head>
        <title>Home page</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <p>Hello ${name}</p>
    </body>
</html>

This is the hello.ftl template file; it refers to the name variable which was passed with the ModelAndView object.

$ mvn package

We build the project with mvn package command.

$ java -jar target/SparkFreeMarkejar-with-dependencies.jar 

We run the program. The maven-assembly-plugin allows to create an executable JAR with all dependencies.

$ curl localhost:4567/hello/Thomas
<!DOCTYPE html>
<html>
    <head>
        <title>Home page</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <p>Hello Thomas</p>
    </body>
</html>

This is the output.

FreeMarker with Spring

Spring is a popular Java application framework. Spring Boot is a product of an effort to create stand-alone, production-grade Spring based applications with minimal effort.

Classic Spring application

In the following example, we integrate FreeMarker into a classic Spring application.

├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── service
    │   │           │   ├── IVersionService.java
    │   │           │   └── VersionService.java
    │   │           └── web
    │   │               └── MyController.java
    │   ├── resources
    │   │   └── my.properties
    │   └── webapp
    │       ├── META-INF
    │       │   └── context.xml
    │       └── WEB-INF
    │           ├── spring-servlet.xml
    │           ├── views
    │           │   ├── index.ftl
    │           │   └── version.ftl
    │           └── web.xml
    └── test
        └── java

This is the project structure of our classic Spring application.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zetcode</groupId>
    <artifactId>ClassicSpringFreeMarker</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>ClassicSpringFreeMarker</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>    
    </properties>
    
    <dependencies>
        
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.23</version>
        </dependency>         
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.3.6.RELEASE</version>
        </dependency>           
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.6.RELEASE</version>
        </dependency>
 
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.3.6.RELEASE</version>
        </dependency>
     
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.6.RELEASE</version>
        </dependency>  
    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>

The pom.xml file contains dependencies for Spring modules and FreeMarker.

web.xml
<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
     
    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>    
    
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

In the web.xml file, we define Spring DispatcherServlet, which is the central dispatcher for HTTP request handlers.

spring-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
 
    <context:component-scan base-package="com.zetcode"/>
    <context:property-placeholder location="classpath*:my.properties"/>
    
    <!--freemarker config--> 
    <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPath" value="/WEB-INF/views/"/>
    </bean>
	
    <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="cache" value="true"/>
        <property name="prefix" value=""/>
        <property name="suffix" value=".ftl"/>
    </bean>  
 
</beans>

In the spring servlet context XML file we define two beans: freemarkerConfig and viewResolver. These are configuration beans for FreeMarker. The spring-servlet.xml is located in code>WEB-INF.

<context:component-scan base-package="com.zetcode" />

Spring will scan for components in the com.zetcode package.

<context:property-placeholder location="classpath*:my.properties"/>

The <context:property-placeholder> element registers a PropertySourcesPlaceholderConfigurer, which allows to set properties with the @Value annotation. The location attribute tells where to look for properties.

my.properties
app.version: "1.0"

In the my.properties file we have one key/value pair. The value is the version of the application. The file is located in the usr/main/resources directory.

IVersionService.java
package com.zetcode.service;

public interface IVersionService {
    
    public String getVersion();
}

The IVersionService interface contains one method contract: getVersion().

VersionService.java
package com.zetcode.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class VersionService implements IVersionService {

    @Value("${app.version}")
    private String appVersion;

    @Override
    public String getVersion() {

        return appVersion;
    }
}

The VersionService returns the version of the application.

@Value("${app.version}")
private String appVersion;

The value from the app.version key, located in the my.properties file, is injected into the appVersion attribute.

MyController.java
package com.zetcode.web;

import com.zetcode.service.VersionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MyController {

    @Autowired
    private VersionService versionService;

    @RequestMapping("/index")
    public String index(Model model) {

        return "index";
    }

    @RequestMapping(value = "/version", method = RequestMethod.GET)
    public ModelAndView version() {

        String version = versionService.getVersion();

        ModelAndView model = new ModelAndView("version");
        model.addObject("version", version);

        return model;
    }
}

This is the controller class.

@RequestMapping(value = "/version", method = RequestMethod.GET)
public ModelAndView version() {

    String version = versionService.getVersion();

    ModelAndView model = new ModelAndView("version");
    model.addObject("version", version);

    return model;
}

In the version method, which is called when a request with /version URL arrives, we call the VersionService's getVersion() method and pass the value into the ModelAndView. The processing is dispatched to the version.ftl template file, where the version value is inserted.

index.ftl
<!DOCTYPE html>
<html>
    <head>
        <title>Home page</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <p>Show application version <a href="version.html">version</a></p>
    </body>
</html>

This is the index.ftl file. It has an link which sends a request to the server to get the version of the application.

version.ftl
<!DOCTYPE html>
<html>
    <head>
        <title>Home page</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <p>Application version: ${version}</p>
    </body>
</html>

The version.ftl template file is used to build the server response to the client.

Spring Boot web application using FreeMarker

In the next application, we integrate FreeMarker into a Spring Boot web application.

Spring Boot web project structure in NetBeans
Figure: Spring Boot web project structure in NetBeans

This is the project structure of the Spring Boot web application using FreeMarker in NetBeans. Note that we are creating a Java SE Maven application in NetBeans, not a Java web Maven application. This is because we have Tomcat embedded into our JAR file.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>SpringBootFreemarkerEx</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.2.RELEASE</version>
    </parent>

    <dependencies>
      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>        
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>       
</project>

This is the Maven build file. It includes dependencies for Spring Boot and FreeMarker. There is no need to configure the FreeMarker in Spring Boot. Upon finding the FreeMarker dependency in the POM file, Spring Boot automatically takes care of the configuration. The spring-boot-maven-plugin creates an executable JAR with an embedded container (Tomcat by default).

SpringBootClient.java
package com.zetcode.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

The Application sets up the Spring Boot application. The @SpringBootApplication annotation does three things: 1) defines the class as a configuration class, 2) enables auto-configuration, 3) enables component scanning.

MyController.java
package com.zetcode.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class MyController {
      
    @GetMapping("/")
    public String index(Model model) {
        return "index";
    }    
    
    @GetMapping("/hello")
    public String hello(Model model, @RequestParam(value="msg", required=false, 
            defaultValue="Freemarker") String msg) {
        
        model.addAttribute("message", msg);
        return "hello";
    }    
}

This is the controller class for the Spring Boot web application. The controller has two mappings. The first mapping resolves to the index.ftl file and the second mapping to the hello.ftl file.

index.ftl
<!DOCTYPE html>
<html>
    <head>
        <title>Spring Boot Form</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <form action="/hello" method="get">
            <p>Message: <input type="text" name="msg"></p>
            <p>
                <input type="submit" value="Submit"> 
                <input type="reset" value="Reset">
            </p>
        </form>
    </body>
</html>

This is the index.ftl file. It has an HTML form which sends a message to the server.

hello.ftl
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Freemarker example</title>
</head>
<body>
    <p>${message}<p>
</body>
</html>

The server responds with a message back to the client. The response is created from the hello.ftl template file.

Spring Boot web example
Figure: Spring Boot web example

The Spring Boot starts an embedded Tomcat server, listening on port 8080.

This tutorial was dedicated to the FreeMarker template engine. You might also be interested in the related tutorials: Servlet FreeMarker JDBCTemplate tutorial, Jtwig tutorial, Java tutorial, Introduction to Play, Introduction to Spark, or Introduction to Stripes.