A Review of Template Engines: What Next After Velocity?
With Velocity deprecated, let's take a walk through some other popular template engines to see what works best where.
Join the DZone community and get the full member experience.
Join For FreeLet’s dive into the Template engine problems of MVC based frameworks.
This post will shed light on the mystery of different templating possibilities supported by the Spring Boot framework.
Spring Boot has become very popular because of its configuration abilities and full support of Spring-based applications. Spring Boot follows the simple motto of “Just run” — wait a sec, no, it’s not Nike. In the age of microservices and dissecting monoliths, this motto has quite a nice impact on the desired prototyping application.
I don’t think there is any need to go much deeper into the Model View Controller (MVC) design pattern issue because there are many other articles that do just that. My main intent is to review the setups of different Java template engines for Spring-based applications. How can such a question even arise?
Well, the reason is that the Velocity Engine has been deprecated for a while, and a lot of developers around the world need to find well-fitting alternatives.
Let’s begin and define the set for our test. We will compare the following engines:
I have not included JSP Engine because JSPs are mature technologies and have been around since the early days, which means that many articles have been already written about it. The fact that JSPs are really hard to beat in raw speed is true, but that's not in the focus here.
First, let's prepare a MvcConfiguration class, which extends WebMvcConfigurerAdapter:
@Configuration
@EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
...
The mentioned MvcConfiguration class must define @BeanViewResolver, which can negotiate about the proper request ViewResolver.
@Bean(name = "viewResolver")
public ViewResolver contentNegotiatingViewResolver( ContentNegotiationManager manager) {
List resolvers = ...
Each of the mentioned template engines have, under the folder webapp, its own directory dedicated only to it. Such a directory (velocity, freemarker, thymeleaf, and pebble) contains only engine-related files.
And here is the deprecated engine that has been widely used over last several years.
Apache Velocity
The Apache Velocity Template Engine is used for comparison and to make testing other the other three alternatives (FreeMarker, Thymeleaf, and Pebble) a little bit simpler. Apache Velocity is one of the Jakarta projects. Each of the Velocity templates is processed, but not compiled to Java, which supports better code separation.
The following code snippets configure Spring Boot ViewResolver and enable Velocity usage:
@Bean(name = "velocityViewResolver")
public ViewResolver getVelocityViewResolver() {
VelocityViewResolver resolver = new VelocityViewResolver();
resolver.setSuffix(".vm");
resolver.setCache(true);
return resolver;
}
Having configured ViewResolver, we need to add it to the contentNegotiatingViewResolver@Bean, which gives us the access to the ContentNegotiationManager. The ContentNegotiationManager provides look-up methods to the file extensions based on MediaType. In our example case, it will be used for a specific engine file suffix search:
@Bean(name = "viewResolver")
public ViewResolver contentNegotiatingViewResolver( ContentNegotiationManager manager) {
List resolvers =
Arrays.asList(getVelocityViewResolver(),
...
Inside the directory webapp, we create the directory velocity and a simple velocity template. We call the file test.vm and it contains the following content:
<html lang="en">
<head>
<title>Test Velocity</title>
</head>
<body>
<h2>This is $test</h2>
</body>
</html>
We are almost done. There is only one more important thing. For setting up specific Spring Boot application properties, we'll need to use a configuration file called application.properties — located inside the project's resources folder. For Velocity, it will contain the loader path setup (You can customize it).
spring.velocity.resourceLoaderPath=/velocity/
Congratulations!
The deprecated template engine Velocity is up and running, but this is not all what we want to achieve, so we'll continue with the next alternative.
Apache FreeMarker
Our first candidate is FreeMarker. FreeMarker is currently coming from the Apache project's incubator, supported by the Apache Software Foundation (ASF). ASF puts its effort to support FreeMarker development, which is a very good sign for a long life. One more reason may be that FreeMarker is widely used across Apache projects — a good example is the newly accepted NetBeans!
Let’s add FM support to our sample project by configuring ViewResolver in the following way:
@Bean(name = "freeMarkerViewResolver")
public ViewResolver getFreeMakerViewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setSuffix(".ftl");
resolver.setCache(true);
return resolver;
}
We also need to properly add FreeMarker ViewResolver to the ContentNegotiationManager inside the MvcConfiguration@Bean:
@Bean(name = "viewResolver")
public ViewResolver contentNegotiatingViewResolver( ContentNegotiationManager manager) {
List resolvers =
Arrays.asList(getVelocityViewResolver(),
getFreeMakerViewResolver(),
...
Now the sample application is ready for the simple FreeMarker templates. Inside the webapp folder, we a create new folder called freemarker and add the following two files: First...
Index.ftl
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test applicaiton</title>
</head>
<body>
<h2>This is test application Main sample site</h2>
</body>
</html>
... and magic.ftl, which will contain simple FM tags:
Magic.ftl
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Magic Spell: ${word}!</title>
</head>
<body>
<h2>MagicHappens by word: ${word}!</h2>
</body>
</html>
But hold on, that's not all. We cannot forget to properly add a configuration inside the application.properties file:
spring.freemarker.templateLoaderPath=/freemarker/
Now we have FreeMarker up and running inside our sample project! Well done, and we can move to the next one.
Pebble Template Engine
It’s a new player on the market. It promises useful inheritance features and "easy-to-read" syntax, but those are beyond the scope of this article. This article is focused on ViewResolver's configuration and having it up and running. First, we again need to configure ViewResolver properly. With Pebble, it's slightly more complicated because the result of the configuration is extremely closely connected to the Servlet config itself. So, we go back into @BeanMvcConfiguration and add:
@Bean(name="pebbleViewResolver")
public ViewResolver getPebbleViewResolver(){
PebbleViewResolver resolver = new PebbleViewResolver();
resolver.setPrefix("/pebble/");
resolver.setSuffix(".html");
resolver.setPebbleEngine(pebbleEngine());
return resolver;
}
As previously mentioned, that template may support configuration by application.properties. Unfortunately, this is currently not the case of Pebble. We need to configure it all manually, and we need to define more Pebble-related @Beans
@Bean
public PebbleEngine pebbleEngine() {
return new PebbleEngine.Builder()
.loader(this.templatePebbleLoader())
.extension(pebbleSpringExtension())
.build();
}
@Bean
public Loader templatePebbleLoader(){
return new ServletLoader(servletContext);
}
@Bean
public SpringExtension pebbleSpringExtension() {
return new SpringExtension();
}
As you can see, the templatePebbleLoader @Bean requires direct access to the ServletContext, which needs to be injected into the configuration @Bean.
@Autowired
private ServletContext servletContext;
...
It also means that by doing this, Pebble takes over the created servlet and will play your default choice when any other exists. This may not be bad, but when you want to use Pebble and, for example, Thymeleaf together, you need to do slightly more Spring hacking.
Now we have prepared the Pebble configuration, so let’s create a new Pebble folder under the webapp and add a new template file — pebble.html
<html>
<head>
<title>{{ pebble }}</title>
</head>
<body>
{{ pebble }}
</body>
</html>
Now we are finished! Pebble is up and running and we can go directly to the last option.
Thymeleaf Template Engine
Thymeleaf presents itself as the ideal choice for HTML5 JVM web development. It might be true, but again, that's beyond the scope of this article, though you can examine this claim by using my example project over at my GitHub account. Thymeleaf has better Spring support than Pebble. This allows us to use the application.properties file for its configuration and add Thymeleaf setup options there:
spring.thymeleaf.prefix=/thymeleaf/
spring.thymeleaf.suffix=.html
But the rest is very similar to Pebble.
@Bean(name = "thymeleafViewResolver")
public ViewResolver getThymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(getThymeleafTemplateEngine());
resolver.setCache(true);
return resolver;
}
Thymeleaf similarly takes control over any new Servlet, as you can see in MvcConfiguration@Bean.
@Bean(name ="thymeleafTemplateEngine")
public SpringTemplateEngine getThymeleafTemplateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(getThymeleafTemplateResolver());
return templateEngine;
}
@Bean(name ="thymeleafTemplateResolver")
public ServletContextTemplateResolver getThymeleafTemplateResolver() {
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/thymeleaf/");
templateResolver.setSuffix(".htm");
return templateResolver;
}
Now it’s time to add ViewResolver to the content negotiation configuration:
@Bean(name = "viewResolver")
public ViewResolver contentNegotiatingViewResolver( ContentNegotiationManager manager) {
List resolvers =
Arrays.asList(getVelocityViewResolver(),
getFreeMakerViewResolver(),
// getPebbleViewResolver()
getThymeleafViewResolver()
);
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setViewResolvers(resolvers);
resolver.setContentNegotiationManager(manager);
return resolver;
}
...
As our last step, we will create, under the webapp folder, a new folder called thymeleaf and add the thyme.html file there:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Thymeleaf</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'HERE IS, ' + ${thyme} + '!'" />
</body>
</htm>
Congratulations! You have successfully configured all four Spring Boot supported template engines.
At the end of the configuration section, it is important to point out that each of the engines has assigned its own @Controller, which is responsible for proper output generation:
Velocity Controller
@Controller
public class VelocityHelloController {
@RequestMapping(value = "/velocity")
public String test(Model model){
System.out.println("Test");
model.addAttribute("test", "Here is Velocity");
return "test";
}
}
FrameMarker
@Controller
public class FMHelloController {
@RequestMapping("/")
public String index(){
System.out.println("INDEX");
return "index";
}
@RequestMapping("/magic")
public String magic(Model model, @RequestParam(value = "word", required=false, defaultValue="MagicHappens") String word) {
System.out.println("MAGIC");
model.addAttribute("word", word);
return "magic";
}
}
Pebble
@Controller
public class PebbleHelloController {
@RequestMapping(value = "/pebble")
public String something(Model model){
System.out.println("Pebble");
model.addAttribute("pebble", "The Pebble");
return "pebble";
}
}
Thymeleaf
@Controller
public class TLHelloController {
@RequestMapping(value = "/thyme")
public String something(Model model){
System.out.println("Thymeleaf");
model.addAttribute("thyme", "The Thymeleaf");
return "thyme";
}
}
Experiences and Summary
Now is the right time to write a few final words about the possibilities we covered today. I don’t want to highlight any of those tested choices as the best replacement to the deprecated Velocity template engine, but from my configuration experiences and Spring framework support, I’d choose FrameMarker. By Choosing FreeMarker I won’t be limited to using Velocity and any other options in parallel, but as has been mentioned before, making the right choice is beyond the scope of this article.
I have created a sample gradle project that imports all the temple engine starters. This setup can be found inside the configuration file build.gradle
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-freemarker:$springBootVersion")
compile("org.springframework.boot:spring-boot-starter-velocity:$springBootVersion")
compile("org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion")
compile("com.mitchellbosecke:pebble-spring-boot-starter:$pebbleVersion")
testCompile "junit:junit:${junitVersion}"
}
Enjoy the sample project in testing your choice!
used versions:
Java JDK: 1.8.111
Spring-Boot: 1.4.1.RELEASE
Pebble: 2.2.3
Published at DZone with permission of Miro Wengner. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments