springboot之thymeleaf

什么是thymeleaf

Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎

模板引擎介绍

模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的html文档。
从字面上理解模板引擎,最重要的就是模板二字,这个意思就是做好一个模板后套入对应位置的数据,最终以html的格式展示出来,这就是模板引擎的作用。

模板引擎的功能就类似我们的会议室开会一样开箱即用,将模板设计好之后直接填充数据即可而不需要重新设计整个页面。提高页面、代码的复用性

thymeleaf特点

  • 动静分离: Thymeleaf选用html作为模板页,这是任何一款其他模板引擎做不到的!Thymeleaf使用html通过一些特定标签语法代表其含义,但并未破坏html结构,即使无网络、不通过后端渲染也能在浏览器成功打开,大大方便界面的测试和修改。

  • 开箱即用: Thymeleaf提供标准和Spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、改JSTL、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。

  • Springboot官方大力推荐和支持,Springboot官方做了很多默认配置,开发者只需编写对应html即可,大大减轻了上手难度和配置复杂度。

Thymeleaf3.0

Thymeleaf在曾经还有一次大的版本升级,从Thymeleaf2.0—>Thymeleaf3.0。在Thymeleaf2.0时代,Thymeleaf基于xml实现,虽然它带来了许多出色强大的功能,但有时会降低性能效率,那个时候Thymeleaf的性能真的太差而被很多人所吐槽带来了很不好的印象。

但是Thymeleaf3.0对比Thymeleaf2.0有着翻天覆地的变化,几乎是全部重写了整个Thymeleaf引擎,在性能、效率上相比Thymeleaf2有了很大改善,能够满足更多项目的需求,且Thymeleaf3.0不再基于xml所以在html环境下有着更宽松的编程环境。

动静分离

你可能还是不明白什么才是真正的动静分离,其实这个主要是由于Thymeleaf模板基于html,后缀也是.html,所以这样就会产生一些有趣的灵魂。

对于传统jsp或者其他模板来说,没有一个模板引擎的后缀为.html,就拿jsp来说jsp的后缀为.jsp,它的本质就是将一个html文件修改后缀为.jsp,然后在这个文件中增加自己的语法、标签然后执行时候通过后台处理这个文件最终返回一个html页面。

浏览器无法直接识别.jsp文件,需要借助网络(服务端)才能进行访问;而Thymeleaf用html做模板可以直接在浏览器中打开。开发者充分考虑html页面特性,将Thymeleaf的语法通过html的标签属性来定义完成,这些标签属性不会影响html页面的完整性和显示。如果通过后台服务端访问页面服务端会寻找这些标签将服务端对应的数据替换到响应位置实现动态页面!大体区别可以参照下图:

在这里插入图片描述
上图的意思就是如果直接打开这个html那么浏览器会对th等标签忽视而显示原始的内容。如果通过服务端访问那么服务端将先寻找th标签将服务端储存的数据替换到对应位置。具体效果可以参照下图,下图即为一个动静结合的实例。

  • 右上角为动态页面通过服务端访问,数据显示为服务端提供的数据,样式依然为html的样式
  • 右下角为静态页面可通过浏览器直接打开,数据为初始的数据

在这里插入图片描述

动态页面每次修改打开都需要重新启动程序、输入链接,这个过程其实是相对漫长的。如果界面设计人员用这种方式进行页面设计时间成本高并且很麻烦,可通过静态页面设计样式,设计完成通过服务端访问即可达成目标UI的界面和应用,达到动静分离的效果。这个特点和优势是所有模板引擎中Thymeleaf所独有的

模板技术与前端框架的区别

在这里插入图片描述

thymeleaf入门程序

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example.lx</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!--        解决thymeleaf页面修改必须得重启服务器的问题,页面修改之后,需要重新build修改的页面-->
		<!--        前提是需要关闭thymeleaf的缓存-->
		<!-- 		idea快捷键 crtl + f9 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional>
		</dependency>
		<!--        页面渲染我们使用thymeleaf,这应该和freemarker是差不多的,都是模板引擎-->
		<!--        优点:它是一个自然化语言,编写的语言前端可以直接使用,方便前后人员的分工合作-->
		<!--        缺点:性能比其他模板引擎要低一点,但是我们在生产环境开启了它的缓存功能,性能也是很高的-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

package com.example.lx.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {


	// thymeleaf目录下的所有页面,只能通过controller来跳转!
//    public class ThymeleafProperties {
//        private String prefix = "classpath:/templates/";
//        private String suffix = ".html";
    @GetMapping("/index")
    public String index(Model model){

        model.addAttribute("message","thymeleaf");

		// 默认前缀和后缀是 classpath:/templates/和.html
		// 所以这儿写个index就可以跳转到页面了
        return "index";
    }
}

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <!--所有的html元素都可以被 thymeleaf 替换接管,	 th:元素名-->
    <title th:text="${message}">index</title>
</head>

</html>

配置

// 是否允许页面缓存的配置,我们在开发时候要确保页面是最新的所以需要禁用缓存;
// 而在上线运营时可能页面不常改动为了减少服务端压力以及提升客户端响应速度会允许页面缓存的使用
spring.thymeleaf.cache=false

语法

链接表达式 @

上面我们已经学习到Thymeleaf是一个基于html的模板引擎,但是我们还是需要加入特定标签来声明和使用Thymeleaf的语法。我们需要在Thymeleaf的头部加Thymeleaf标识(名称空间):

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

在Thymeleaf 中,如果想引入链接比如link,href,src,需要使用@{资源地址}引入资源。其中资源地址可以static目录下的静态资源,也可以是互联网中的绝对资源。

<!-- 引入css,static目录下的可以直接引入,不需要加static -->
<link rel="stylesheet" th:href="@{index.css}">

<!-- 引入JavaScript -->
<script type="text/javascript" th:src="@{index.js}"></script>

<!-- 超链接,会直接定位到controller中的请求 -->
<a th:href="@{index.html}">超链接</a>

<!-- 也可以用html原始的 -->
<script src="/static/reg/js/jQuery/jquery-3.1.1.js"></script>

<!-- 导入图片 -->
<img th:src="@{img/1700557561889.jpg}" width="172" height="172">

这样启动程序访问页面,页面的内容就自动修改成标准html语法格式的内容:

在这里插入图片描述

消息表达式#

// templates目录下建立home.properties
bigsai.nane=bigsai
bigsai.age=22
province=Jiang Su

// application.properties
spring.messages.basename=templates/home

//html
<td th:text="#{bigsai.name}"></td>

常用

<!--
	引入thymeleaf,前提得先引入它的maven依赖
-->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">


<!--在thymeleaf中使用js调用自定义函数-->
<a href="javascript:searchByKeyWord()" >搜索</a>

<!--${}内可以做运算-->
<li><a th:href="${'javascript:searchProducts(&quot;catalog3Id&quot;,' + catalog.catalogId + ')'}" 
th:text="${catalog.catalogName}"></a></li>


function searchByKeyWord(){

        searchProducts("keyword",$('#keyword_input').val())
    }



<!--
	nav:${result.navs} result是controller返回的对象,navs是该对象的属性
	th:each 循环
	th:text 标签内文本展示
	nav 变量
-->
<a th:href="${nav.link}" th:each="nav:${result.navs}">
<span th:text="${nav.navName}"></span> :
<span th:text="${nav.navValue}"></span> ×</a>


<!-- [[${}]] 标签外文本展示 -->
<a th:attr="style=${(#strings.isEmpty(p) || #strings.startsWith(p,'hotScore'))?'border-color: #e4393c;color:#FFF;background: #e4393c':'border-color: #CCC;color:#333;background: #fff'}"
                               th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))?'sort_a desc':'sort_a'}"
                               sort="hotScore" href="#">综合排序[[${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))?'↓':'↑'}]]</a>


<!--图片-->
<img th:src="${brand.brandImg}" alt="">




<!--
	th:attr 属性或者自定义属性
-->
<a th:attr="style=${(#strings.isEmpty(p) 
|| #strings.startsWith(p,'hotScore'))?
'border-color: #e4393c;color:#FFF;background: #e4393c':'border-color: #CCC;color:#333;background: #fff'}"

th:attr="pn=${result.pageNum - 1}"




<!--<h1>测试转义字符</h1>-->
<div th:text="${msg}"/>
<!--只输出,h1字体的 测试转义字符
    被转义,标签可以被识别出来
-->
<div th:utext="${msg}"/>

函数

// 判断是否为空
#strings.isEmpty()

// 判断a是否以b开头
#strings.startsWith(a,b)
#strings.endsWith()

// 截取priceRange中 _ 之后的值
#strings.substringAfter(priceRange,'_')
#strings.substringBefore(priceRange,'_')

// 判断列表中是否有该值
#lists.contains(result.attrIds,attr.attrId)

// 数字格式化
// param1 要格式化的数字
// param2 整数位数,如果写的少了,也不会抹去
// param3 小数位数
#numbers.formatDecimal(a,b,c)

// 以b分割a,return String[]
#strings.arraySplit(a,b)

// map a中是否包含key b
#maps.containsKey(a,'b')


// 获取当前时间
#dates.createNow().getTime()

// 格式化日期
${#dates.format(new java.util.Date(),"yyyy-MM-dd HH:mm:ss")

特殊字符

&quot; 双引号

优先级

例如:th:if和th:with在同一标签内,th:with定义一个变量,th:if使用这个变量,因为优先级的关系,th:if先使用,th:with后定义,这样就出现了错误。

// 在上面的优先级高
// 没有th:else
th:if

th:with

其他

<!--        

	param 是controller返回的一个map,在这儿可以用来获取参数

	这个参数应该是框架固定的,固定写法

-->
<input id="keyword_input" type="text" placeholder="手机" th:value="${param.url}" />




<!-- 直接使用这种方式来改变样式会报错,提示param.sort应该应用在text中,巴拉巴拉之类的,
 所以我们使用th:with定义变量,先吧param.sort取出来-->
 
<!--  <a class="sort_a" th:attr="style=${(#strings.isEmpty(param.sort) 
|| #strings.startsWith(param.sort,'hotScore'))?
'border-color: #e4393c;color:#FFF;background: #e4393c'
:'border-color: #CCC;color:#333;background: #fff'}" 
sort="hotScore" href="/static/search/#">综合排序</a> -->

<!-- th:class 自定义class -->
<div class="filter_top_left" th:with="p = ${param.sort},priceRange = ${param.skuPrice}">
<!--   sort=hotScore_asc/desc -->
<a th:attr="style=${(#strings.isEmpty(p) || #strings.startsWith(p,'hotScore'))?'border-color: #e4393c;color:#FFF;background: #e4393c':'border-color: #CCC;color:#333;background: #fff'}"
th:class="${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))?'sort_a desc':'sort_a'}"
sort="hotScore" href="#">综合排序[[${(!#strings.isEmpty(p) && #strings.startsWith(p,'hotScore') && #strings.endsWith(p,'desc'))?'↓':'↑'}]]</a>




<!--格式化字符串-->
<a th:href="|http://item.gulimall.com/${product.skuId}.html|">
	<img th:src="${product.skuImg}" class="dim">
</a>





<!--获取session中的内容-->
session.属性

thymeleaf页面跳转

从控制器类向前端页面跳转时,指定传递的页面

返回的就是我要显示的页面名

当返回的就是我要显示的页面名时,使用thymeleaf解析器(thymeleaf设置的视图解析器)解析出文件地址,转发方式跳转到该页面,具体代码见入门程序。

forward 请求转发

当以forward为前缀时,创建internalsersourceview视图,此时不会直接被thymeleaf解析,而是将forward去掉,寻找控制器的value一样的方法,用thymeleaf解析该方法的返回值,并请求转发访问该页面

// 会寻找有 @RequestMapping("/test_thymeleaf")的控制方法,
// 用thymeleaf解析该方法的返回值,得出文件的路径,并请求转发跳转至该页面
@RequestMapping("/forward_thymeleaf")
    public String forwrad_thyleaf()
    {
        return "forward:/test_thymeleaf";
    }

redirect

当以redirect为前缀时,创建internalsersourceview视图,此时不会直接被thymeleaf解析,而是将redirect去掉,寻找控制器的value一样的方法,用thymeleaf解析该方法的返回值,并重定向访问该页面

// 会寻找有 @RequestMapping("/test_thymeleaf")的控制方法,
// 用thymeleaf解析该方法的返回值,得出文件的路径,并请求重定向至该页面
@RequestMapping("/forward_thymeleaf")
    public String forwrad_thyleaf()
    {
        return "redirect:/test_thymeleaf";
    }

使用model modelandview

//  forward和redirect是不管返回值是model modelandview  都可以使用
@RequestMapping(value = "/update2")
public ModelAndView after_choose_change(Books books)
{
    booksServiceimpl.updateBook(books);
    System.out.println("books 这是修改后的books"+books);
    ModelAndView modelAndView=new ModelAndView();
    modelAndView.setViewName("redirect:/books/allbooks");
    return modelAndView;
}

不需要传递数据的跳转

package com.atlinxi.gulimall.authserver.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


/**
 * 发送一个请求直接跳转到一个页面,我们又不想controller中写这些空方法
 * SpringMvc viewcontroller:将请求和页面映射过来
 * @return
 */
@Configuration
public class GulimallWebConfig implements WebMvcConfigurer {



    /**
     * 直接跳转(渲染一个页面),无需写自定义的controller
     *
     *
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        /**
         * * @GetMapping("/login.html")
         *      *     public String loginPage(){
         *      *
         *      *         return "login";
         *      *     }
         */
        registry.addViewController("/login.html").setViewName("login");
        registry.addViewController("/reg.html").setViewName("reg");
    }
}

不能使用ajax请求进行页面跳转

在做谷粒商城注册页面的时候,用到了通过cotroller进行页面跳转,我也忘了是为什么,可能只是想试一试,在html页面中写一个点击事件,请求controller进行页面跳转,非常简单,结果实在是出乎意料。

页面没有任何变化,请求进入controller,浏览器控制台打印返回结果,它把整个html以文本的方式打印出来了。。。。。。。

最后还是靠百度,ajax 只能实现局部刷新,无法执行整体的页面跳转并携带数据,使用重定向也无法跳转到其他的页面。

解决:

  • 把 Ajax 请求改成了 form 表单
  • location.href

功能实现

国际化

说一下引入bootstrap的问题

真tm奇怪,刚导入bootstrap.min.css的时候,怎么都不能用,自己写一个就能用,试了以下几种办法

  1. 把css文件复制一下改个名字
  2. 配置文件添加 spring.mvc.staticPathPattern = /**
  3. 在引入路径前面加 /,否则有时候不知道为什么它引入的路径好像会自动带上请求的路径
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>Signin Template · Bootstrap v4 中文文档 v4.6</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/4.6/examples/sign-in/">

    <!-- Bootstrap core CSS -->
    <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet" type="text/css">

</head>
<body>

<form style="width: 350px;text-align: center;margin: auto;margin-top: 100px">
    <img class="mb-4" src="https://getbootstrap.com/docs/4.6/assets/brand/bootstrap-solid.svg" alt="" width="72"
         height="72">
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
    <input type="email" id="inputEmail" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
    <input type="password" id="inputPassword" class="form-control" th:placeholder="#{login.password}" required="">
    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" value="remember-me"> [[#{login.remember}]]
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.tip}">Sign in</button>
    <p class="mt-5 mb-3 text-muted">© 2017-2023</p>
    <a class="btn btn-sm mb-1" style="margin: auto" th:href="@{index.html(language='zh_CN')}">中文</a>
    <a class="btn btn-sm mb-1" style="margin: auto" th:href="@{index.html(language='en_US')}">English</a>
</form>


</body>
</html>
# 创建三个国际化文件
# login.properties,login_en_US.properties,login_zh_CN.properties
# 分别对应默认,英文、中文

# login_zh_CN.properties
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名


# login_en_US.properties
login.password=password
login.remember=remember me
login.tip=please sign in
login.username=username
spring:
  thymeleaf:
    cache: false
#    国际化,在resources下创建这个文件夹把上面三个国际化文件放进去
  messages:
    basename: i18n.login

server:
  servlet:
    context-path: /lx

package com.example.thymeleaf.config;


import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

//
public class MyLocalResolver implements LocaleResolver {

    // 解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {

        // 获取请求中的语言参数
        String language = request.getParameter("language");

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

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


        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}




package com.example.thymeleaf.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class MyMvcConfig implements WebMvcConfigurer {


    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }


    // 自定义的国际化组件就生效了
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocalResolver();
    }
}

登录(使用拦截器)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>Signin Template · Bootstrap v4 中文文档 v4.6</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/4.6/examples/sign-in/">

    <!-- Bootstrap core CSS -->
    <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet" type="text/css">

</head>
<body>

<form style="width: 350px;text-align: center;margin: auto;margin-top: 100px" th:action="@{/user/login}">
    <img class="mb-4" src="https://getbootstrap.com/docs/4.6/assets/brand/bootstrap-solid.svg" alt="" width="72"
         height="72">
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
    <p style="color: red" th:if="${!#strings.isEmpty(msg)}" th:text="${msg}"></p>
    <input name="username" type="email" id="inputEmail" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
    <input name="password" type="password" id="inputPassword" class="form-control" th:placeholder="#{login.password}" required="">
    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" value="remember-me"> [[#{login.remember}]]
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.tip}">Sign in</button>
    <p class="mt-5 mb-3 text-muted">© 2017-2023</p>
    <a class="btn btn-sm mb-1" style="margin: auto" th:href="@{index.html(language='zh_CN')}">中文</a>
    <a class="btn btn-sm mb-1" style="margin: auto" th:href="@{index.html(language='en_US')}">English</a>
</form>


</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>Signin Template · Bootstrap v4 中文文档 v4.6</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/4.6/examples/sign-in/">

    <!-- Bootstrap core CSS -->
    <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet" type="text/css">

</head>
<body>

登录成功

[[${session.lxLoginUser}]]

</body>
</html>
package com.example.thymeleaf.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;

@Controller
public class LoginController {

    @GetMapping("/user/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        Model model, HttpServletRequest request){

        if (!ObjectUtils.isEmpty(username) && "123".equals(password)){
            request.getSession().setAttribute("lxLoginUser",username);
            return "redirect:/main.html";
        }else {
            model.addAttribute("msg","用户名或者密码错误");
            return "index";
        }

    }
}

package com.example.thymeleaf.config;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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

        Object lxLoginUser = request.getSession().getAttribute("lxLoginUser");

        if (lxLoginUser==null){
            request.setAttribute("msg","没有权限,请先登录");
            request.getRequestDispatcher("index.html").forward(request,response);
        }else {
            return true;
        }

        return false;
    }
}

package com.example.thymeleaf.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@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");
    }


    // 自定义的国际化组件就生效了
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocalResolver();
    }


    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // 注册拦截器,拦截所有请求,
        // 但不拦截 "/user/login","/index.html","/" 和静态资源
        registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
                .excludePathPatterns(
                        "/user/login","/index.html","/",
                        "/css/**","/js/**","/img/**");
    }


}

展示员工列表

  • 公共页面的抽取
  • 公共页面传参实现菜单栏的动态的赋值class属性(选中时高亮)
  • 列表循环展示
package com.example.thymeleaf.controller;

import com.example.thymeleaf.pojo.Employee;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Controller
public class EmployeeController {

    // 测试用,就先不写 service dao,直接返回数据
    @GetMapping("empList")
    public String empList(Model model){

        List<Employee> employeeList = new ArrayList<>();

        employeeList.add(new Employee("房思琪",14,new BigDecimal(200)));
        employeeList.add(new Employee("思纹姐姐",24,new BigDecimal(20000)));
        employeeList.add(new Employee("首饰店老板",24,new BigDecimal(10000000)));

        model.addAttribute("empList",employeeList);

        return "empList";

    }


}

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>


<!--公共模块儿页面,

    todo 不需要引入相关的css?在引入的页面引入就可以了?-->


<!--th:fragment 定义一个fragment,可以让别的地方引用-->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow" th:fragment="topBar">
    <a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#">[[${session.lxLoginUser}]]</a>
    <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse" data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>
    <ul class="navbar-nav px-3">
        <li class="nav-item text-nowrap">
            <a class="nav-link" href="#">Sign out</a>
        </li>
    </ul>
</nav>


<nav th:fragment="sideBar" id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
    <div class="sidebar-sticky pt-3">
        <ul class="nav flex-column">
            <li class="nav-item">
                <a th:class="${active=='main.html'?'nav-link active':'nav-link'} " th:href="@{index.html}">
                    <span data-feather="home"></span>
                    Dashboard <span class="sr-only">(current)</span>
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">
                    <span data-feather="file"></span>
                    Orders
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">
                    <span data-feather="shopping-cart"></span>
                    Products
                </a>
            </li>
            <li class="nav-item">
                <a th:class="${active=='empList'?'nav-link active':'nav-link'}" th:href="@{empList}">
                    <span data-feather="users"></span>
                    员工管理
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">
                    <span data-feather="bar-chart-2"></span>
                    Reports
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">
                    <span data-feather="layers"></span>
                    Integrations
                </a>
            </li>
        </ul>

        <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
            <span>Saved reports</span>
            <a class="d-flex align-items-center text-muted" href="#" aria-label="Add a new report">
                <span data-feather="plus-circle"></span>
            </a>
        </h6>
        <ul class="nav flex-column mb-2">
            <li class="nav-item">
                <a class="nav-link" href="#">
                    <span data-feather="file-text"></span>
                    Current month
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">
                    <span data-feather="file-text"></span>
                    Last quarter
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">
                    <span data-feather="file-text"></span>
                    Social engagement
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">
                    <span data-feather="file-text"></span>
                    Year-end sale
                </a>
            </li>
        </ul>
    </div>
</nav>


</body>
</html>

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
    <meta name="generator" content="Hugo 0.101.0">
    <title>Dashboard Template · Bootstrap v4.6</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/4.6/examples/dashboard/">



    <!-- Bootstrap core CSS -->
    <link th:href="@{css/bootstrap.min.css}" rel="stylesheet">



    <style>
        .bd-placeholder-img {
            font-size: 1.125rem;
            text-anchor: middle;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }

        @media (min-width: 768px) {
            .bd-placeholder-img-lg {
                font-size: 3.5rem;
            }
        }
    </style>


    <!-- Custom styles for this template -->
    <link th:href="@{css/dashboard.css}" rel="stylesheet">
</head>
<body>

<!-- 引入其他页面的fragment
    insert 插入一层div
    replace 替换当前元素,一般用这个
-->
<div th:insert="~{commons/commons::topBar}"></div>
<div th:replace="~{commons/commons::sideBar(active='empList')}"></div>
<div class="container-fluid">
    <div class="row">


        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
            <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
                <h1 class="h2">员工管理</h1>
                <div class="btn-toolbar mb-2 mb-md-0">
                    <div class="btn-group mr-2">
                        <button type="button" class="btn btn-sm btn-outline-secondary">Share</button>
                        <button type="button" class="btn btn-sm btn-outline-secondary">Export</button>
                    </div>
                    <button type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle">
                        <span data-feather="calendar"></span>
                        This week
                    </button>
                </div>
            </div>

            <!--            <canvas class="my-4 w-100" id="myChart" width="900" height="380"></canvas>-->

            <div class="table-responsive">
                <table class="table table-striped table-sm">
                    <thead>
                    <tr>
                        <th>姓名</th>
                        <th>年龄</th>
                        <th>工资</th>
                        <th>操作</th>
                    </tr>
                    </thead>
                    <tbody>

                    <tr th:each="item:${empList}">
                        <td>[[${item.name}]]</td>
                        <td>[[${item.age}]]</td>
                        <td>[[${item.salary}]]</td>

                        <td>
                            <button class="btn btn-sm btn-primary">编辑</button>
                            <button class="btn btn-sm btn-danger">删除</button>
                        </td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </main>
    </div>
</div>


<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="../assets/js/vendor/jquery.slim.min.js"><\/script>')</script><script src="../assets/dist/js/bootstrap.bundle.min.js"></script>


<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
<script src="dashboard.js"></script>
</body>
</html>

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
    <meta name="generator" content="Hugo 0.101.0">
    <title>Dashboard Template · Bootstrap v4.6</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/4.6/examples/dashboard/">



    <!-- Bootstrap core CSS -->
    <link th:href="@{css/bootstrap.min.css}" rel="stylesheet">



    <style>
        .bd-placeholder-img {
            font-size: 1.125rem;
            text-anchor: middle;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }

        @media (min-width: 768px) {
            .bd-placeholder-img-lg {
                font-size: 3.5rem;
            }
        }
    </style>


    <!-- Custom styles for this template -->
    <link th:href="@{css/dashboard.css}" rel="stylesheet">
</head>
<body>



<div th:replace="~{commons/commons::topBar}"></div>
<!--引用fragment传递参数-->
<div th:replace="~{commons/commons::sideBar(active='main.html')}"></div>

<div class="container-fluid">
    <div class="row">

        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
            <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
                <h1 class="h2">Dashboard</h1>
                <div class="btn-toolbar mb-2 mb-md-0">
                    <div class="btn-group mr-2">
                        <button type="button" class="btn btn-sm btn-outline-secondary">Share</button>
                        <button type="button" class="btn btn-sm btn-outline-secondary">Export</button>
                    </div>
                    <button type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle">
                        <span data-feather="calendar"></span>
                        This week
                    </button>
                </div>
            </div>

<!--            <canvas class="my-4 w-100" id="myChart" width="900" height="380"></canvas>-->

            <h2>Section title</h2>
            <div class="table-responsive">
                <table class="table table-striped table-sm">
                    <thead>
                    <tr>
                        <th>#</th>
                        <th>Header</th>
                        <th>Header</th>
                        <th>Header</th>
                        <th>Header</th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr>
                        <td>1,001</td>
                        <td>random</td>
                        <td>data</td>
                        <td>placeholder</td>
                        <td>text</td>
                    </tr>
                    <tr>
                        <td>1,002</td>
                        <td>placeholder</td>
                        <td>irrelevant</td>
                        <td>visual</td>
                        <td>layout</td>
                    </tr>
                    <tr>
                        <td>1,003</td>
                        <td>data</td>
                        <td>rich</td>
                        <td>dashboard</td>
                        <td>tabular</td>
                    </tr>
                    <tr>
                        <td>1,003</td>
                        <td>information</td>
                        <td>placeholder</td>
                        <td>illustrative</td>
                        <td>data</td>
                    </tr>
                    <tr>
                        <td>1,004</td>
                        <td>text</td>
                        <td>random</td>
                        <td>layout</td>
                        <td>dashboard</td>
                    </tr>
                    <tr>
                        <td>1,005</td>
                        <td>dashboard</td>
                        <td>irrelevant</td>
                        <td>text</td>
                        <td>placeholder</td>
                    </tr>
                    <tr>
                        <td>1,006</td>
                        <td>dashboard</td>
                        <td>illustrative</td>
                        <td>rich</td>
                        <td>data</td>
                    </tr>
                    <tr>
                        <td>1,007</td>
                        <td>placeholder</td>
                        <td>tabular</td>
                        <td>information</td>
                        <td>irrelevant</td>
                    </tr>
                    <tr>
                        <td>1,008</td>
                        <td>random</td>
                        <td>data</td>
                        <td>placeholder</td>
                        <td>text</td>
                    </tr>
                    <tr>
                        <td>1,009</td>
                        <td>placeholder</td>
                        <td>irrelevant</td>
                        <td>visual</td>
                        <td>layout</td>
                    </tr>
                    <tr>
                        <td>1,010</td>
                        <td>data</td>
                        <td>rich</td>
                        <td>dashboard</td>
                        <td>tabular</td>
                    </tr>
                    <tr>
                        <td>1,011</td>
                        <td>information</td>
                        <td>placeholder</td>
                        <td>illustrative</td>
                        <td>data</td>
                    </tr>
                    <tr>
                        <td>1,012</td>
                        <td>text</td>
                        <td>placeholder</td>
                        <td>layout</td>
                        <td>dashboard</td>
                    </tr>
                    <tr>
                        <td>1,013</td>
                        <td>dashboard</td>
                        <td>irrelevant</td>
                        <td>text</td>
                        <td>visual</td>
                    </tr>
                    <tr>
                        <td>1,014</td>
                        <td>dashboard</td>
                        <td>illustrative</td>
                        <td>rich</td>
                        <td>data</td>
                    </tr>
                    <tr>
                        <td>1,015</td>
                        <td>random</td>
                        <td>tabular</td>
                        <td>information</td>
                        <td>text</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </main>
    </div>
</div>


<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="../assets/js/vendor/jquery.slim.min.js"><\/script>')</script><script src="../assets/dist/js/bootstrap.bundle.min.js"></script>


<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
<script src="dashboard.js"></script>
</body>
</html>

新增员工

spring:
  thymeleaf:
    cache: false
#    国际化
  messages:
    basename: i18n.login
  mvc:
    static-path-pattern: /**
    format:
#      前端提交的日期格式传到后端的时候的格式化
      date-time: yyyy-MM-dd HH:mm:ss
server:
  servlet:
    context-path: /lx
package com.example.thymeleaf.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;

import java.math.BigDecimal;
import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {

    private Integer id;
    private String lastName;
    private String email;
    private String gender;
    private String department;

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime birth;
}




package com.example.thymeleaf.controller;

import com.example.thymeleaf.pojo.Employee;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Controller
public class EmployeeController {

    /**
     * 测试用,该controller就先不写 service dao,直接返回数据
     * @param model
     * @return
     */


    private static List<Employee> employeeList = new ArrayList<>();

    private static void initEmp(Employee employee){

        employeeList = new ArrayList<>();

        LocalDateTime of = LocalDateTime.of(2019, 11, 3, 15, 34, 54);

        employeeList.add(new Employee(1001,"房思琪","asfds@163.com","女","教学部",of));
        employeeList.add(new Employee(1002,"林奕含","agfhghds@163.com","女","市场部",of));
        employeeList.add(new Employee(1003,"叶文洁","hodfbnd@163.com","女","教研部",of));
        employeeList.add(new Employee(1004,"章北海","sjfghg@163.com","男","运营部",of));
        employeeList.add(new Employee(1005,"云天明","orindo@163.com","男","后勤部",of));


        if (employee!=null){
            employee.setId(1006);
            employeeList.add(employee);
        }
    }


    @GetMapping("empList")
    public String empList(Model model){


        initEmp(null);
        model.addAttribute("empList",employeeList);

        return "empList";

    }





    @GetMapping("emp")
    public String addPage(Model model){

        // 查出所有的部门信息
        List<String> departments = Arrays.asList("教学部","市场部","教研部","运营部","后勤部");
        model.addAttribute("departments",departments);

        return "add";
    }




    @PostMapping("emp")
    public String addEmp(Model model,Employee employee){

        initEmp(employee);

        model.addAttribute("empList",employeeList);
        return "empList";

        // 实际业务中,这块儿应该是保存到数据库之后,重定向到empList的
    }






}

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
    <meta name="generator" content="Hugo 0.101.0">
    <title>Dashboard Template · Bootstrap v4.6</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/4.6/examples/dashboard/">



    <!-- Bootstrap core CSS -->
    <link th:href="@{css/bootstrap.min.css}" rel="stylesheet">



    <style>
        .bd-placeholder-img {
            font-size: 1.125rem;
            text-anchor: middle;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }

        @media (min-width: 768px) {
            .bd-placeholder-img-lg {
                font-size: 3.5rem;
            }
        }
    </style>


    <!-- Custom styles for this template -->
    <link th:href="@{css/dashboard.css}" rel="stylesheet">
</head>
<body>

<!-- 引入其他页面的fragment
    insert 插入一层div
    replace 替换当前元素,一般用这个
-->
<div th:insert="~{commons/commons::topBar}"></div>
<div th:replace="~{commons/commons::sideBar(active='empList')}"></div>
<div class="container-fluid">
    <div class="row">


        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">

            <form th:action="@{/emp}" method="post">
                <div class="form-group">
                    <label for="exampleInputEmail1">LastName</label>
                    <input name="lastName" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="ks">
                    <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
                </div>
                <div class="form-group">
                    <label for="exampleFormControlInput1">Email</label>
                    <input name="email" type="email" class="form-control" id="exampleFormControlInput1" placeholder="name@example.com">
                </div>

                <div style="margin-bottom: 10px">
                    gender &nbsp;
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="radio" name="gender" id="inlineRadio1" value="">
                        <label class="form-check-label" for="inlineRadio1"></label>
                    </div>
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="radio" name="gender" id="inlineRadio2" value="" checked="checked">
                        <label class="form-check-label" for="inlineRadio2"></label>
                    </div>
                </div>

                <div class="form-group">
                    <label for="exampleInputEmail1">department</label>
                    <select class="form-control" name="department" placeholder="教学部">

                        <option name="department" th:each="item:${departments}" th:value="${item}">[[${item}]]</option>

                    </select>
                </div>
                <div class="form-group">
                    <label for="exampleInputEmail1">birth</label>
                    <input name="birth" type="text" class="form-control" id="birth" aria-describedby="departmentHelp" placeholder="2019-11-03 15:34:54">
                </div>

                <button type="submit" class="btn btn-primary">添加</button>
            </form>
        </main>
    </div>
</div>


<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="../assets/js/vendor/jquery.slim.min.js"><\/script>')</script><script src="../assets/dist/js/bootstrap.bundle.min.js"></script>


<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
<script src="dashboard.js"></script>
</body>
</html>

修改员工

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
    <meta name="generator" content="Hugo 0.101.0">
    <title>Dashboard Template · Bootstrap v4.6</title>

    <link rel="canonical" href="https://getbootstrap.com/docs/4.6/examples/dashboard/">



    <!-- Bootstrap core CSS -->
    <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">



    <style>
        .bd-placeholder-img {
            font-size: 1.125rem;
            text-anchor: middle;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }

        @media (min-width: 768px) {
            .bd-placeholder-img-lg {
                font-size: 3.5rem;
            }
        }
    </style>


    <!-- Custom styles for this template -->
    <link th:href="@{/css/dashboard.css}" rel="stylesheet">
</head>
<body>

<!-- 引入其他页面的fragment
    insert 插入一层div
    replace 替换当前元素,一般用这个
-->
<div th:insert="~{commons/commons::topBar}"></div>
<div th:replace="~{commons/commons::sideBar(active='empList')}"></div>
<!--<div class="container-fluid">-->
<!--    <div class="row">-->


        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">

            <form th:action="@{/emp}" method="post">
                <div class="form-group">
                    <label for="exampleInputEmail1">LastName</label>
                    <input th:value="${emp.lastName}" name="lastName" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="ks">
                    <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
                </div>
                <div class="form-group">
                    <label for="exampleFormControlInput1">Email</label>
                    <input th:value="${emp.email}" name="email" type="email" class="form-control" id="exampleFormControlInput1" placeholder="name@example.com">
                </div>

                <div style="margin-bottom: 10px">
                    gender &nbsp;
                    <div class="form-check form-check-inline">
                        <input th:checked="${emp.gender=='男'}" class="form-check-input" type="radio" name="gender" id="inlineRadio1" value="">
                        <label class="form-check-label" for="inlineRadio1"></label>
                    </div>
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="radio" name="gender" id="inlineRadio2" value="" th:checked="${emp.gender=='女'}">
                        <label class="form-check-label" for="inlineRadio2"></label>
                    </div>
                </div>

                <div class="form-group">
                    <label for="exampleInputEmail1">department</label>
                    <select class="form-control" name="department" placeholder="教学部">

                        <option th:selected="${emp.department==item}" name="department" th:each="item:${departments}" th:value="${item}">[[${item}]]</option>

                    </select>
                </div>
                <div class="form-group">
                    <label for="exampleInputEmail1">birth</label>
                    <input th:value="${#strings.replace(emp.birth,'T',' ')}" name="birth" type="text" class="form-control" id="birth" aria-describedby="departmentHelp" placeholder="2019-11-03 15:34:54">
                </div>

                <button type="submit" class="btn btn-primary">修改</button>
            </form>
        </main>
    </div>
</div>


<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="../assets/js/vendor/jquery.slim.min.js"><\/script>')</script>


<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
</body>
</html>

package com.example.thymeleaf.controller;

import com.example.thymeleaf.pojo.Employee;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Controller
public class EmployeeController {

    /**
     * 测试用,该controller就先不写 service dao,直接返回数据
     * @param model
     * @return
     */


    private static List<Employee> employeeList = new ArrayList<>();


    private static List<String> departments = Arrays.asList("教学部","市场部","教研部","运营部","后勤部");

    private static void initEmp(Employee employee){

        employeeList = new ArrayList<>();

        LocalDateTime of = LocalDateTime.of(2019, 11, 3, 15, 34, 54);

        employeeList.add(new Employee(1001,"房思琪","asfds@163.com","女","教学部",of));
        employeeList.add(new Employee(1002,"林奕含","agfhghds@163.com","女","市场部",of));
        employeeList.add(new Employee(1003,"叶文洁","hodfbnd@163.com","女","教研部",of));
        employeeList.add(new Employee(1004,"章北海","sjfghg@163.com","男","运营部",of));
        employeeList.add(new Employee(1005,"云天明","orindo@163.com","男","后勤部",of));


        if (employee!=null){
            employee.setId(1006);
            employeeList.add(employee);
        }
    }


    @GetMapping("empList")
    public String empList(Model model){


        initEmp(null);
        model.addAttribute("empList",employeeList);

        return "empList";

    }





    @GetMapping("emp")
    public String addPage(Model model){

        // 查出所有的部门信息
        model.addAttribute("departments",departments);

        return "add";
    }




    @PostMapping("emp")
    public String addEmp(Model model,Employee employee){

        initEmp(employee);

        model.addAttribute("empList",employeeList);
        return "empList";

        // 实际业务中,这块儿应该是保存到数据库之后,重定向到empList的
    }



    // 员工的修改页面
    @GetMapping("/emp/{id}")
    public String toUpdateEmp(@PathVariable("id") Integer id,Model model){

        List<Employee> collect = employeeList.stream().filter(item -> item.getId().equals(id)).limit(1).collect(Collectors.toList());
        model.addAttribute("emp",collect.get(0));

        model.addAttribute("departments",departments);

        return "update";

    }


}

删除员工

  • 删除员工
  • 注销登录
  • 404、500页面
<a class="btn btn-sm btn-danger" th:href="@{/emp/del/{id}(id=${item.id})}">删除</a>

<li class="nav-item text-nowrap">
	<a class="nav-link" th:href="@{/user/signOut}">Sign out</a>
</li>
@GetMapping("/emp/del/{id}")
    public String toDeleteEmp(@PathVariable("id") Integer id,Model model){

        List<Employee> collect = employeeList.stream().filter(item -> !(item.getId().equals(id))).collect(Collectors.toList());
        model.addAttribute("empList",collect);

        // todo 删除是有bug的,重定向后的方法中又重新填充了一遍数据,
        // 实际工作中就是一个删除,这里就不再赘述了
        return "redirect:/empList";

    }



@GetMapping("/user/signOut")
    public String signOut(HttpServletRequest request){
        // 用户清空sesiion的内容,并且销毁session对象
        // session就是一个会话一个session,所以清除的就是当前用户的,不会影响到其他用户
        request.getSession().invalidate();
        return "redirect:/index.html";

    }

在resources下创建404、500的html,当访问发生对应的错误后就会跳转到相应的页面。
在这里插入图片描述

部分内容转载自:
https://zhuanlan.zhihu.com/p/163391278(写的超级好)
https://blog.csdn.net/sharesb/article/details/124381312
https://blog.csdn.net/weixin_45784666/article/details/121450116

为什么你没有告诉我?
盯着日期看,那是五年前的秋天,那年,张阿姨的女儿终于结婚了,伊纹姐姐搬来没多久,一维哥哥刚刚开始打她,今年她们高中毕业,那年她们十三岁。
故事必须重新讲过。

房思琪的初恋乐园
林奕含

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: springboot+thymeleaf项目是一种基于Java语言开发的Web应用程序。它采用了Spring Boot框架和Thymeleaf模板引擎,可以快速地搭建一个高效、可靠、易于维护的Web应用程序。该项目具有以下特点: 1. 简单易用:Spring Boot框架提供了一系列的自动化配置,使得开发者可以快速地搭建一个Web应用程序,而不需要过多的配置。 2. 高效可靠:Spring Boot框架采用了一系列的优化措施,使得应用程序具有高效、可靠的性能。 3. 易于维护:Thymeleaf模板引擎提供了一种简单、易于维护的模板语言,使得开发者可以快速地开发出具有良好可读性的Web应用程序。 总之,springboot+thymeleaf项目是一种非常优秀的Web应用程序开发框架,可以帮助开发者快速地开发出高效、可靠、易于维护的Web应用程序。 ### 回答2: Spring Boot是一个基于Spring框架的快速开发框架,这个框架的优点在于其简单易用,能够快速搭建一个Java Web应用程序,无需进行复杂的配置和繁琐的XML文件编写。而Thymeleaf则是一种Web和HTML的模板引擎,可以方便地处理文本、链接和表单等元素,支持多重继承和页面片段的复用等特性。 Spring Boot和Thymeleaf的结合,可以帮助开发人员更加简便地搭建Web应用程序。在使用Spring Boot进行项目开发时,可以使用Thymeleaf来完成Web开发的视图层,进行模版板的渲染和数据绑定。这样就可以很直接地将数据通过模板引擎展现出来,且更加方便。 在一个Spring Boot Thymeleaf项目的构建中,需要进行如下步骤: 1. 首先,引入Spring Boot和Thymeleaf的依赖以及其他必要的依赖,例如web和mybatis等相关组件。 2. 创建一个Controller类,并使用@Controller注解将类标记为Controller,编写具体的Action方法,这些方法可以用@RequestMapping或@GetMapping等注解来定义处理请求的URL路径和请求类型等相关信息。 3. 创建一个Model类,用于封装需要传输到前端的数据和相关操作等。 4. 在Controller内部设置Model变量并将相关数据注入Model,然后将需要展现的数据作为参数传递给Thymeleaf进行渲染,最后将渲染完成后的结果返回给前端页面展现。 5. 编写HTML页面,使用Thymeleaf标签来渲染动态数据。 需要注意的是,在进行Thymeleaf模板的渲染时,需要遵守一定的规范,例如页面中的数据变量名称需与Model中的属性名称一致,引入Thymeleaf命名空间等等。 总之,Spring Boot与Thymeleaf结合使用可以帮助开发人员快速地完成Web开发,整个过程简单而且高效。使用Thymeleaf能够降低模版制作的门槛,进一步提高开发效率,并且能够提供丰富的模版处理标签,使得页面制作更加灵活。 ### 回答3: 近年来,使用SpringBootThymeleaf进行Web开发已经成为越来越多的开发者选择的方案。SpringBoot是一个基于Spring框架的快速Web应用开发框架,而Thymeleaf是一种基于HTML的模板引擎,其中需要了解的内容包括以下几点: 首先,SpringBoot框架的优点是非常明显的。它提供了很多便于使用的方法,例如自动装配,以及基于配置的许多默认值。这使得开发者可以花更少的时间和精力来开发项目,将重点放在业务逻辑和功能实现上。 其次,Thymeleaf是一种非常强大和灵活的模板引擎,其语法简单易懂,而且支持HTML5标准。它还提供了一些样式和布局的工具,以及易于使用的表达式和标签,使得Web页面开发更加容易。 当然,SpringBoot集成Thymeleaf的过程也并不复杂。只需添加thymeleaf-starter包依赖,SpringBoot将自动将Thymeleaf注册为默认的模板引擎。然后,您只需要编写Thymeleaf模板文件即可。 最后,值得注意的是,使用SpringBootThymeleaf进行Web开发的好处在于它们之间的紧密集成。这种紧密集成可以更轻松地创建动态和交互性的Web应用程序,这是传统的HTML和JavaScript不能提供的。 总的来说,SpringBootThymeleaf是一对非常强大且易于使用的Web开发工具组合,它们的出现大大提高了Web开发的效率和质量,同时也为开发人员提供了更好的开发体验。我们相信,这对于Web开发者来说是非常有价值的组合。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值