Spring Boot提供了大量的模板引擎,包含了FreeMarker,Groovy,Thymeleaf,Velocity和Mustache,Spring Boot中推荐使用Thymeleaf作为模板引擎,因为Thymeleaf提供了完美的Spring MVC的支持。
Thymeleaf是一个java类库,它是一个xml/xhtml/html5的模板引擎,可以作为MVC的Web应用的View层。
Thymeleaf还提供了额外的模块与Spring MVC集成,所以我们可以使用Thymeleaf完全替代JSP。
在Spring Boot中集成Thymeleaf是通过org.springframework.boot.autoconfigure.thymeleaf包对Thymeleaf进行了自动配置,如下:
通过ThymeleafAutoConfiguration类对集成所需的Bean进行自动配置,包括了templateResolver,templateEngine和thymeleafViewResolver的配置,源代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.autoconfigure.thymeleaf;
import com.github.mxab.thymeleaf.extras.dataattribute.dialect.DataAttributeDialect;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import javax.servlet.Servlet;
import nz.net.ultraq.thymeleaf.LayoutDialect;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion;
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
import org.thymeleaf.dialect.IDialect;
import org.thymeleaf.extras.conditionalcomments.dialect.ConditionalCommentsDialect;
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.resourceresolver.SpringResourceResourceResolver;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
@Configuration
@EnableConfigurationProperties({ThymeleafProperties.class})
@ConditionalOnClass({SpringTemplateEngine.class})
@AutoConfigureAfter({WebMvcAutoConfiguration.class})
public class ThymeleafAutoConfiguration {
public ThymeleafAutoConfiguration() {
}
@Configuration
@ConditionalOnWebApplication
protected static class ThymeleafResourceHandlingConfig {
protected ThymeleafResourceHandlingConfig() {
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledResourceChain
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}
}
@Configuration
@ConditionalOnJava(JavaVersion.EIGHT)
@ConditionalOnClass({Java8TimeDialect.class})
protected static class ThymeleafJava8TimeDialect {
protected ThymeleafJava8TimeDialect() {
}
@Bean
@ConditionalOnMissingBean
public Java8TimeDialect java8TimeDialect() {
return new Java8TimeDialect();
}
}
@Configuration
@ConditionalOnClass({SpringSecurityDialect.class})
protected static class ThymeleafSecurityDialectConfiguration {
protected ThymeleafSecurityDialectConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public SpringSecurityDialect securityDialect() {
return new SpringSecurityDialect();
}
}
@Configuration
@ConditionalOnClass({DataAttributeDialect.class})
protected static class DataAttributeDialectConfiguration {
protected DataAttributeDialectConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public DataAttributeDialect dialect() {
return new DataAttributeDialect();
}
}
@Configuration
@ConditionalOnClass(
name = {"nz.net.ultraq.thymeleaf.LayoutDialect"}
)
protected static class ThymeleafWebLayoutConfiguration {
protected ThymeleafWebLayoutConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public LayoutDialect layoutDialect() {
return new LayoutDialect();
}
}
@Configuration
@ConditionalOnMissingBean({SpringTemplateEngine.class})
protected static class ThymeleafDefaultConfiguration {
private final Collection templateResolvers;
private final Collection dialects;
public ThymeleafDefaultConfiguration(Collection templateResolvers, ObjectProvider> dialectsProvider) {
this.templateResolvers = templateResolvers;
this.dialects = (Collection)dialectsProvider.getIfAvailable();
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
Iterator var2 = this.templateResolvers.iterator();
while(var2.hasNext()) {
ITemplateResolver templateResolver = (ITemplateResolver)var2.next();
engine.addTemplateResolver(templateResolver);
}
if (!CollectionUtils.isEmpty(this.dialects)) {
var2 = this.dialects.iterator();
while(var2.hasNext()) {
IDialect dialect = (IDialect)var2.next();
engine.addDialect(dialect);
}
}
return engine;
}
}
@Configuration
@ConditionalOnClass(
name = {"org.thymeleaf.templatemode.TemplateMode"}
)
static class Thymeleaf3Configuration {
Thymeleaf3Configuration() {
}
@Configuration
@ConditionalOnClass({Servlet.class})
@ConditionalOnWebApplication
static class Thymeleaf3ViewResolverConfiguration extends AbstractThymeleafViewResolverConfiguration {
Thymeleaf3ViewResolverConfiguration(ThymeleafProperties properties, SpringTemplateEngine templateEngine) {
super(properties, templateEngine);
}
protected void configureTemplateEngine(ThymeleafViewResolver resolver, SpringTemplateEngine templateEngine) {
Method setTemplateEngine;
try {
setTemplateEngine = ReflectionUtils.findMethod(resolver.getClass(), "setTemplateEngine", new Class[]{Class.forName("org.thymeleaf.ITemplateEngine", true, resolver.getClass().getClassLoader())});
} catch (ClassNotFoundException var5) {
throw new IllegalStateException(var5);
}
ReflectionUtils.invokeMethod(setTemplateEngine, resolver, new Object[]{templateEngine});
}
}
@Configuration
@ConditionalOnMissingBean(
name = {"defaultTemplateResolver"}
)
static class DefaultTemplateResolverConfiguration extends AbstractTemplateResolverConfiguration {
DefaultTemplateResolverConfiguration(ThymeleafProperties properties, ApplicationContext applicationContext) {
super(properties, applicationContext);
}
@Bean
public SpringResourceTemplateResolver defaultTemplateResolver() {
SpringResourceTemplateResolver resolver = super.defaultTemplateResolver();
Method setCheckExistence = ReflectionUtils.findMethod(resolver.getClass(), "setCheckExistence", new Class[]{Boolean.TYPE});
ReflectionUtils.invokeMethod(setCheckExistence, resolver, new Object[]{this.getProperties().isCheckTemplate()});
return resolver;
}
}
}
@Configuration
@ConditionalOnMissingClass({"org.thymeleaf.templatemode.TemplateMode"})
static class Thymeleaf2Configuration {
Thymeleaf2Configuration() {
}
@Configuration
@ConditionalOnClass({ConditionalCommentsDialect.class})
static class ThymeleafConditionalCommentsDialectConfiguration {
ThymeleafConditionalCommentsDialectConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public ConditionalCommentsDialect conditionalCommentsDialect() {
return new ConditionalCommentsDialect();
}
}
@Configuration
@ConditionalOnClass({Servlet.class})
@ConditionalOnWebApplication
static class Thymeleaf2ViewResolverConfiguration extends AbstractThymeleafViewResolverConfiguration {
Thymeleaf2ViewResolverConfiguration(ThymeleafProperties properties, SpringTemplateEngine templateEngine) {
super(properties, templateEngine);
}
protected void configureTemplateEngine(ThymeleafViewResolver resolver, SpringTemplateEngine templateEngine) {
resolver.setTemplateEngine(templateEngine);
}
}
@Configuration
@ConditionalOnMissingBean(
name = {"defaultTemplateResolver"}
)
static class DefaultTemplateResolverConfiguration extends AbstractTemplateResolverConfiguration {
DefaultTemplateResolverConfiguration(ThymeleafProperties properties, ApplicationContext applicationContext) {
super(properties, applicationContext);
}
@Bean
public SpringResourceResourceResolver thymeleafResourceResolver() {
return new SpringResourceResourceResolver();
}
}
}
}
通过ThymeleafProperties来配置Thymeleaf,在application.yml中以spring.thymeleaf开头来配置,通过查看ThymeleafProperties的主要源代码,我们可以看出如何设置属性以及默认配置:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.autoconfigure.thymeleaf;
import java.nio.charset.Charset;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.MimeType;
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");
private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML5";
private Charset encoding;
private MimeType contentType;
private boolean cache;
private Integer templateResolverOrder;
private String[] viewNames;
private String[] excludedViewNames;
private boolean enabled;
public ThymeleafProperties() {
this.encoding = DEFAULT_ENCODING;
this.contentType = DEFAULT_CONTENT_TYPE;
this.cache = true;
this.enabled = true;
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isCheckTemplate() {
return this.checkTemplate;
}
public void setCheckTemplate(boolean checkTemplate) {
this.checkTemplate = checkTemplate;
}
public boolean isCheckTemplateLocation() {
return this.checkTemplateLocation;
}
public void setCheckTemplateLocation(boolean checkTemplateLocation) {
this.checkTemplateLocation = checkTemplateLocation;
}
public String getPrefix() {
return this.prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return this.suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public String getMode() {
return this.mode;
}
public void setMode(String mode) {
this.mode = mode;
}
public Charset getEncoding() {
return this.encoding;
}
public void setEncoding(Charset encoding) {
this.encoding = encoding;
}
public MimeType getContentType() {
return this.contentType;
}
public void setContentType(MimeType contentType) {
this.contentType = contentType;
}
public boolean isCache() {
return this.cache;
}
public void setCache(boolean cache) {
this.cache = cache;
}
public Integer getTemplateResolverOrder() {
return this.templateResolverOrder;
}
public void setTemplateResolverOrder(Integer templateResolverOrder) {
this.templateResolverOrder = templateResolverOrder;
}
public String[] getExcludedViewNames() {
return this.excludedViewNames;
}
public void setExcludedViewNames(String[] excludedViewNames) {
this.excludedViewNames = excludedViewNames;
}
public String[] getViewNames() {
return this.viewNames;
}
public void setViewNames(String[] viewNames) {
this.viewNames = viewNames;
}
}
实战:
1,新建一个spring boot项目,修改pom.xml,代码如下:
4.0.0
com.jack
springboot2web
0.0.1-SNAPSHOT
jar
springboot2web
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
1.5.6.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-thymeleaf
1.5.6.RELEASE
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
上面主要添加了thymeleaf的依赖,然后会自动包含spring-boot-starter-web的依赖
2,示例java bean
此类用来在模板页面展示数据用,包含name属性和age属性:
package com.jack.web.pojo;
/**
* 人的java bean
*/
public class Person {
private String name;
private Integer age;
public Person() {
super();
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
3,脚本样式静态文件
根据默认原则,脚本样式,图片等静态文件应放置在src/main/resources/static下,这里引入了Bootstrap和jQuery,如下:
4,演示页面
根据默认原则,页面应该放置在src/main/resources/templates下。在src/main/resources/templates下新建index.html,代码如下:
Thymeleaf
访问model
列表
-
package com.jack.web.controller;
import com.jack.web.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.List;
@Controller
public class ThymeleafController {
@RequestMapping("/thymeleaf")
public String index(Model model){
Person single = new Person("jack", 11);
List people = new ArrayList();
Person p1 = new Person("xx", 12);
Person p2 = new Person("yy", 13);
Person p3 = new Person("zz", 14);
people.add(p1);
people.add(p2);
people.add(p3);
model.addAttribute("singlePerson",single);
model.addAttribute("people",people);
return "index";
}
}
6,修改application.yml
server:
port: 9090
7,运行
在浏览器输入http://localhost:9090/thymeleaf,效果如图所示:
单击“获得名字”选项,效果如图所示: