天天看點

Spring 學習筆記(十)渲染 Web 視圖 (Apache Tilesa 和 Thymeleaf)

使用Apache Tiles視圖定義布局

為了在Spring中使用Tiles,需要配置幾個bean。我們需要一個TilesConfigurer bean,它會負責定位和加載Tile定義并協調生成Tiles。除此之外,還需要TilesViewResolver bean将邏輯視圖名稱解析為Tile定義。

配置TilesConfigurer來解析Tile定義。

@Bean
public TilesConfigurer tilesConfigurer(){
    TilesConfigurer tiles = new TilesConfigurer();
    tiles.setDefinitions(new String[] {
        "/WEB-INF/layout/tiles.xml"
    });
    tiles.setCheckRefresh(true);
    return tiles;
}
           

當配置TilesConfigurer的時候,所要設定的最重要的屬性就是definitions。這個屬性接受一個String類型的數組,其中每個條目都指定一個Tile定義的XML檔案。對于Spittr應用來講,我們讓它在“/WEB-INF/layout/”目錄下查找tiles.xml。

其實我們還可以指定多個Tile定義檔案,甚至能夠在路徑位置上使用通配符,當然在上例中我們沒有使用該功能。例如,我們要求TilesConfigurer加載“/WEB-INF/”目錄下的所有名字為tiles.xml的檔案,那麼可以按照如下的方式設定definitions屬性:

tiles.setDefinitons(new String[] {
    "/WEB-INF/**/tiles.xml"
});
           

讓我們來配置TilesViewResolver,可以看到,這是一個很基本的bean定義,沒有什麼要設定的屬性:

@Bean
public ViewResolver viewResolver(){
    return new TilesViewResolver();
}
           

如果你更喜歡XML配置的話,那麼可以按照如下的形式配置TilesConfigurer和TilesViewResolver:

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles.TilesConfigurer">
    <property name="definitions">
        <list>
            <value>/WEB-INF/layout/tiles.xml.xml</value>
            <value>/WEB-INF/views/**/tiles.xml</value>
        </list>
    </property>
</bean>

<bean id="viewResolver" class="org.springframework.web.servlet.view.tiles.TilesViewResolver"/>
           

TilesConfigurer會加載Tile定義并與Apache Tiles協作,而TilesViewRe-solver會将邏輯視圖名稱解析為引用Tile定義的視圖。它是通過查找與邏輯視圖名稱相比對的Tile定義實作該功能的。我們需要建立幾個Tile定義以了解它是如何運轉的。

定義 Tiles

Apache Tiles提供了一個文檔類型定義(document type definition,DTD),用來在XML檔案中指定Tile的定義。每個定義中需要包含一個<definition>元素,這個元素會有一個或多個<putattribute>元素。例如,如下的XML文檔為 Spittr 應用定義了幾個 tile。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
        "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
        "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
    <definition name="base" template="/WEB-INF/layout/page.jsp">
        <put-attribute name="header" value="/WEB-INF/layout/header.jsp"/>
        <put-attribute name="footer" value="/WEB-INF/layout/footer.jsp"/>
    </definition>
    <definition name="home" extends="base">
        <put-attribute name="body" value="/WEB-INF/view/home.jsp"/>
    </definition>
    <definition name="registerForm" extends="base">
        <put-attribute name="body" value="/WEB-INF/view/registerForm.jsp"/>
    </definition>
    <definition name="prefile" extends="base">
        <put-attribute name="body" value="/WEB-INF/views/profile.jsp"/>
    </definition>
    <definition name="spittles" extends="base">
        <put-attribute name="body" value="/WEB-INF/views/spittles.jsp"/>
    </definition>
    <definition name="spittle" extends="base">
        <put-attribute name="body" value="/WEB-INF/views/spittle.jsp"/>
    </definition>
</tiles-definitions>
           

每個<definition>元素都定義了一個Tile,它最終引用的是一個JSP模闆。在名為base的Tile中,模闆引用的是“/WEBINF/layout/page.jsp”。某個Tile可能還會引用其他的JSP模闆,使這些JSP模闆嵌入到主模闆中。對于base Tile來講,它引用的是一個頭部JSP模闆和一個底部JSP模闆。

base Tile所引用的page.jsp模闆如下面程式清單所示。

<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="t" %>
<%@ page session="false" %>
<html>
<head>
    <title>Spittr</title>
    <link rel="stylesheet" type="text/css" href="<s:uri value="/resource/style.css"/>">
</head>
<body>
    <div id="header">
        <t:insertAttribute name="header"/>
    </div>
    <div id="content">
        <t:insertAttribute name="body"/>
    </div>
    <div id="footer">
        <t:insertAttribute name="footer"/>
    </div>
</body>
</html>
           

在程式清單6.3中,需要重點關注的事情就是如何使用Tile标簽庫中的<t:insert Attribute> JSP标簽來插入其他的模闆。在這裡,用它來插入名為header、body和footer的模闆。最終,它會形成圖6.4所示的布局。

image.png

現在,我們關注一下home Tile,它擴充了base。因為它擴充了base,是以它會繼承base中的模闆和所有的屬性。盡管home Tile定義相對來說很簡單,但是它實際上包含了如下的定義:

<definition name="home" template="/WEB-INF/layout/page.jsp">
        <put-attribute name="header" value="/WEB-INF/layout/header.jsp"/>
        <put-attribute name="footer" value="/WEB-INF/layout/footer.jsp"/>
        <put-attribute name="body" value="/WEB-INF/views/home.jsp"/>
    </definition>
           

屬性所引用的每個模闆是很簡單的,如下是header.jsp模闆:

<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
<a href="<s:uri value="/" />">
<img scr="<s:uri value="/resources"/>/images/spittr_logo_50.png" border="0"/></a>
           

footer.jsp模闆更為簡單:

Copyright &copy; Craig Walls
           

每個擴充自base的Tile都定義了自己的主體區模闆,是以每個都會與其他的有所差別。但是為了完整地了解home Tile,如下展現了home.jsp:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page session="false"%>
<h1>Welcome to Spittr</h1>

<a href="<c:url value="/spittles"/>">Spittles</a>
<a href="<c:url value="spitter/register"/>">Register</a>
           

這裡的關鍵點在于通用的元素放到了page.jsp、header.jsp以及footer.jsp中,其他的Tile模闆中不再包含這部分内容。這使得它們能夠跨頁面重用,這些元素的維護也得以簡化。

使用Thymeleaf

JSP 缺點:

JSP缺乏良好格式的一個副作用就是它很少能夠與其産生的HTML類似。是以,在Web浏覽器或HTML編輯器中檢視未經渲染的JSP模闆是非常令人困惑的,而且得到的結果看上去也非常醜陋。

JSP規範是與Servlet規範緊密耦合的。這意味着它隻能用在基于Servlet的Web應用之中。JSP模闆不能作為通用的模闆(如格式化Email),也不能用于非Servlet的Web應用。

配置Thymeleaf視圖解析器

為了要在Spring中使用Thymeleaf,我們需要配置三個啟用Thymeleaf與Spring內建的bean:

-ThymeleafViewResolver:将邏輯視圖名稱解析為Thymeleaf模闆視圖;

-SpringTemplateEngine:處理模闆并渲染結果;

-TemplateResolver:加載Thymeleaf模闆。

如下為聲明這些bean的Java配置。

配置Spring對Thymeleaf的支援:

@Bean
    public viewReslover viewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine);
        return viewResolver;
    }
    
    @Bean
    public TemplateEngine templateEngine(TemplateResolver templateResolver) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        return templateEngine;
    }
    
    @Bean
    public TemplateReslover templateReslover() {
        TemplateReslover templateReslover = new ServlteContextTemplateResolver();
        templateReslover.setPrefix("/WEB-INF/templates/");
        templateReslover.setSuffix(".html");
        templateReslover.setTemplate("HTML5");
        return templateReslover;
    }
           

不管使用哪種配置方式,Thymeleaf都已經準備就緒了,它可以将響應中的模闆渲染到Spring MVC控制器所處理的請求中。

ThymeleafViewResolver是Spring MVC中ViewResolver的一個實作類。像其他的視圖解析器一樣,它會接受一個邏輯視圖名稱,并将其解析為視圖。不過在該場景下,視圖會是一個Thymeleaf模闆。

需要注意的是ThymeleafViewResolver bean中注入了一個對SpringTemplate Engine bean的引用。SpringTemplateEngine會在Spring中啟用Thymeleaf引擎,用來解析模闆,并基于這些模闆渲染結果。可以看到,我們為其注入了一個TemplateResolver bean的引用。

TemplateResolver會最終定位和查找模闆。與之前配置InternalResource-ViewResolver類似,它使用了prefix和suffix屬性。字首和字尾将會與邏輯視圖名組合使用,進而定位Thymeleaf引擎。它的templateMode屬性被設定成了HTML 5,這表明我們預期要解析的模闆會渲染成HTML 5輸出。

所有的Thymeleaf bean都已經配置完成了,那麼接下來我們該建立幾個視圖了。

定義Thymeleaf模闆

使用Thymeleaf命名空間的首頁模闆引擎:

<html xmlns="http://www.w3.org/1999/xhtml"
        xmlns="http://www.thymeleaf.org">
<link>
    <title>Spittr</title>
    <link rel="stylesheet" type="/text/css" th:href="@{/resources/style.css}"></link>
</head>
<body>
    <h1>Welcome to Spittr</h1>
    <a th:href="@{/spittles}">Spittles</a>
    <a th:href="@{/spitter/register}">Register</a>
</body>
</html>
           

首頁模闆相對來講很簡單,隻使用了th:href屬性。這個屬性與對應的原生HTML屬性很類似,也就是href屬性,并且可以按照相同的方式來使用。th:href屬性的特殊之處在于它的值中可以包含Thymeleaf表達式,用來計算動态的值。它會渲染成一個标準的href屬性,其中會包含在渲染時動态建立得到的值。這是Thymeleaf命名空間中很多屬性的運作方式:它們對應标準的HTML屬性,并且具有相同的名稱,但是會渲染一些計算後得到的值。在本例中,使用th:href屬性的三個地方都用到了“@{}”表達式,用來計算相對于URL的路徑(就像在JSP頁面中,我們可能會使用的JSTL <c:url>标簽或Spring<s:url>标簽類似)。

借助Thymeleaf實作表單綁定

表單綁定是Spring MVC的一項重要特性。它能夠将表單送出的資料填充到指令對象中,并将其傳遞給控制器,而在展現表單的時候,表單中也會填充指令對象中的值。如果沒有表單綁定功能的話,我們需要確定HTML表單域要映射後端指令對象中的屬性,并且在校驗失敗後展現表單的時候,還要負責確定輸入域中值要設定為指令對象的屬性。但是,如果有表單綁定的話,它就會負責這些事情了。

請參考如下的Thymeleaf模闆片段,它會渲染FirstName輸入域:

<label th:class="${#fields.hasErrors('firstName')}? 'error'">First Name</label>
<input type="text" th:field="*{fileName}" th:class="${#fields.hasErrors('firstName')}? 'error'"/><br/>
           

th:class屬性會渲染為一個class屬性,它的值是根據給定的表達式計算得到的。在上面的這兩個th:class屬性中,它會直接檢查firstName域有沒有校驗錯誤。如果有的話,class屬性在渲染時的值為error。如果這個域沒有錯誤的話,将不會渲染class屬性。

作者:theodore的技術提升之路

連結:https://www.jianshu.com/p/03fa32a3a1f4

來源:簡書

簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。