FreeMarker
last modified January 27, 2024
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 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:
- separation of concerns,
- avoiding repetition of code,
- easier switching between views,
- reusability.
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.
implementation 'org.freemarker:freemarker:2.3.31'
We use this FreeMarker dependency in a Gradle project.
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.
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 { var cfg = new Configuration(new Version("2.3.31")); cfg.setClassForTemplateLoading(FreeMarkerConsoleEx.class, "/views"); cfg.setDefaultEncoding("UTF-8"); Template template = cfg.getTemplate("test.ftlh"); 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.
var cfg = new Configuration(new Version("2.3.31"));
Configuration
is used to set the FreeMarker settings; it takes the
version of the FreeMarker library as a parameter.
cfg.setClassForTemplateLoading(FreeMarkerConsoleEx.class, "/views");
The setClassForTemplateLoading
sets the class whose method will be
used to load templates. The templates are located in the views
subdirectory of src/main/resources
directory.
Template template = cfg.getTemplate("test.ftlh");
With the getTemplate
method, we retrieve the test.ftlh
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.
The message is: ${msg}
The test.ftlh
template file contains one interpolation; it will be
replaced with the generated string.
version '1.0' apply plugin: 'java' apply plugin: 'application' sourceCompatibility = 17 mainClassName = "com.zetcode.FreeMarkerConsoleEx" repositories { mavenCentral() } dependencies { implementation 'org.freemarker:freemarker:2.3.31' }
This is the Gradle build file.
$ gradle run -q The message is: Today is a beautiful day
FreeMarker list directive
The #list
directive lists a collection of data.
The next example produces a list of cars.
package com.zetcode; 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.
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.ArrayList; import java.util.HashMap; import java.util.Map; public class FreeMarkerConsoleEx { public static void main(String[] args) throws IOException, TemplateException { var cfg = new Configuration(new Version("2.3.31")); cfg.setClassForTemplateLoading(FreeMarkerConsoleEx.class, "/views"); cfg.setDefaultEncoding("UTF-8"); Template template = cfg.getTemplate("test.ftlh"); Map<String, Object> templateData = new HashMap<>(); var c1 = new Car("Audi", 52642); var c2 = new Car("Volvo", 29000); var c3 = new Car("Skoda", 9000); var 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<>(); var c1 = new Car("Audi", 52642); var c2 = new Car("Volvo", 29000); var c3 = new Car("Skoda", 9000); var cars = new ArrayList<>(); cars.add(c1); cars.add(c2); cars.add(c3); templateData.put("cars", cars);
Here we create a list of Car
objects and put it into the data
model.
<#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.
$ gradle run -q Audi: 52,642 Volvo: 29,000 Skoda: 9,000
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.
<#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.
<#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
The <#list>
directive is used for traversing a sequence.
<#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.
<#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)
<#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 with Spark
In the following example, we are going to integrate the FreeMarker template engine into our Spark application.
build.gradle src └── main ├── java │ └── com │ └── zetcode │ └── SparkFreeMarkerEx.java └── resources └── views └── hello.ftlh
This is the directory structure of the project.
apply plugin: 'java' apply plugin: 'application' archivesBaseName = "spark-freemarker" version = '1.0' mainClassName = "com.zetcode.SparkFreemarkerEx" repositories { mavenCentral() } dependencies { implementation 'com.sparkjava:spark-core:2.9.4' implementation 'com.sparkjava:spark-template-freemarker:2.7.1' implementation 'org.slf4j:slf4j-simple:1.7.36' }
Here we have the Gradle build file, which includes the
spark-template-freemarker
dependency.
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 SparkFreemarkerEx { public static void main(String[] args) throws IOException { Configuration conf = new Configuration(new Version(2, 3, 26)); conf.setClassForTemplateLoading(SparkFreemarkerEx.class, "/views"); get("/hello/:name/", SparkFreemarkerEx::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.ftlh"); } }
We set up the same application for FreeMarker.
Configuration conf = new Configuration(new Version(2, 3, 26)); conf.setClassForTemplateLoading(SparkFreemarkerEx.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/", SparkFreemarkerEx::message, new FreeMarkerEngine(conf));
The FreeMarkerEngine
is passed to the get
method.
<!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.ftlh
template file; it refers to the name variable
which was passed with the ModelAndView
object.
$ curl localhost:4567/hello/Lucy/ <!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 Lucy</p> </body> </html>
Spring Boot FreeMarker
In the next application, we integrate FreeMarker into a Spring Boot web application.
build.gradle src └── main ├── java │ └── com │ └── zetcode │ ├── Application.java │ └── controller │ └── MyController.java └── resources └── templates ├── hello.ftlh └── index.ftlh
This is the project structure.
plugins { id 'org.springframework.boot' version '2.7.1' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.zetcode' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-freemarker' }
This is the Gradle 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.
package com.zetcode; 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 defines the class as a
configuration class,enables auto-configuration, and enables component scanning.
package com.zetcode.controller; 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.ftlh
file.
<!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.ftlh
file. It has an HTML form which sends a message
to the server.
<!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.ftlh
template file.
$ ./gradlew bootRun
The Spring Boot starts an embedded Tomcat server, listening on port 8080.
Source
In this tutorial we have worked with the FreeMarker template engine.
Author
List all Java tutorials.