Spring Boot 整合視圖層技術

  • 2019 年 10 月 7 日
  • 筆記

這一節我們主要學習如何整合視圖層技術:

  • Jsp
  • Freemarker
  • Thymeleaf

在之前的案例中,我們都是通過 @RestController 來處理請求,所以返回的內容為json對象。那麼如果需要渲染html頁面的時候,要如何實現呢?

  Spring Boot推薦使用模板引擎

  模板引擎實現偽html 達到seo優化 使動態頁面靜態化

  在動態html上實現Spring Boot依然可以完美勝任,並且提供了多種模板引擎的默認配置支援,所以在推薦的模板引擎下,我們可以很快的上手開發動態網站。

  Spring Boot提供了默認配置的模板引擎主要有以下幾種:

    Thymeleaf

    FreeMarker

    Velocity

    Groovy

    Mustache

  Spring Boot建議使用這些模板引擎,避免使用jsp。

Jsp

創建項目

  創建 war 項目,編寫pom.xml

<?xml version="1.0" encoding="UTF-8"?>    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">      <modelVersion>4.0.0</modelVersion>        <groupId>com.springboot</groupId>      <artifactId>springboot-view</artifactId>      <version>1.0-SNAPSHOT</version>      <packaging>war</packaging>        <name>springboot-view Maven Webapp</name>      <!-- FIXME change it to the project's website -->      <url>http://www.example.com</url>        <properties>          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>          <maven.compiler.source>1.8</maven.compiler.source>          <maven.compiler.target>1.8</maven.compiler.target>      </properties>        <parent>          <groupId>org.springframework.boot</groupId>          <artifactId>spring-boot-starter-parent</artifactId>          <version>2.1.6.RELEASE</version>      </parent>        <dependencies>          <!-- Web 組件 -->          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-web</artifactId>          </dependency>          <!-- Spring Boot不推薦使用 jsp,所以需要手動導入 jstl 和 jasper 依賴 -->          <!-- jstl -->          <dependency>              <groupId>javax.servlet</groupId>              <artifactId>jstl</artifactId>          </dependency>          <!-- jasper -->          <dependency>              <groupId>org.apache.tomcat.embed</groupId>              <artifactId>tomcat-embed-jasper</artifactId>              <scope>provided</scope>          </dependency>      </dependencies>  </project>

視圖解析器

  resources/application.properties

spring.mvc.view.prefix=/WEB-INF/jsp/  spring.mvc.view.suffix=.jsp

實體類

  User.java

package com.springboot.pojo;    import java.io.Serializable;    public class User implements Serializable {        private Integer id;      private String username;      private Integer age;        public User() {      }        public User(Integer id, String username, Integer age) {          this.id = id;          this.username = username;          this.age = age;      }        public Integer getId() {          return id;      }        public void setId(Integer id) {          this.id = id;      }        public String getUsername() {          return username;      }        public void setUsername(String username) {          this.username = username;      }        public Integer getAge() {          return age;      }        public void setAge(Integer age) {          this.age = age;      }        @Override      public String toString() {          return "User{" +                  "id=" + id +                  ", username='" + username + ''' +                  ", age=" + age +                  '}';      }    }

控制層

  UserController.java

package com.springboot.controller;    import com.springboot.pojo.User;  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 UserController {        @RequestMapping("/showUser")      public String showUser(Model model) {          List<User> list = new ArrayList<>();          list.add(new User(1, "張三", 18));          list.add(new User(2, "李四", 20));          list.add(new User(3, "王五", 22));          model.addAttribute("list", list);          // 跳轉視圖          return "userList";      }    }

視圖層

  userList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>  <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  <!DOCTYPE html>  <html>  <head>      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">      <title>用戶展示</title>  </head>  <body>      <table border="1" cellspacing="0" align="center" width="50%">          <tr>              <th>ID</th>              <th>Name</th>              <th>Age</th>          </tr>          <c:forEach items="${list}" var="user">              <tr>                  <td>${user.id}</td>                  <td>${user.username}</td>                  <td>${user.age}</td>              </tr>          </c:forEach>      </table>  </body>  </html>

啟動類

  App.java

package com.springboot;    import org.springframework.boot.SpringApplication;  import org.springframework.boot.autoconfigure.SpringBootApplication;    @SpringBootApplication  public class App {        public static void main(String[] args) {          SpringApplication.run(App.class, args);      }    }

  結果

Freemarker

創建項目

  創建 war 項目,編寫pom.xml

<?xml version="1.0" encoding="UTF-8"?>    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">      <modelVersion>4.0.0</modelVersion>        <groupId>com.springboot</groupId>      <artifactId>springboot-view</artifactId>      <version>1.0-SNAPSHOT</version>      <packaging>war</packaging>        <name>springboot-view Maven Webapp</name>      <!-- FIXME change it to the project's website -->      <url>http://www.example.com</url>        <properties>          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>          <maven.compiler.source>1.8</maven.compiler.source>          <maven.compiler.target>1.8</maven.compiler.target>      </properties>        <parent>          <groupId>org.springframework.boot</groupId>          <artifactId>spring-boot-starter-parent</artifactId>          <version>2.1.6.RELEASE</version>      </parent>        <dependencies>          <!-- web 組件 -->          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-web</artifactId>          </dependency>          <!-- freemarker 組件 -->          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-freemarker</artifactId>          </dependency>      </dependencies>  </project>

視圖解析器

  該文件內容不一定非得需要,可以不添加,不添加使用默認值。

  resources/application.properties

# FREEMARKER (FreeMarkerAutoConfiguration)  spring.freemarker.allow-request-override=false  spring.freemarker.allow-session-override=false  spring.freemarker.cache=true  spring.freemarker.charset=UTF-8  spring.freemarker.check-template-location=true  spring.freemarker.content-type=text/html  spring.freemarker.enabled=true  spring.freemarker.expose-request-attributes=false  spring.freemarker.expose-session-attributes=false  spring.freemarker.expose-spring-macro-helpers=true  spring.freemarker.prefer-file-system-access=true  spring.freemarker.suffix=.ftl  spring.freemarker.template-loader-path=classpath:/templates/  spring.freemarker.settings.template_update_delay=0  spring.freemarker.settings.default_encoding=UTF-8  spring.freemarker.settings.classic_compatible=true  spring.freemarker.order=1

實體類

  User.java

package com.springboot.pojo;    import java.io.Serializable;    public class User implements Serializable {        private Integer id;      private String username;      private Integer age;        public User() {      }        public User(Integer id, String username, Integer age) {          this.id = id;          this.username = username;          this.age = age;      }        public Integer getId() {          return id;      }        public void setId(Integer id) {          this.id = id;      }        public String getUsername() {          return username;      }        public void setUsername(String username) {          this.username = username;      }        public Integer getAge() {          return age;      }        public void setAge(Integer age) {          this.age = age;      }        @Override      public String toString() {          return "User{" +                  "id=" + id +                  ", username='" + username + ''' +                  ", age=" + age +                  '}';      }    }

控制層

  UserController.java

package com.springboot.controller;    import com.springboot.pojo.User;  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 UserController {        @RequestMapping("/showUser")      public String showUser(Model model) {          List<User> list = new ArrayList<>();          list.add(new User(1, "張三", 18));          list.add(new User(2, "李四", 20));          list.add(new User(3, "王五", 22));          model.addAttribute("list", list);          // 跳轉視圖          return "userList";      }    }

視圖層

  Spring Boot要求模板形式的視圖層技術的文件必須要放到 src/main/resources 目錄下的 templates 目錄。

  該目錄內的模板文件名稱必須是 ftl 的後綴結尾。

  userList.ftl

<!DOCTYPE html>  <html>  <head>      <title>用戶展示</title>      <meta charset="UTF-8"></meta>  </head>  <body>      <table border="1" cellspacing="0" align="center" width="50%">          <tr>              <th>ID</th>              <th>Name</th>              <th>Age</th>          </tr>          <#list list as user >              <tr>                  <td>${user.id}</td>                  <td>${user.username}</td>                  <td>${user.age}</td>              </tr>          </#list>      </table>  </body>  </html>

啟動類

  App.java

package com.springboot;    import org.springframework.boot.SpringApplication;  import org.springframework.boot.autoconfigure.SpringBootApplication;    @SpringBootApplication  public class App {        public static void main(String[] args) {          SpringApplication.run(App.class, args);      }    }

  結果

Thymeleaf (重點講解)

入門案例

創建項目

  創建 war 項目,編寫pom.xml

<?xml version="1.0" encoding="UTF-8"?>    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">      <modelVersion>4.0.0</modelVersion>        <groupId>com.springboot</groupId>      <artifactId>springboot-view</artifactId>      <version>1.0-SNAPSHOT</version>      <packaging>war</packaging>        <name>springboot-view Maven Webapp</name>      <!-- FIXME change it to the project's website -->      <url>http://www.example.com</url>        <properties>          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>          <maven.compiler.source>1.8</maven.compiler.source>          <maven.compiler.target>1.8</maven.compiler.target>      </properties>        <parent>          <groupId>org.springframework.boot</groupId>          <artifactId>spring-boot-starter-parent</artifactId>          <version>2.1.6.RELEASE</version>      </parent>        <dependencies>          <!-- web 組件 -->          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-web</artifactId>          </dependency>          <!-- thymeleaf 組件 -->          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-thymeleaf</artifactId>          </dependency>      </dependencies>  </project>

控制層

  ThymeleafController.java

package com.springboot.controller;    import org.springframework.stereotype.Controller;  import org.springframework.ui.Model;  import org.springframework.web.bind.annotation.RequestMapping;    @Controller  public class ThymeleafController {        @RequestMapping("/show")      public String showMsg(Model model) {          model.addAttribute("msg", "Thymeleaf 入門案例");          return "msg";      }    }

視圖層

  Spring Boot要求模板形式的視圖層技術的文件必須要放到 src/main/resources 目錄下的 templates 目錄。

  msg.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Thymeleaf</title>  </head>  <body>      <span th:text="Thymeleaf"></span>      <hr/>      <span th:text="${msg}"></span>  </body>  </html>

啟動類

  App.java

package com.springboot;    import org.springframework.boot.SpringApplication;  import org.springframework.boot.autoconfigure.SpringBootApplication;    @SpringBootApplication  public class App {        public static void main(String[] args) {          SpringApplication.run(App.class, args);      }    }

  運行結果出現異常(如果是 Spring Boot 1.X.X 版本才會出現此異常):

org.xml.sax.SAXParseException: 元素類型 "meta" 必須由匹配的結束標記 "</meta>" 終止。

異常處理

  如果是 Spring Boot 1.X.X 版本需要以下操作,本案例是 Spring Boot 2.1.6 版本,所以不需要更改。

方式一:編寫風格嚴謹的HTML程式碼

<meta charset="UTF-8"/>

方式二:更換Thymeleaf的jar包版本

  thymeleaf.jar:更新為 3.0 以上的版本

<img src="https://mrhelloworld.oss-cn-shanghai.aliyuncs.com/articlesImages/1562659063590.png"/

<properties>      <thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>  </properties>

運行結果

Thymeleaf 語法詳解

Thymeleaf 內置對象語法:

  • 調用內置對象一定要用 #
  • 大部分的內置對象都以 s 結尾 stringsnumbersdates

準備數據

實體類

  User.java

package com.springboot.pojo;    import java.io.Serializable;    public class User implements Serializable {        private Integer id;      private String username;      private Integer age;        public User() {      }        public User(Integer id, String username, Integer age) {          this.id = id;          this.username = username;          this.age = age;      }        public Integer getId() {          return id;      }        public void setId(Integer id) {          this.id = id;      }        public String getUsername() {          return username;      }        public void setUsername(String username) {          this.username = username;      }        public Integer getAge() {          return age;      }        public void setAge(Integer age) {          this.age = age;      }        @Override      public String toString() {          return "User{" +                  "id=" + id +                  ", username='" + username + ''' +                  ", age=" + age +                  '}';      }    }

控制層

  ThymeleafController.java

package com.springboot.controller;    import com.springboot.pojo.User;  import org.springframework.stereotype.Controller;  import org.springframework.ui.Model;  import org.springframework.web.bind.annotation.PathVariable;  import org.springframework.web.bind.annotation.RequestMapping;    import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse;  import java.util.*;    @Controller  public class ThymeleafController {        @RequestMapping("/show")      public String showMsg(Model model,                            HttpServletRequest request,                            HttpServletResponse response) {            // 字元串          model.addAttribute("msg", "Thymeleaf 入門案例");          // 日期時間          model.addAttribute("myDate", new Date());          // 條件判斷if          model.addAttribute("sex", 1);          // 條件判斷switch          model.addAttribute("id", 1);          // 對象          model.addAttribute("user", new User(1, "張三", 20));            // 迭代遍歷list          List<User> userList = new ArrayList<>();          userList.add(new User(1, "張三", 20));          userList.add(new User(2, "李四", 22));          userList.add(new User(3, "王五", 24));          model.addAttribute("userList", userList);            // 迭代遍歷map          Map<String, User> userMap = new HashMap<>();          userMap.put("u1", new User(1, "張三", 20));          userMap.put("u2", new User(2, "李四", 22));          userMap.put("u3", new User(3, "王五", 24));          model.addAttribute("userMap", userMap);            // 域對象操作          request.setAttribute("req", "HttpServletRequest");          request.getSession().setAttribute("sess", "HttpSession");          request.getSession().getServletContext().setAttribute("app", "Application");          return "msg";        }        /**       * URL表達式-相對路徑       * @return       */      @RequestMapping("/index")      public String index() {          return "index";      }        /**       * URL表達式-普通傳參       * @param id       * @param username       * @return       */      @RequestMapping("/user")      public String user(Integer id, String username) {          System.out.println("id:" + id + " username:" + username);          return "user";      }        /**       * URL表達式-restful傳參       * @param id       * @param username       * @return       */      @RequestMapping("/person/{id}/{username}")      public String person(@PathVariable Integer id, @PathVariable String username) {          System.out.println("id:" + id + " username:" + username);          return "person";      }    }

視圖層

  index.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Thymeleaf</title>  </head>  <body>      index  </body>  </html>

  user.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Thymeleaf</title>  </head>  <body>      user  </body>  </html>

  person.html

<!DOCTYPE html>  <html lang="en">  <head>      <meta charset="UTF-8">      <title>Thymeleaf</title>  </head>  <body>      person  </body>  </html>

字元串

變數輸出

th:text:在頁面中輸入值

<span th:text="Thymeleaf"></span>

th:value:可以將一個值設置至input標籤的value中

<input type="text" th:value="${msg}" />

字元串操作

${#strings.isEmpty(key)}:判斷字元串是否為空

<span th:text="${#strings.isEmpty(msg)}"></span>

${#strings.contains(msg, 'T')}:判斷字元串是否包含子串

<span th:text="${#strings.contains(msg, 'T')}"></span>

${#strings.startsWith(msg, 'T')}:判斷字元串是否以子串開頭

<span th:text="${#strings.startsWith(msg, 'T')}"></span>

${#strings.endsWith(msg, 'T')}:判斷字元串是否以子串結尾

<span th:text="${#strings.endsWith(msg, 'T')}"></span>

${#strings.length(msg)}:返回字元串的長度

<span th:text="${#strings.length(msg)}"></span>

${#strings.indexOf(msg, 'T')}:查找子串的位置,並返回該子串的下標,如果沒找到則返回-1

<span th:text="${#strings.indexOf(msg, 'T')}"></span>

${#strings.substring(msg, 5)}:截取子串,從指定下標開始截止到末尾結束

${#strings.substring(msg, 5, 12)}:截取子串,從指定下標開始截止到指定下標結束

<span th:text="${#strings.substring(msg, 5)}"></span>  <span th:text="${#strings.substring(msg, 5, 12)}"></span>

${#strings.toUpperCase(msg)}:將字元串轉大寫

<span th:text="${#strings.toUpperCase(msg)}"></span>

${#strings.toLowerCase(msg)}:將字元串轉小寫

<span th:text="${#strings.toLowerCase(msg)}"></span>

日期時間

${#dates.format(key)}:格式化日期,以瀏覽器默認語言為格式化標準

<span th:text="${#dates.format(myDate)}"></span>

${#dates.format(key,'yyy/MM/dd')}:自定義格式日期轉換

<span th:text="${#dates.format(myDate, 'yyyy年MM月dd日 HH:mm:ss')}"></span>

${#dates.year(myDate)}:獲取年份,還可以獲取月份、日、時、分、秒

<span th:text="${#dates.year(myDate)}"></span>年  <span th:text="${#dates.month(myDate)}"></span>月  <span th:text="${#dates.day(myDate)}"></span>日  <span th:text="${#dates.hour(myDate)}"></span>時  <span th:text="${#dates.minute(myDate)}"></span>分  <span th:text="${#dates.second(myDate)}"></span>秒

條件判斷

th:if:單選擇

性別:<span th:if="${sex} == 1">男</span>       <span th:if="${sex} == 2">女</span>

th:switch:多選擇(如果要實現 if else if else 判斷表達式,在 Thymeleaf 要使用 th:switch 代替)

編號:<span th:switch="${id}">          <span th:case="1">1 張三</span>          <span th:case="2">2 李四</span>          <span th:case="3">3 王五</span>      </span>

對象

<input th:value="${user.username}"/>  <span th:text="${user.age}"></span>

迭代遍歷

th:each:迭代遍歷

<table border="1" cellspacing="0">      <tr>          <th>ID</th>          <th>NAME</th>          <th>AGE</th>      </tr>      <tr th:each="user : ${userList}">          <td th:text="${user.id}"></td>          <td th:text="${user.username}"></td>          <td th:text="${user.age}"></td>      </tr>  </table>

th:each:狀態變數屬性

  • index:當前迭代器的索引 從 0 開始
  • count:當前迭代對象的計數 從 1 開始
  • size:被迭代對象的長度
  • even/odd:布爾值,當前循環是否是偶數/奇數 從 0 開始
  • first:布爾值,當前循環的是否是第一條,如果是返回 true 否則返回 false
  • last:布爾值,當前循環的是否是最後一條,如果是則返回 true 否則返回 false
<table border="1" cellspacing="0">      <tr>          <th>Id</th>          <th>Name</th>          <th>Age</th>          <th>Index</th>          <th>Count</th>          <th>Size</th>          <th>Even</th>          <th>Odd</th>          <th>First</th>          <th>Last</th>      </tr>      <tr th:each="user, state : ${userList}">          <td th:text="${user.id}"></td>          <td th:text="${user.username}"></td>          <td th:text="${user.age}"></td>          <td th:text="${state.index}"></td>          <td th:text="${state.count}"></td>          <td th:text="${state.size}"></td>          <td th:text="${state.even}"></td>          <td th:text="${state.odd}"></td>          <td th:text="${state.first}"></td>          <td th:text="${state.last}"></td>      </tr>  </table>

th:each:迭代 Map

<table border="1" cellspacing="0">      <tr>          <th>Id</th>          <th>Name</th>          <th>Age</th>      </tr>      <tr th:each="user : ${userMap}">          <td th:text="${user}"></td>      </tr>  </table>  <table border="1" cellspacing="0">      <tr>          <th>Id</th>          <th>Name</th>          <th>Age</th>      </tr>      <tr th:each="user : ${userMap}">          <td th:each="entry : ${user}" th:text="${entry.value.id}" ></td>          <td th:each="entry : ${user}" th:text="${entry.value.username}"></td>          <td th:each="entry : ${user}" th:text="${entry.value.age}"></td>      </tr>  </table>

<img src="https://mrhelloworld.oss-cn-shanghai.aliyuncs.com/articlesImages/1562747209340.png"/

域對象操作

${#httpServletRequest.getAttribute(key)}:HttpServletRequest

Request:<span th:text="${#httpServletRequest.getAttribute('req')}"></span>

${session.key}:HttpSession

Session:<span th:text="${session.sess}"></span>

${application.key}:ServletContext

Application:<span th:text="${application.app}"></span>

URL表達式

基本語法

URL表達式的基本語法:@{}

th:href:絕對路徑

<a th:href="@{http://www.baidu.com}">絕對路徑</a>

th:href:相對路徑,相對於當前項目的根路徑

<a th:href="@{/index}">相對於當前項目的根路徑</a>

th:href:相對路徑, 相對於伺服器的根路徑

<a th:href="@{~/project/resourcename}">相對於伺服器的根路徑</a>

參數傳遞

<a th:href="@{/user(id=1, username=zhagnsan)}">相對路徑-普通傳參</a>  <a th:href="@{/person/1/zhangsan}">相對路徑-restful傳參</a>  <a th:href="@{/person/{id}/{name}(id=2, name=lisi)}">相對路徑-restful傳參</a>

✍️本章節到這裡就結束了,喜歡的話就點贊?加轉發?吧,我們下章節再見?。