SpringBoot系列之集成Thymeleaf用法手冊

SpringBoot系列之Thymeleaf語法簡單介紹css

@
Thymeleaf官方文檔已經有比較詳細的描述,因此本博客只挑部分比較重要的點看一下,還有介紹一下和SpringBoot怎麼集成使用html

一、模板引擎

引用百度百科的模板引擎解釋:前端

模板引擎(這裏特指用於Web開發的模板引擎)是爲了使用戶界面與業務數據(內容)分離而產生的,它能夠生成特定格式的文檔,用於網站的模板引擎就會生成一個標準的HTML文檔。html5

在JavaEE領域有幾中比較經常使用的模板引擎,分別是Jsp、Velocity、Freemarker、Thymeleaf,不過對於前端頁面渲染效率來講,jsp其實仍是最快的,Velocity次之。Thymeleaf雖然渲染效率不是很快,可是語法方面是比較輕巧的,Thymeleaf語法比Velocity輕巧,可是渲染效率不如Velocityjava

二、Thymeleaf簡介

2.1)、Thymeleaf定義

Thymeleaf是適用於Web和獨立環境的現代服務器端Java模板引擎,可以處理HTML,XML,JavaScript,CSS甚至純文本。具體參考Thymeleaf官網jquery

官網提供了在線文檔也有文件格式的各類文檔
在這裏插入圖片描述git

2.2)、適用模板

Thymeleaf適用於以下模板:github

  • HTML
  • XML
  • TEXT
  • JAVASCRIPT
  • CSS
  • RAW

有兩種標記模板模式(HTML 和 XML)、三種文本模板模式(TEXT、JAVASCRIPT 和 CSS)和一種無操做模板模式 (RAW)。web

ok,下面給出一些比較重要的知識點

三、重要知識點

3.1)、th:text和th:utext

這是很經常使用的text標籤,做用是Thymeleaf中設置文本的標籤,分爲兩種,一種是th:text,另一種是th:utext,兩種最重要的區別就是會不會對特殊字符進行轉義

  • 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>

在這裏插入圖片描述

3.2)、標準表達式

官方文檔裏有standard Expression Syntax這個章節,介紹的就是標準的表達式語法應用

  • Simple expressions(簡單表達式語法):
    • Variable Expressions: ${...} // 獲取遍歷值,支持OGNL語法 etc.
    1. 獲取自定義對象的屬性值
    2. 獲取自定義的變量屬性值
    3. 使用內置的基本對象
      • ctx: the context object.

      • vars: the context variables.

      • locale: the context locale.

      • request: (only in Web Contexts) the HttpServletRequest object.

      • response: (only in Web Contexts) the HttpServletResponse object.

      • session: (only in Web Contexts) the HttpSession object.

      • servletContext: (only in Web Contexts) the ServletContext object.

      詳情參考Thymeleaf的附錄A
    4. 內置的工具類對象
      官網已經給出比較詳細的介紹,詳細用法參考Thymeleaf附錄B
      在這裏插入圖片描述
    • Selection Variable Expressions: *{...} // 選定對象,也就是獲取使用 th:object 屬性的表達式的值
    • Message Expressions: #{...} //國際化內容 詳細用法參考個人博客:SpringBoot系列之i18n國際化多語言支持教程
    • Link URL Expressions: @{...} // 定義URL連接
<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>`
  • Literals (字面量值)
    • Text literals: 'one text', 'Another one!',…
    • Number literals: 0, 34, 3.0, 12.3,…
    • Boolean literals: true, false
    • Null literal: null
    • Literal tokens: one, sometext, main,…
  • Text operations (文本操做):
    • String concatenation: + //鏈接操做 @{url/}+${id}
    • Literal substitutions: |The name is ${name}| //字符串中使用${name}變量值
  • Arithmetic operations: (數學運算)
    • Binary operators: +, -, *, /, %
    • Minus sign (unary operator): -
  • Boolean operations:(布爾運算)
    • Binary operators: and, or
    • Boolean negation (unary operator): !, not
  • Comparisons and equality:(比較運算)
    • Comparators: >, <, >=, <= (gt, lt, ge, le)
    • Equality operators: ==, != (eq, ne)
  • Conditional operators:(條件運算,包括三元運算符etc.)
    • If-then: (if) ? (then)
    • If-then-else: (if) ? (then) : (else)
    • Default: (value) ?: (defaultvalue)
  • Special tokens:(特殊的令牌,也就是使用No-Operatio)
    • No-Operation: _
      在這裏插入圖片描述

      All these features can be combined and neste:
      'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

翻譯過來意思是,這些語法均可以組合使用,這個章節是Thymeleaf一個重要的基本使用章節,本博客對一些重要的知識點進行摘錄同時介紹一下在SpringBoot裏如何使用,固然天然沒有官方文檔詳細的,不過官方並無經過中文文檔,英文水平很差的話,閱讀起來比較困難,固然我也找了一篇國內翻譯過來的Thymeleaf中文文檔,讀者詳細的能夠參考文檔

3.3)、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>
        <!--最新上架//-->

3.4)、公共模塊抽取

在項目開發中常常遇到一些能夠重用的頁面,這時候就能夠Thymeleaf的Template Layout進行公共頁面的複用

本博客以官方介紹的複用footer.html頁面進行說明
在這裏插入圖片描述
使用步驟:

  1. 抽取公共的片斷
<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

  <body>
  
    <div th:fragment="copy">
      &copy; 2011 The Good Thymes Virtual Grocery
    </div>
  
  </body>
  
</html>
  1. 引入公共的片斷
    <div th:insert="~{footer :: copy}"></div>
    ~{templatename::selector}:模板名::選擇器
    ~{templatename::fragmentname}:模板名::片斷名
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div

三種引入公共片斷的th屬性:

  • th:insert:將公共片斷整個插入到聲明引入的元素中
  • th:replace:將聲明引入的元素替換爲公共片斷
  • th:include:將被引入的片斷的內容包含進這個標籤中

效果對比:

<div>
    <footer>
    &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
</div>
<footer>
&copy; 2011 The Good Thymes Virtual Grocery
</footer>
<div>
&copy; 2011 The Good Thymes Virtual Grocery
</div

3.5)、行內寫法介紹

所謂行內寫法就是沒寫在html對應的標籤裏的寫法,直接在頁面空白處,用[[....]]或者[(....)]的寫法,而後[[....]]和[(....)]的區別其實就等同於th:text和th:utext的區別,一個會進行轉義,一個不會轉義特殊字符

  • [[....]]寫法:會轉義html標籤這些特殊字符(轉成字符)
  • [(....)]寫法:不會轉義html標籤這些特殊字符(按照其原意)
    寫個例子就明白了:
    在這裏插入圖片描述
<span>
    The message is [[${msg}]]
</span>
<br/>
<span>
    The message is [(${msg})]
</span>

在這裏插入圖片描述

3.6)、Thymeleaf語法規則

引用尚桂谷老師的概括:
在這裏插入圖片描述

四、SpringBoot集成

4.1)、Springboot集成Thymeleaf簡介

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>

4.2)、Thymeleaf自動配置源碼簡單分析

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官方例子

相關文章
相關標籤/搜索