0
# Template Engine Configuration
1
2
Spring Boot's template engine configuration provides comprehensive auto-configuration for popular template engines including Thymeleaf, FreeMarker, Mustache, and Groovy templates.
3
4
## Capabilities
5
6
### Thymeleaf Configuration
7
8
Auto-configuration for Thymeleaf template engine with Spring integration.
9
10
```java { .api }
11
/**
12
* Auto-configuration for Thymeleaf template engine
13
* Configures Thymeleaf for web applications with Spring MVC and WebFlux support
14
*/
15
@AutoConfiguration
16
@ConditionalOnClass({TemplateMode.class, SpringTemplateEngine.class})
17
@ConditionalOnWebApplication
18
@AutoConfigureAfter({WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class})
19
@EnableConfigurationProperties(ThymeleafProperties.class)
20
public class ThymeleafAutoConfiguration {
21
22
/**
23
* Thymeleaf template engine with Spring integration
24
*/
25
@Bean
26
@ConditionalOnMissingBean(SpringTemplateEngine.class)
27
public SpringTemplateEngine templateEngine(ThymeleafProperties properties,
28
ObjectProvider<ITemplateResolver> templateResolvers,
29
ObjectProvider<IDialect> dialects) {
30
SpringTemplateEngine engine = new SpringTemplateEngine();
31
for (ITemplateResolver templateResolver : templateResolvers.orderedStream().collect(Collectors.toList())) {
32
engine.addTemplateResolver(templateResolver);
33
}
34
for (IDialect dialect : dialects.orderedStream().collect(Collectors.toList())) {
35
engine.addDialect(dialect);
36
}
37
return engine;
38
}
39
40
/**
41
* Thymeleaf template resolver for classpath templates
42
*/
43
@Bean
44
@ConditionalOnMissingBean(name = "defaultTemplateResolver")
45
public ITemplateResolver defaultTemplateResolver(ThymeleafProperties properties,
46
ApplicationContext applicationContext) {
47
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
48
resolver.setApplicationContext(applicationContext);
49
resolver.setPrefix(properties.getPrefix());
50
resolver.setSuffix(properties.getSuffix());
51
resolver.setTemplateMode(properties.getMode());
52
if (properties.getEncoding() != null) {
53
resolver.setCharacterEncoding(properties.getEncoding().name());
54
}
55
resolver.setCacheable(properties.isCache());
56
Integer order = properties.getTemplateResolverOrder();
57
if (order != null) {
58
resolver.setOrder(order);
59
}
60
resolver.setCheckExistence(properties.isCheckTemplate());
61
return resolver;
62
}
63
}
64
65
/**
66
* Thymeleaf configuration properties
67
*/
68
@ConfigurationProperties(prefix = "spring.thymeleaf")
69
public class ThymeleafProperties {
70
71
/**
72
* Templates encoding
73
*/
74
private Charset encoding = StandardCharsets.UTF_8;
75
76
/**
77
* Whether to check that the template exists before rendering it
78
*/
79
private boolean checkTemplate = true;
80
81
/**
82
* Whether to check that the templates location exists
83
*/
84
private boolean checkTemplateLocation = true;
85
86
/**
87
* Prefix that gets prepended to view names when building a URL
88
*/
89
private String prefix = "classpath:/templates/";
90
91
/**
92
* Suffix that gets appended to view names when building a URL
93
*/
94
private String suffix = ".html";
95
96
/**
97
* Template mode to be applied to templates
98
*/
99
private String mode = "HTML";
100
101
/**
102
* Whether to enable template caching
103
*/
104
private boolean cache = true;
105
106
/**
107
* Order of the template resolver in the chain
108
*/
109
private Integer templateResolverOrder;
110
111
/**
112
* Comma-separated list of view names that can be resolved
113
*/
114
private String[] viewNames;
115
116
/**
117
* Comma-separated list of view names that should be excluded from resolution
118
*/
119
private String[] excludedViewNames;
120
121
/**
122
* Whether to enable MVC Thymeleaf view resolution
123
*/
124
private boolean enableSpringElCompiler = false;
125
126
/**
127
* Whether to render the partial mode in case of AJAX requests
128
*/
129
private boolean renderHiddenMarkersBeforeCheckboxes = false;
130
131
// Getters and setters
132
public String getPrefix() { return this.prefix; }
133
public void setPrefix(String prefix) { this.prefix = prefix; }
134
public String getSuffix() { return this.suffix; }
135
public void setSuffix(String suffix) { this.suffix = suffix; }
136
public String getMode() { return this.mode; }
137
public void setMode(String mode) { this.mode = mode; }
138
public boolean isCache() { return this.cache; }
139
public void setCache(boolean cache) { this.cache = cache; }
140
public Charset getEncoding() { return this.encoding; }
141
public void setEncoding(Charset encoding) { this.encoding = encoding; }
142
}
143
```
144
145
### FreeMarker Configuration
146
147
Auto-configuration for FreeMarker template engine.
148
149
```java { .api }
150
/**
151
* Auto-configuration for FreeMarker template engine
152
* Configures FreeMarker for web applications and email templates
153
*/
154
@AutoConfiguration
155
@ConditionalOnClass({freemarker.template.Configuration.class, FreeMarkerConfigurationFactory.class})
156
@AutoConfigureAfter({WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class})
157
@EnableConfigurationProperties(FreeMarkerProperties.class)
158
public class FreeMarkerAutoConfiguration {
159
160
/**
161
* FreeMarker configuration
162
*/
163
@Bean
164
@ConditionalOnMissingBean
165
public FreeMarkerConfigurationFactoryBean freeMarkerConfiguration(FreeMarkerProperties properties) {
166
FreeMarkerConfigurationFactoryBean freeMarkerConfigurationFactoryBean = new FreeMarkerConfigurationFactoryBean();
167
applyProperties(freeMarkerConfigurationFactoryBean, properties);
168
return freeMarkerConfigurationFactoryBean;
169
}
170
171
/**
172
* FreeMarker configuration for web applications
173
*/
174
@Configuration(proxyBeanMethods = false)
175
@ConditionalOnWebApplication(type = Type.SERVLET)
176
@ConditionalOnClass({Servlet.class, FreeMarkerConfigurer.class})
177
protected static class FreeMarkerWebConfiguration {
178
179
@Bean
180
@ConditionalOnMissingBean(FreeMarkerConfig.class)
181
public FreeMarkerConfigurer freeMarkerConfigurer(FreeMarkerProperties properties) {
182
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
183
applyProperties(configurer, properties);
184
return configurer;
185
}
186
187
@Bean
188
@ConditionalOnMissingBean(name = "freeMarkerViewResolver")
189
@ConditionalOnProperty(name = "spring.freemarker.enabled", matchIfMissing = true)
190
public FreeMarkerViewResolver freeMarkerViewResolver(FreeMarkerProperties properties) {
191
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
192
properties.applyToMvcViewResolver(resolver);
193
return resolver;
194
}
195
}
196
}
197
198
/**
199
* FreeMarker configuration properties
200
*/
201
@ConfigurationProperties(prefix = "spring.freemarker")
202
public class FreeMarkerProperties extends AbstractTemplateViewResolverProperties {
203
204
/**
205
* Default encoding of templates
206
*/
207
public static final String DEFAULT_TEMPLATE_LOADER_PATH = "classpath:/templates/";
208
209
/**
210
* Default suffix for template names
211
*/
212
public static final String DEFAULT_SUFFIX = ".ftlh";
213
214
/**
215
* Comma-separated list of template loader paths
216
*/
217
private String[] templateLoaderPath = new String[]{DEFAULT_TEMPLATE_LOADER_PATH};
218
219
/**
220
* Whether HttpSession attributes are allowed to override controller generated model attributes
221
*/
222
private boolean allowSessionOverride = false;
223
224
/**
225
* Whether to allow RequestContext attributes to override controller generated model attributes
226
*/
227
private boolean allowRequestOverride = false;
228
229
/**
230
* Whether to expose RequestContext to templates for use by Spring's macro library
231
*/
232
private boolean exposeRequestAttributes = false;
233
234
/**
235
* Whether to expose HttpSession attributes to templates
236
*/
237
private boolean exposeSessionAttributes = false;
238
239
/**
240
* Whether to expose all Spring beans to templates
241
*/
242
private boolean exposeSpringMacroHelpers = true;
243
244
/**
245
* Prefer file system access for template loading
246
*/
247
private boolean preferFileSystemAccess = true;
248
249
/**
250
* Template encoding
251
*/
252
private String defaultEncoding = "UTF-8";
253
254
/**
255
* Well-known FreeMarker keys which are passed to FreeMarker's Configuration
256
*/
257
private Map<String, String> settings = new HashMap<>();
258
259
// Getters and setters
260
public String[] getTemplateLoaderPath() { return this.templateLoaderPath; }
261
public void setTemplateLoaderPath(String... templateLoaderPath) { this.templateLoaderPath = templateLoaderPath; }
262
public boolean isPreferFileSystemAccess() { return this.preferFileSystemAccess; }
263
public void setPreferFileSystemAccess(boolean preferFileSystemAccess) { this.preferFileSystemAccess = preferFileSystemAccess; }
264
public Map<String, String> getSettings() { return this.settings; }
265
public void setSettings(Map<String, String> settings) { this.settings = settings; }
266
}
267
```
268
269
### Mustache Configuration
270
271
Auto-configuration for Mustache template engine.
272
273
```java { .api }
274
/**
275
* Auto-configuration for Mustache template engine
276
* Configures Mustache for web applications with logic-less templates
277
*/
278
@AutoConfiguration
279
@ConditionalOnClass({Mustache.class, MustacheViewResolver.class})
280
@AutoConfigureAfter({WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class})
281
@EnableConfigurationProperties(MustacheProperties.class)
282
public class MustacheAutoConfiguration {
283
284
/**
285
* Mustache compiler for template compilation
286
*/
287
@Bean
288
@ConditionalOnMissingBean
289
public Mustache.Compiler mustacheCompiler(MustacheProperties mustache,
290
Environment environment,
291
ObjectProvider<MustacheEnvironmentCollector> collector) {
292
293
MustacheEnvironmentCollector environmentCollector = collector.getIfAvailable(() -> new DefaultMustacheEnvironmentCollector());
294
return Mustache.compiler()
295
.withResolver(new DefaultResolver(mustache.getPrefix()))
296
.withCollector(environmentCollector);
297
}
298
299
/**
300
* Configuration for servlet-based web applications
301
*/
302
@Configuration(proxyBeanMethods = false)
303
@ConditionalOnWebApplication(type = Type.SERVLET)
304
protected static class MustacheWebMvcConfiguration {
305
306
@Bean
307
@ConditionalOnMissingBean(name = "mustacheViewResolver")
308
public MustacheViewResolver mustacheViewResolver(Mustache.Compiler mustacheCompiler,
309
MustacheProperties mustache) {
310
MustacheViewResolver resolver = new MustacheViewResolver(mustacheCompiler);
311
mustache.applyToMvcViewResolver(resolver);
312
return resolver;
313
}
314
}
315
316
/**
317
* Configuration for reactive web applications
318
*/
319
@Configuration(proxyBeanMethods = false)
320
@ConditionalOnWebApplication(type = Type.REACTIVE)
321
protected static class MustacheReactiveConfiguration {
322
323
@Bean
324
@ConditionalOnMissingBean(name = "mustacheViewResolver")
325
public MustacheViewResolver mustacheViewResolver(Mustache.Compiler mustacheCompiler,
326
MustacheProperties mustache) {
327
MustacheViewResolver resolver = new MustacheViewResolver(mustacheCompiler);
328
resolver.setOrder(mustache.getOrder());
329
return resolver;
330
}
331
}
332
}
333
334
/**
335
* Mustache configuration properties
336
*/
337
@ConfigurationProperties(prefix = "spring.mustache")
338
public class MustacheProperties extends AbstractTemplateViewResolverProperties {
339
340
/**
341
* Default prefix for template names
342
*/
343
public static final String DEFAULT_PREFIX = "classpath:/templates/";
344
345
/**
346
* Default suffix for template names
347
*/
348
public static final String DEFAULT_SUFFIX = ".mustache";
349
350
/**
351
* Prefix to apply to template names
352
*/
353
private String prefix = DEFAULT_PREFIX;
354
355
/**
356
* Suffix to apply to template names
357
*/
358
private String suffix = DEFAULT_SUFFIX;
359
360
/**
361
* Whether to check that the template exists before rendering it
362
*/
363
private boolean checkTemplateLocation = true;
364
365
/**
366
* Character set used to read template files
367
*/
368
private Charset charset = StandardCharsets.UTF_8;
369
370
// Getters and setters
371
public String getPrefix() { return this.prefix; }
372
public void setPrefix(String prefix) { this.prefix = prefix; }
373
public String getSuffix() { return this.suffix; }
374
public void setSuffix(String suffix) { this.suffix = suffix; }
375
public boolean isCheckTemplateLocation() { return this.checkTemplateLocation; }
376
public void setCheckTemplateLocation(boolean checkTemplateLocation) { this.checkTemplateLocation = checkTemplateLocation; }
377
public Charset getCharset() { return this.charset; }
378
public void setCharset(Charset charset) { this.charset = charset; }
379
}
380
```
381
382
### Groovy Templates Configuration
383
384
Auto-configuration for Groovy template engine.
385
386
```java { .api }
387
/**
388
* Auto-configuration for Groovy template engine
389
* Configures Groovy templates for web applications
390
*/
391
@AutoConfiguration
392
@ConditionalOnClass({GroovyTemplateEngine.class, GroovyMarkupConfigurer.class})
393
@AutoConfigureAfter({WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class})
394
@EnableConfigurationProperties(GroovyTemplateProperties.class)
395
public class GroovyTemplateAutoConfiguration {
396
397
/**
398
* Configuration for servlet-based web applications
399
*/
400
@Configuration(proxyBeanMethods = false)
401
@ConditionalOnWebApplication(type = Type.SERVLET)
402
@ConditionalOnClass({Servlet.class, GroovyMarkupConfigurer.class})
403
protected static class GroovyMarkupConfiguration {
404
405
@Bean
406
@ConditionalOnMissingBean(GroovyMarkupConfig.class)
407
public GroovyMarkupConfigurer groovyMarkupConfigurer(GroovyTemplateProperties properties) {
408
GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer();
409
configurer.setResourceLoaderPath(properties.getResourceLoaderPath());
410
configurer.setCacheTemplates(properties.isCache());
411
Configuration configuration = new Configuration();
412
configuration.setAutoNewLine(properties.getConfiguration().isAutoNewLine());
413
configuration.setAutoIndent(properties.getConfiguration().isAutoIndent());
414
configurer.setConfiguration(configuration);
415
return configurer;
416
}
417
418
@Bean
419
@ConditionalOnMissingBean(name = "groovyMarkupViewResolver")
420
public GroovyMarkupViewResolver groovyMarkupViewResolver(GroovyTemplateProperties properties) {
421
GroovyMarkupViewResolver resolver = new GroovyMarkupViewResolver();
422
properties.applyToMvcViewResolver(resolver);
423
return resolver;
424
}
425
}
426
}
427
428
/**
429
* Groovy template configuration properties
430
*/
431
@ConfigurationProperties(prefix = "spring.groovy.template")
432
public class GroovyTemplateProperties extends AbstractTemplateViewResolverProperties {
433
434
/**
435
* Default prefix for template names
436
*/
437
public static final String DEFAULT_RESOURCE_LOADER_PATH = "classpath:/templates/";
438
439
/**
440
* Default suffix for template names
441
*/
442
public static final String DEFAULT_SUFFIX = ".tpl";
443
444
/**
445
* Template path
446
*/
447
private String resourceLoaderPath = DEFAULT_RESOURCE_LOADER_PATH;
448
449
/**
450
* Whether to cache templates
451
*/
452
private boolean cache = true;
453
454
/**
455
* Groovy template engine configuration
456
*/
457
private final Configuration configuration = new Configuration();
458
459
// Getters and setters
460
public String getResourceLoaderPath() { return this.resourceLoaderPath; }
461
public void setResourceLoaderPath(String resourceLoaderPath) { this.resourceLoaderPath = resourceLoaderPath; }
462
public boolean isCache() { return this.cache; }
463
public void setCache(boolean cache) { this.cache = cache; }
464
public Configuration getConfiguration() { return this.configuration; }
465
466
/**
467
* Groovy template configuration settings
468
*/
469
public static class Configuration {
470
/**
471
* Whether to auto indent generated markup
472
*/
473
private boolean autoIndent = false;
474
475
/**
476
* Whether to auto generate new lines in generated markup
477
*/
478
private boolean autoNewLine = false;
479
480
// Getters and setters
481
public boolean isAutoIndent() { return this.autoIndent; }
482
public void setAutoIndent(boolean autoIndent) { this.autoIndent = autoIndent; }
483
public boolean isAutoNewLine() { return this.autoNewLine; }
484
public void setAutoNewLine(boolean autoNewLine) { this.autoNewLine = autoNewLine; }
485
}
486
}
487
```
488
489
**Usage Examples:**
490
491
```java
492
// Thymeleaf controller
493
@Controller
494
public class UserController {
495
496
@GetMapping("/users/{id}")
497
public String showUser(@PathVariable Long id, Model model) {
498
User user = userService.findById(id);
499
model.addAttribute("user", user);
500
return "user-profile"; // resolves to /templates/user-profile.html
501
}
502
503
@GetMapping("/users")
504
public String listUsers(Model model) {
505
List<User> users = userService.findAll();
506
model.addAttribute("users", users);
507
return "user-list";
508
}
509
}
510
511
// FreeMarker service for email templates
512
@Service
513
public class EmailTemplateService {
514
515
private final FreeMarkerConfigurationFactoryBean freeMarkerConfig;
516
517
public EmailTemplateService(FreeMarkerConfigurationFactoryBean freeMarkerConfig) {
518
this.freeMarkerConfig = freeMarkerConfig;
519
}
520
521
public String generateWelcomeEmail(User user) throws Exception {
522
Template template = freeMarkerConfig.getObject().getTemplate("welcome-email.ftlh");
523
Map<String, Object> model = new HashMap<>();
524
model.put("user", user);
525
model.put("appName", "MyApp");
526
527
try (StringWriter writer = new StringWriter()) {
528
template.process(model, writer);
529
return writer.toString();
530
}
531
}
532
}
533
534
// Mustache API response
535
@RestController
536
public class ReportController {
537
538
private final Mustache.Compiler mustacheCompiler;
539
540
public ReportController(Mustache.Compiler mustacheCompiler) {
541
this.mustacheCompiler = mustacheCompiler;
542
}
543
544
@GetMapping("/api/reports/{id}/html")
545
public ResponseEntity<String> generateReport(@PathVariable Long id) {
546
Report report = reportService.findById(id);
547
548
String templateContent = "{{#report}}<h1>{{title}}</h1><p>{{description}}</p>{{/report}}";
549
Mustache template = mustacheCompiler.compile(templateContent);
550
551
Map<String, Object> context = Map.of("report", report);
552
String html = template.execute(context);
553
554
return ResponseEntity.ok()
555
.contentType(MediaType.TEXT_HTML)
556
.body(html);
557
}
558
}
559
560
// Properties configuration
561
# application.properties
562
# Thymeleaf
563
spring.thymeleaf.cache=false
564
spring.thymeleaf.prefix=classpath:/templates/
565
spring.thymeleaf.suffix=.html
566
spring.thymeleaf.mode=HTML
567
spring.thymeleaf.encoding=UTF-8
568
569
# FreeMarker
570
spring.freemarker.template-loader-path=classpath:/templates/
571
spring.freemarker.suffix=.ftlh
572
spring.freemarker.settings.template_update_delay=0
573
spring.freemarker.settings.default_encoding=UTF-8
574
spring.freemarker.settings.classic_compatible=true
575
576
# Mustache
577
spring.mustache.prefix=classpath:/templates/
578
spring.mustache.suffix=.mustache
579
spring.mustache.cache=true
580
spring.mustache.charset=UTF-8
581
582
# Groovy Templates
583
spring.groovy.template.cache=true
584
spring.groovy.template.configuration.auto-indent=true
585
spring.groovy.template.configuration.auto-new-line=true
586
```
587
588
## Types
589
590
### Template Configuration Types
591
592
```java { .api }
593
/**
594
* Abstract base class for template view resolver properties
595
*/
596
public abstract class AbstractTemplateViewResolverProperties extends AbstractViewResolverProperties {
597
598
/**
599
* Whether template caching is enabled
600
*/
601
private boolean cache = true;
602
603
/**
604
* Content-Type value used for views
605
*/
606
private MimeType contentType;
607
608
/**
609
* Character encoding used for views
610
*/
611
private Charset charset;
612
613
/**
614
* Name of the RequestContext attribute for all views
615
*/
616
private String requestContextAttribute;
617
618
// Getters and setters
619
public boolean isCache() { return this.cache; }
620
public void setCache(boolean cache) { this.cache = cache; }
621
public MimeType getContentType() { return this.contentType; }
622
public void setContentType(MimeType contentType) { this.contentType = contentType; }
623
}
624
625
/**
626
* Template resolver interface for resolving template names to resources
627
*/
628
public interface ITemplateResolver {
629
/**
630
* Name of this template resolver
631
*/
632
String getName();
633
634
/**
635
* Order in which this resolver will be executed
636
*/
637
Integer getOrder();
638
639
/**
640
* Resolve template for the given context
641
*/
642
TemplateResolution resolveTemplate(IEngineConfiguration configuration,
643
ITemplateContext ownerTemplate,
644
String template,
645
Map<String, Object> templateResolutionAttributes);
646
}
647
648
/**
649
* Customizer for template view resolvers
650
*/
651
@FunctionalInterface
652
public interface TemplateViewResolverCustomizer<T extends AbstractTemplateViewResolver> {
653
/**
654
* Customize the template view resolver
655
* @param viewResolver the view resolver to customize
656
*/
657
void customize(T viewResolver);
658
}
659
```