封面画师:adsuger     封面ID:76462341

0. 前言

本文主要讲述使用SpringBoot + Thymeleaf在项目中的基本使用,并 不涉及数据库使用

1. 首页

1.1 首页跳转

在templates目录下的静态资源是无法直接访问的,相当于我们以前的Web-INF目录,我们可以使用Controller进行访问。

首页的添加有两种方式:直接在Controller中编写跳转、使用配置类进行配置。

假设我们的首页名为index.html,并且放置于templates目录下。

直接在Controller中编写跳转

1
2
3
4
@RequestMapping({"/index.html","/"})
public String index(){
return "index";
}

使用配置类进行配置

1
2
3
4
5
6
7
8
9
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
}
}

1.2 加载首页样式

因为我们使用了Thymeleaf,因此我们需要将HTML中的一些属性改成Thymeleaf支持的属性。

添加命名空间:

1
<html lang="en" xmlns:th="https://www.thymeleaf.org/">

引入静态资源(链接前的第一个/表示项目的根目录):

1
2
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/signin.css}" rel="stylesheet">

❓为什么链接前要加一个 “/” 表示根目录?

❗因为请求转发涉及到相对路径的问题,所以最前面要加 “/” 表示根路径。

在Thymeleaf中,链接(URL)使用@{ ... } 进行表示。

使用Thymeleaf可能会出现因为模板引擎缓存的问题,导致引入静态资源无效,这时我们在配置文件中关闭Thymeleaf的缓存即可:

1
2
#关闭模板引擎缓存
spring.thymeleaf.cache=false

2. 页面国际化

所谓国际化,就是可以点击 “中文” 、 “English” 或其他语言实现切换界面语言。

实现首页的国际化:

  • 在resources目录下创建目录 i18n ;

  • 在 i18n 目录下创建文件 login.properties、login_en_US.properties、login_zh_CN.properties分别表示登录界面语言为默认语言、中文、英文。

  • 点击IDEA下方Resource Bundle,进行可视化配置。

  • 在配置文件进行配置:

    1
    2
    #国际化配置文件真实位置
    spring.messages.basename=i18n.login
  • 在界面中使用Thymeleaf表达式修改,如:

    1
    2
    3
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}"></h1>
    <input type="text" name="username" class="form-control"
    th:placeholder="#{login.username}">
  • 编写点击切换的链接:

    1
    2
    <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
    <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
  • 编写自定义地区解析器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class MyLocaleResolver implements LocaleResolver {

    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest httpServletRequest) {
    //获取请求中的语言参数
    String language = httpServletRequest.getParameter("l");

    Locale locale = Locale.getDefault(); //如果没有参数使用默认的

    //如果请求的链接携带了国际化的参数
    if(!StringUtils.isEmpty(language)){
    //zh_CN
    String[] split = language.split("_");

    //国家,地区
    locale = new Locale(split[0],split[1]);
    }
    return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
    }
    }
  • 在配置类中添加bean至容器:

    1
    2
    3
    4
    5
    6
    // 自定义国际化组件生效
    // 方法名别写错了,SpringMVC中约定Bean的名称必须是localeResolver
    @Bean
    public LocaleResolver localeResolver(){
    return new MyLocaleResolver();
    }

3. 登录设置

3.1 登录失败提示信息

登录的Controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RequestMapping("/user/login")
public String login(
@RequestParam("username") String username,
@RequestParam("password") String password,
Model model, HttpSession session) {

// 模拟具体业务
if(!StringUtils.isEmpty(username) && "123456".equals(password)){
session.setAttribute("loginUser",username);
return "redirect:/main.html";
}else{
//通知用户密码错误
model.addAttribute("msg","用户名或密码错误");
return "index";
}
}

错误信息:

学会使用表达工具对象( Expression Utility Objects),参考官方文档第19项。

官方文档:Expression Utility Objects

1
2
<!--如果msg为空则不显示-->
<p style="color: #ff0000" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

3.2 登录拦截器

编写自定义拦截器类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LoginHandlerInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//登录成功后,应该有用户的session
Object loginUser = request.getSession().getAttribute("loginUser");

// 登录验证
if (loginUser == null){
request.setAttribute("msg","请进行登录查看!");
request.getRequestDispatcher("/index.html").forward(request,response);
// false 不放行
return false;
}else {
// true 放行
return true;
}
}
}

在配置类中添加拦截器:

1
2
3
4
5
6
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index.html","/","/user/login","/static/*");
}

4. 抽取公共资源

公共资源放置于templates/commons/commons.html中:

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org/">

<!--顶部导航栏-->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
<!-- 省略中间代码 -->
</nav>
<!--侧边栏 -->
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
<!-- 省略中间代码 -->
</nav>
</html>

公共资源的使用(list.html):

1
2
3
4
5
6
7
8
9
<!--顶部导航栏-->
<div th:replace="~{commons/commons::topbar}"></div>
<div class="container-fluid">
<div class="row">
<!--侧边栏-->
<!-- 传递一个参数,用于实现高亮 -->
<div th:replace="~{commons/commons::sidebar(active='list.html')}"></div>
</div>
</div>

插入公共资源中th:replaceth:insert的区别:

th:replace是直接将当前标签替换为对应的公共资源,th:insert表示插入公共资源,会在公共资源外加一层<div></div>

5. 404 与 500

在SpringBoot中,404页面与500页面变得异常简单,我们只需要在templates目录下创建error文件夹,然后在这个文件夹下编写404.html和500.html即可。当发生 404 或 500 错误时,SpringBoot会自动引导界面跳转至对应的界面。

6. 其他

Thymeleaf三元表达式的使用,点击侧边栏跳转至对应的界面,侧边栏对应的字样高亮:

1
<a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">

Thymeleaf日期显示格式化:

1
<td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>

Thymeleaf遍历:

1
2
<option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}"
th:value="${dept.getId()}"></option>

SpringBoot中MVC的日期格式是yyyy/MM/dd,如果我们要进行日期格式化,可以在配置文件中添加:

1
2
#配置日期格式化
spring.mvc.date-format=yyyy-MM-dd

Thymeleaf中URL拼接(RestFul风格):

1
2
3
4
5
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">编辑</a>
<!-- 或者 -->
<a class="btn btn-sm btn-primary" th:href="@{/emp(id=${emp.getId()})}">编辑</a>
<!-- 或者 -->
<a class="btn btn-sm btn-primary" th:href="@{/emp/id=(${emp.getId()})}">编辑</a>