目录javascript
SpringBoot系列之Thymeleaf语法简单介绍css
@
Thymeleaf官方文档已经有比较详细的描述,因此本博客只挑部分比较重要的点看一下,还有介绍一下和SpringBoot怎么集成使用html
引用百度百科的模板引擎解释:前端
模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它能够生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。html5
在JavaEE领域有几中比较经常使用的模板引擎,分别是Jsp、Velocity、Freemarker、Thymeleaf,不过对于前端页面渲染效率来讲,jsp其实仍是最快的,Velocity次之。Thymeleaf虽然渲染效率不是很快,可是语法方面是比较轻巧的,Thymeleaf语法比Velocity轻巧,可是渲染效率不如Velocityjava
Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎,可以处理HTML,XML,JavaScript,CSS甚至纯文本。具体参考Thymeleaf官网jquery
官网提供了在线文档也有文件格式的各类文档
git
Thymeleaf适用于以下模板:github
有两种标记模板模式(HTML 和 XML)、三种文本模板模式(TEXT、JAVASCRIPT 和 CSS)和一种无操做模板模式 (RAW)。web
ok,下面给出一些比较重要的知识点
这是很经常使用的text标签,做用是Thymeleaf中设置文本的标签,分为两种,一种是th:text,另一种是th:utext,两种最重要的区别就是会不会对特殊字符进行转义
注意:这里的特殊字符主要指html标签,/n、/t、etc.这些字符是不支持的
写个例子对比一下:
<span th:text="${'Welcome to our <b>fantastic</b> grocery store!'}"></span><br/> <span th:utext="${'Welcome to our <b>fantastic</b> grocery store!'}"></span>
官方文档里有standard Expression Syntax这个章节,介绍的就是标准的表达式语法应用
<link th:href="@{/static/css/public.css}" rel="stylesheet" type="text/css" /> <link th:href="@{/static/css/index.css}" rel="stylesheet" type="text/css" /> <script type="text/javascript" th:src="@{/static/js/jquery-1.3.2.min.js}"></script> <script type="text/javascript" th:src="@{/static/js/html5.js}"></script> <script type="text/javascript" th:src="@{/static/js/popbox.js}"></script>
* Fragment Expressions: ~{...} //片断引用的表达式 eg: `<div th:insert="~{commons :: main}">....</div>`
@{url/}+${id}
${name}
变量值No-Operation: _
All these features can be combined and neste:
'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))
翻译过来意思是,这些语法均可以组合使用,这个章节是Thymeleaf一个重要的基本使用章节,本博客对一些重要的知识点进行摘录同时介绍一下在SpringBoot里如何使用,固然天然没有官方文档详细的,不过官方并无经过中文文档,英文水平很差的话,阅读起来比较困难,固然我也找了一篇国内翻译过来的Thymeleaf中文文档,读者详细的能够参考文档
遍历是Thymeleaf很经常使用的例子,支持的属性值有:
下面仍是给下例子,比较容易理解,以下代码使用th:each,th:each="item : ${items}"
<!--最新上架--> <div class="first-pannel clearfix"> <div class="index-f clearfix"> <h3 class="index-f-head"> 最新上架 <span>天天都有上新,天天都有惊喜</span> </h3> <div class="index-f-body"> <div class="top-sales newProduct"> <ul class="top-sales-list clearfix"> <li class="top-sales-item newProduct" th:each="item : ${items}"> <p class="item-img"> <a th:href="@{'/portal/item/toDetail/'+${item.spuId}+'/'+${item.skuId}}"><img th:src="@{${item.imgPath}}" /></a> </p> <p class="item-buss"><a th:href="@{'/portal/item/toDetail/'+${item.spuId}+'/'+${item.skuId}}"></a></p> <p class="item-name spec"><a th:href="@{'/portal/item/toDetail/'+${item.spuId}+'/'+${item.skuId}}" th:text="${item.itemName}"></a></p> <p class="item-price spec"><em th:text="${item.mPrice}"></em>元</p> </li> </ul> </div> </div> </div> </div> <!--最新上架//-->
在项目开发中常常遇到一些能够重用的页面,这时候就能够Thymeleaf的Template Layout进行公共页面的复用
本博客以官方介绍的复用footer.html页面进行说明
使用步骤:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <body> <div th:fragment="copy"> © 2011 The Good Thymes Virtual Grocery </div> </body> </html>
<div th:insert="~{footer :: copy}"></div>
<div th:insert="footer :: copy"></div> <div th:replace="footer :: copy"></div> <div th:include="footer :: copy"></div
三种引入公共片断的th属性:
效果对比:
<div> <footer> © 2011 The Good Thymes Virtual Grocery </footer> </div> <footer> © 2011 The Good Thymes Virtual Grocery </footer> <div> © 2011 The Good Thymes Virtual Grocery </div
所谓行内写法就是没写在html对应的标签里的写法,直接在页面空白处,用[[....]]或者[(....)]的写法,而后[[....]]和[(....)]的区别其实就等同于th:text和th:utext的区别,一个会进行转义,一个不会转义特殊字符
<span> The message is [[${msg}]] </span> <br/> <span> The message is [(${msg})] </span>
引用尚桂谷老师的概括:
maven配置
由于引入了SpringBoot的parent工程,因此不须要写版本号
<!-- Themeleaf --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
application.yml配置
注意:这里的属性大部分均可以不配置的,由于Springboot的自动配置由于作了不少自动配置,咱们不配置,就使用默认的,不过下面的例子只是给读者了解一下有这些配置
#添加Thymeleaf配置,除了cache在项目没上线前建议关了,其它配置均可以不用配的,本博客只是列举一下有这些配置 thymeleaf: # cache默认开启的,这里能够关了,项目上线以前,项目上线后能够开启 cache: false # 这个prefix能够注释,由于默认就是templates的,您能够改为其它的自定义路径 prefix: classpath:/templates/ suffix: .html mode: HTML5 # 指定一下编码为utf8 encoding: UTF-8 # context-type为text/html,也能够不指定,由于boot能够自动识别 content-type: text/html
ok,Springboot中Thymeleaf使用很是简单,由于Springboot已经为咱们作了不少自动配置,其实,yaml都不须要配置的,直接引入对应的jar,而后就能够直接使用,在resources资源文件夹下面新建一个templates文件夹,全部的html文件都丢在这里,静态资源文件也丢在resources资源文件夹下面
新建一个html文件,而后注意加上<html xmlns:th="http://www.thymeleaf.org">
注意Thymeleaf语法要求比较严格 <meta charset="utf-8" >
,不如这样写是不能够的,必须加上斜杠的,<meta charset="utf-8" />
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <span th:text="${'Welcome to our <b>fantastic</b> grocery store!'}"></span><br/> <span th:utext="${'Welcome to our <b>fantastic</b> grocery store!'}"></span> </div> <span> The message is [[${msg}]] </span> <br/> <span> The message is [(${msg})] </span> </body> </html>
ok,而后为何我说直接引入对应pom配置就能够直接使用了?由于Springboot已经为项目作了不少自动配置,因此本博客简单跟一下源码,了解一下SpringbootThymeleaf的自动配置
SpringBoot的自动配置类在ThymeleafAutoConfiguration里
package org.springframework.boot.autoconfigure.thymeleaf; .... @Configuration(proxyBeanMethods = false)//定义这是一个配置类 @EnableConfigurationProperties(ThymeleafProperties.class)//使用ThymeleafProperties属性类的属性 @ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })//指定TemplateMode、SpringTemplateEngine(模板引擎类)起效的状况,整个配置类才起做用 @AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })//必须在WebMvcAutoConfiguration(SpringMVC自动配置类,这个配置类会加载组装全部的视图解析器)、WebFluxAutoConfiguration类起效后,这个Thymeleaf自动配置类才起效 public class ThymeleafAutoConfiguration { //没有自定义的模板解析器类的状况,使用默认的模板解析器 @Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean(name = "defaultTemplateResolver") static class DefaultTemplateResolverConfiguration { private static final Log logger = LogFactory.getLog(DefaultTemplateResolverConfiguration.class); //Thymeleaf的properties配置 private final ThymeleafProperties properties; private final ApplicationContext applicationContext; DefaultTemplateResolverConfiguration(ThymeleafProperties properties, ApplicationContext applicationContext) { this.properties = properties; this.applicationContext = applicationContext; } //用PostConstruct注解,在依赖注入完成以后,实现类的初始化配置,这个方法主要是检查模板引擎的资源文件路径是否有 @PostConstruct void checkTemplateLocationExists() { boolean checkTemplateLocation = this.properties.isCheckTemplateLocation(); if (checkTemplateLocation) { TemplateLocation location = new TemplateLocation(this.properties.getPrefix()); if (!location.exists(this.applicationContext)) { logger.warn("Cannot find template location: " + location + " (please add some templates or check " + "your Thymeleaf configuration)"); } } } //默认的Thymeleaf资源解析器 @Bean SpringResourceTemplateResolver defaultTemplateResolver() { SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); //资源解析器的全部配置 resolver.setApplicationContext(this.applicationContext); resolver.setPrefix(this.properties.getPrefix()); resolver.setSuffix(this.properties.getSuffix()); resolver.setTemplateMode(this.properties.getMode()); if (this.properties.getEncoding() != null) { resolver.setCharacterEncoding(this.properties.getEncoding().name()); } resolver.setCacheable(this.properties.isCache()); Integer order = this.properties.getTemplateResolverOrder(); if (order != null) { resolver.setOrder(order); } resolver.setCheckExistence(this.properties.isCheckTemplate()); return resolver; } } //又是Thymeleaf的自动配置,自动配置模板引擎SpringTemplateEngine @Configuration(proxyBeanMethods = false) protected static class ThymeleafDefaultConfiguration { @Bean @ConditionalOnMissingBean(ISpringTemplateEngine.class) SpringTemplateEngine templateEngine(ThymeleafProperties properties, ObjectProvider<ITemplateResolver> templateResolvers, ObjectProvider<IDialect> dialects) { SpringTemplateEngine engine = new SpringTemplateEngine(); engine.setEnableSpringELCompiler(properties.isEnableSpringElCompiler()); engine.setRenderHiddenMarkersBeforeCheckboxes(properties.isRenderHiddenMarkersBeforeCheckboxes()); templateResolvers.orderedStream().forEach(engine::addTemplateResolver); dialects.orderedStream().forEach(engine::addDialect); return engine; } } @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true) static class ThymeleafWebMvcConfiguration { @Bean @ConditionalOnEnabledResourceChain @ConditionalOnMissingFilterBean(ResourceUrlEncodingFilter.class) FilterRegistrationBean<ResourceUrlEncodingFilter> resourceUrlEncodingFilter() { FilterRegistrationBean<ResourceUrlEncodingFilter> registration = new FilterRegistrationBean<>( new ResourceUrlEncodingFilter()); registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ERROR); return registration; } //比较重要的视图解析器配置 @Configuration(proxyBeanMethods = false) static class ThymeleafViewResolverConfiguration { @Bean @ConditionalOnMissingBean(name = "thymeleafViewResolver") ThymeleafViewResolver thymeleafViewResolver(ThymeleafProperties properties, SpringTemplateEngine templateEngine) { ThymeleafViewResolver resolver = new ThymeleafViewResolver(); //设置模板引擎 resolver.setTemplateEngine(templateEngine); //字符编码设置 resolver.setCharacterEncoding(properties.getEncoding().name()); resolver.setContentType( appendCharset(properties.getServlet().getContentType(), resolver.getCharacterEncoding())); resolver.setProducePartialOutputWhileProcessing( properties.getServlet().isProducePartialOutputWhileProcessing()); resolver.setExcludedViewNames(properties.getExcludedViewNames()); resolver.setViewNames(properties.getViewNames()); // This resolver acts as a fallback resolver (e.g. like a // InternalResourceViewResolver) so it needs to have low precedence resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5); //Thymeleaf缓存 resolver.setCache(properties.isCache()); return resolver; } private String appendCharset(MimeType type, String charset) { if (type.getCharset() != null) { return type.toString(); } LinkedHashMap<String, String> parameters = new LinkedHashMap<>(); parameters.put("charset", charset); parameters.putAll(type.getParameters()); return new MimeType(type, parameters).toString(); } } } @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.REACTIVE) @ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true) static class ThymeleafReactiveConfiguration { @Bean @ConditionalOnMissingBean(ISpringWebFluxTemplateEngine.class) SpringWebFluxTemplateEngine templateEngine(ThymeleafProperties properties, ObjectProvider<ITemplateResolver> templateResolvers, ObjectProvider<IDialect> dialects) { SpringWebFluxTemplateEngine engine = new SpringWebFluxTemplateEngine(); engine.setEnableSpringELCompiler(properties.isEnableSpringElCompiler()); engine.setRenderHiddenMarkersBeforeCheckboxes(properties.isRenderHiddenMarkersBeforeCheckboxes()); templateResolvers.orderedStream().forEach(engine::addTemplateResolver); dialects.orderedStream().forEach(engine::addDialect); return engine; } } //ThymeleafWebFluxConfiguration自动配置 @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.REACTIVE) @ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true) static class ThymeleafWebFluxConfiguration { @Bean @ConditionalOnMissingBean(name = "thymeleafReactiveViewResolver") ThymeleafReactiveViewResolver thymeleafViewResolver(ISpringWebFluxTemplateEngine templateEngine, ThymeleafProperties properties) { ThymeleafReactiveViewResolver resolver = new ThymeleafReactiveViewResolver(); resolver.setTemplateEngine(templateEngine); mapProperties(properties, resolver); mapReactiveProperties(properties.getReactive(), resolver); // This resolver acts as a fallback resolver (e.g. like a // InternalResourceViewResolver) so it needs to have low precedence resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5); return resolver; } private void mapProperties(ThymeleafProperties properties, ThymeleafReactiveViewResolver resolver) { PropertyMapper map = PropertyMapper.get(); map.from(properties::getEncoding).to(resolver::setDefaultCharset); resolver.setExcludedViewNames(properties.getExcludedViewNames()); resolver.setViewNames(properties.getViewNames()); } private void mapReactiveProperties(Reactive properties, ThymeleafReactiveViewResolver resolver) { PropertyMapper map = PropertyMapper.get(); map.from(properties::getMediaTypes).whenNonNull().to(resolver::setSupportedMediaTypes); map.from(properties::getMaxChunkSize).asInt(DataSize::toBytes).when((size) -> size > 0) .to(resolver::setResponseMaxChunkSizeBytes); map.from(properties::getFullModeViewNames).to(resolver::setFullModeViewNames); map.from(properties::getChunkedModeViewNames).to(resolver::setChunkedModeViewNames); } } @Configuration(proxyBeanMethods = false) @ConditionalOnClass(LayoutDialect.class) static class ThymeleafWebLayoutConfiguration { @Bean @ConditionalOnMissingBean LayoutDialect layoutDialect() { return new LayoutDialect(); } } @Configuration(proxyBeanMethods = false) @ConditionalOnClass(DataAttributeDialect.class) static class DataAttributeDialectConfiguration { @Bean @ConditionalOnMissingBean DataAttributeDialect dialect() { return new DataAttributeDialect(); } } @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ SpringSecurityDialect.class }) static class ThymeleafSecurityDialectConfiguration { @Bean @ConditionalOnMissingBean SpringSecurityDialect securityDialect() { return new SpringSecurityDialect(); } } @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Java8TimeDialect.class) static class ThymeleafJava8TimeDialect { @Bean @ConditionalOnMissingBean Java8TimeDialect java8TimeDialect() { return new Java8TimeDialect(); } } }
ThymeleafProperties是SpringBoot的属性配置类,使用ConfigurationProperties注解进行属性映射
@ConfigurationProperties(prefix = "spring.thymeleaf") public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; //默认的模板资源路径 public static final String DEFAULT_PREFIX = "classpath:/templates/"; //默认解析html资源 public static final String DEFAULT_SUFFIX = ".html"; /** * Whether to check that the template exists before rendering it. */ private boolean checkTemplate = true; /** * Whether to check that the templates location exists. */ private boolean checkTemplateLocation = true; /** * Prefix that gets prepended to view names when building a URL. */ private String prefix = DEFAULT_PREFIX; /** * Suffix that gets appended to view names when building a URL. */ private String suffix = DEFAULT_SUFFIX; /** * Template mode to be applied to templates. See also Thymeleaf's TemplateMode enum. */ //默认模式也是html的 private String mode = "HTML"; /** * Template files encoding. */ private Charset encoding = DEFAULT_ENCODING; /** * Whether to enable template caching. */ //默认开启缓存,项目没上线建议经过配置关闭,而后按F9就能够自动编译,避免影响调试 private boolean cache = true; .... }
ok,而后简单跟一下视图解析器的源码:Thymeleaf视图解析器类的关键代码,建立视图view的方法,如图,也是根据viewname进行重定向
从上面方法能够看出进行重定向或者forward等等方法,而后调一下redirect的,看看RedirectView类,翻下源码,找到以下关键代码:
一样在这个类里,进行了状态码设置,请求头设置,而后response.sendRedirect(encodedURL);
而forward的是经过如图方法进行页面跳转:
附录:
Thymeleaf官方例子