Spring Boot 整合视图层技术

这一节我们主要学习如何整合视图层技术:

  • 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



    4.0.0

    com.springboot
    springboot-view
    1.0-SNAPSHOT
    war

    springboot-view Maven Webapp
    
    http://www.example.com

    
        UTF-8
        1.8
        1.8
    

    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.6.RELEASE
    

    
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
        
            javax.servlet
            jstl
        
        
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
            provided
        
    

视图解析器

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





    
    用户展示


    
ID Name Age
${user.id} ${user.username} ${user.age}

启动类

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



    4.0.0

    com.springboot
    springboot-view
    1.0-SNAPSHOT
    war

    springboot-view Maven Webapp
    
    http://www.example.com

    
        UTF-8
        1.8
        1.8
    

    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.6.RELEASE
    

    
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.boot
            spring-boot-starter-freemarker
        
    

视图解析器

该文件内容不一定非得需要,可以不添加,不添加使用默认值。

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



    用户展示
    


    
ID Name Age
${user.id} ${user.username} ${user.age}

启动类

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



    4.0.0

    com.springboot
    springboot-view
    1.0-SNAPSHOT
    war

    springboot-view Maven Webapp
    
    http://www.example.com

    
        UTF-8
        1.8
        1.8
    

    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.6.RELEASE
    

    
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
    

控制层

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



    
    Thymeleaf


    
    

启动类

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" 必须由匹配的结束标记 "" 终止。

异常处理

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

方式一:编写风格严谨的HTML代码


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

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

    3.0.11.RELEASE

运行结果

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



    
    Thymeleaf


    index

user.html



    
    Thymeleaf


    user

person.html



    
    Thymeleaf


    person

字符串

变量输出

th:text :在页面中输入值

th:value :可以将一个值设置至input标签的value中


字符串操作

${#strings.isEmpty(key)} :判断字符串是否为空

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

${#strings.startsWith(msg, 'T')} :判断字符串是否以子串开头

${#strings.endsWith(msg, 'T')} :判断字符串是否以子串结尾

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

${#strings.indexOf(msg, 'T')} :查找子串的位置,并返回该子串的下标,如果没找到则返回-1

${#strings.substring(msg, 5)} :截取子串,从指定下标开始截止到末尾结束

${#strings.substring(msg, 5, 12)} :截取子串,从指定下标开始截止到指定下标结束


${#strings.toUpperCase(msg)} :将字符串转大写

${#strings.toLowerCase(msg)} :将字符串转小写

日期时间

${#dates.format(key)} :格式化日期,以浏览器默认语言为格式化标准

${#dates.format(key,'yyy/MM/dd')} :自定义格式日期转换

${#dates.year(myDate)} :获取年份,还可以获取月份、日、时、分、秒

条件判断

th:if :单选择

性别:
     

th:switch :多选择(如果要实现 if else if else 判断表达式,在 Thymeleaf 要使用 th:switch 代替)

编号:
        1 张三
        2 李四
        3 王五
    

对象

迭代遍历

th:each :迭代遍历

ID NAME AGE

th:each :状态变量属性

  • index:当前迭代器的索引 从 0 开始
  • count:当前迭代对象的计数 从 1 开始
  • size:被迭代对象的长度
  • even/odd:布尔值,当前循环是否是偶数/奇数 从 0 开始
  • first:布尔值,当前循环的是否是第一条,如果是返回 true 否则返回 false
  • last:布尔值,当前循环的是否是最后一条,如果是则返回 true 否则返回 false
Id Name Age Index Count Size Even Odd First Last

th:each :迭代 Map

Id Name Age
Id Name Age

域对象操作

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

Request:

${session.key} :HttpSession

Session:

${application.key} :ServletContext

Application:

URL表达式

基本语法

URL表达式的基本语法: @{}

th:href :绝对路径

绝对路径

th:href :相对路径,相对于当前项目的根路径

相对于当前项目的根路径

th:href :相对路径, 相对于服务器的根路径

相对于服务器的根路径

参数传递

相对路径-普通传参
相对路径-restful传参
相对路径-restful传参

✍️本章节到这里就结束了,喜欢的话就点赞 加转发:revolving_hearts:吧,我们下章节再见:wave:。