• 【编程不良人】快速入门SpringBoot学习笔记05---Thymeleaf使用及测试案例


    1. Thymeleaf

    配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    Thymeleaf是一个用于web和独立环境的现代服务器端Java模板引擎。

    Jsp也是Java模板引擎,Thymeleaf完全可以用来替代Jsp,在使用时Jsp和Thymeleaf不要混用

                                                                                                                    --摘自官网Thymeleaf

       Thymeleaf是跟Velocity、FreeMarker类似的模板引擎,它可以完全替代JSP,相较与其他的模板引擎相比, Thymeleaf在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。注意:使用时必须要经过controller。

    1.1 集成Thymeleaf模板

    pom.xml引入依赖

      
      <!--引入thymeleaf依赖-->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
      </dependency>

    application.yml编写配置

      server:
        port: 8888
        servlet:
          context-path: /spring-boot-day7
      ​
      # 配置Thymeleaf
      spring:
        thymeleaf:
          prefix: classpath:/templates/ # 指定Thymeleaf前缀目录
          suffix: .html # 指定模板后缀是否开启Thymeleaf缓存,默认是true开启缓存
                        # 在开发过程中推荐使用false(类似jsp加载)

    编写控制器测试

      package com.study.controller;
      ​
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      /**
       * @ClassName HelloController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/11 15:59
       * @Version 1.0
       */
      @Controller //一定要是@Controller,不能再使用@RestController注解
      @RequestMapping("hello")
      public class HelloController {
          @RequestMapping("hello")
          public String hello(){
              System.out.println("测试springboot与Thymeleaf的集成");
              return "index";// templates/index.html,最终封装成index.html进行返回
                             // 直接访问根路径(http://localhost:8888/spring-boot-day7/)加载出index.html,但无渲染效果
          }
      }

    在templates目录中定义模板index.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>测试SpringBoot与Thymeleaf的集成</title>
      </head>
      <body>
          <h1>Hello,Thymeleaf!</h1>
      </body>
      </html>

    测试路径:直接访问根路径定位到templates/index.html

      http://localhost:8888/spring-boot-day7/

      http://localhost:8888/spring-boot-day7/hello/hello

    1.2 模板基本语法

    新建demo.html用于页面展示

    使用Thymeleaf时必须在html页面中加入唯一的命名空间(namespace):Thymeleaf简写th,在xmlns:名字处写th

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

    注意:有的插件可以导入/提示命名空间!

    初始的demo.html页面

      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>用来测试Thymeleaf语法</title>
      </head>
      <body>
          <h1>测试Thymeleaf语法基本使用</h1>
      </body>
      </html>

    新建控制器DemoController

      package com.study.controller;
      ​
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpSession;
      ​
      /**
       * @ClassName DemoController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/13 20:36
       * @Version 1.0
       */
      @Controller
      @RequestMapping("demo")
      public class DemoController {
          @RequestMapping("demo")
          public String demo(HttpServletRequest request, Model model, HttpSession session){
              System.out.println("demo ok!");
              return "demo"; //templates/demo.html,最终封装成demo.html进行返回
          }
      }
      ​

    测试路径:http://localhost:8888/spring-boot-day7/demo/demo

    1.3 获取单个类型数据

    DemoController

      
      package com.study.controller;
      ​
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpSession;
      ​
      /**
       * @ClassName DemoController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/13 20:36
       * @Version 1.0
       */
      @Controller
      @RequestMapping("demo")
      public class DemoController {
          @RequestMapping("demo")
          //model底层封装的也是request作用域
          public String demo(HttpServletRequest request, Model model, HttpSession session){
              System.out.println("demo ok!");
              /**
               * 1.传递单个数据:String、Integer、解析超链接
               */
              String name = "小崔";
              request.setAttribute("name",name);
              Integer age = 27;
              model.addAttribute("age",age);
              String content = "<a href='http://www.baidu.com'>百度一下</a>";
              model.addAttribute("content",content);
              return "demo";
          }
      }

    demo.html

      <h2>1.获取单个数据</h2>
      <!--
          th:text="${属性名}"获取request作用域数据:直接将获取到的数据以文本形式渲染到页面中
          th:utext="${属性名}"获取request作用域数据:先将获取到的数据解析成html标签,再以文本形式渲染到页面中
      -->
      <h3>String类型数据:<span th:text="${name}"/></h3>
      <h3>Integer类型数据:<span th:text="${age}"/></h3>
      <h3>超链接:<span th:utext="${content}"/></h3>
      <h3>输入框:</h3>
      <input type="text" name="username" th:value="${name}">
      <input type="text" name="age" th:value="${age}">

    1.4 获取对象类型数据

    新建对象User

      
      package com.study.entity;
      ​
      import java.util.Date;
      ​
      /**
       * @ClassName User
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/13 20:33
       * @Version 1.0
       */
      public class User {
          private Integer id;
          private String name;
          private Double salary;
          private Date birthday;
      ​
          public User() {
          }
      ​
          public User(Integer id, String name, Double salary, Date birthday) {
              this.id = id;
              this.name = name;
              this.salary = salary;
              this.birthday = birthday;
          }
      ​
          @Override
          public String toString() {
              return "User{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", salary=" + salary +
                      ", birthday=" + birthday +
                      '}';
          }
      ​
          public Integer getId() {
              return id;
          }
      ​
          public void setId(Integer id) {
              this.id = id;
          }
      ​
          public String getName() {
              return name;
          }
      ​
          public void setName(String name) {
              this.name = name;
          }
      ​
          public Double getSalary() {
              return salary;
          }
      ​
          public void setSalary(Double salary) {
              this.salary = salary;
          }
      ​
          public Date getBirthday() {
              return birthday;
          }
      ​
          public void setBirthday(Date birthday) {
              this.birthday = birthday;
          }
      }

    DemoController

      package com.study.controller;
      ​
      import com.study.entity.User;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpSession;
      import java.util.Arrays;
      import java.util.Date;
      import java.util.List;
      ​
      /**
       * @ClassName DemoController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/13 20:36
       * @Version 1.0
       */
      @Controller
      @RequestMapping("demo")
      public class DemoController {
          @RequestMapping("demo")
          //model底层封装的也是request作用域
          public String demo(HttpServletRequest request, Model model, HttpSession session){
              System.out.println("demo ok!");
              /**
               * 2.传递对象类型数据
               */
              User user = new User(1,"小崔",1234.56,new Date());
              model.addAttribute("user",user);
              return "demo";
          }
      }
      ​

    demo.html

      <h2>2.获取对象类型数据</h2>
      <h3>
          user.id:<span th:text="${user.id}"/><br>
          user.name:<span th:text="${user.name}"/><br>
          user.salary:<span th:text="${user.salary}"/><br>
          <!--格式化日期:${#dates.format(格式化值,'格式化的格式')}},其中,dates.format为内置函数-->
          user.birthday:<span th:text="${#dates.format(user.birthday,'yyyy-MM-dd HH:mm:ss')}"/>
      </h3>

    1.5 获取集合类型数据

    DemoController

      
      package com.study.controller;
      ​
      import com.study.entity.User;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpSession;
      import java.util.Arrays;
      import java.util.Date;
      import java.util.List;
      ​
      /**
       * @ClassName DemoController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/13 20:36
       * @Version 1.0
       */
      @Controller
      @RequestMapping("demo")
      public class DemoController {
          @RequestMapping("demo")
          //model底层封装的也是request作用域
          public String demo(HttpServletRequest request, Model model, HttpSession session){
              System.out.println("demo ok!");
              /**
               * 3.传递集合类型数据
               */
              List<User> users = Arrays.asList(
                      new User(11,"熊大",1111.11,new Date()),
                      new User(22,"熊二",2222.22,new Date()),
                      new User(33,"光头强",3333.33,new Date())
              );
              model.addAttribute("users",users);
              return "demo";
          }
      }

    demo.html

      <h2>3.获取集合类型数据</h2>
      <ul>
          <!--遍历数据:th:each="变量(current_element当前遍历元素),变量(state遍历状态对象):集合"-->
          <li th:each="user,state:${users}">
              state count: <span th:text="${state.count}"></span>
              state odd: <span th:text="${state.odd}"></span>
              state even: <span th:text="${state.even}"></span>
              state size: <span th:text="${state.size}"></span>
              id: <span th:text="${user.id}"></span>
              name: <span th:text="${user.name}"></span>
              salary: <span th:text="${user.salary}"></span>
              birthday: <span th:text="${#dates.format(user.birthday,'yyyy年MM月dd日')}"></span>
          </li>
      </ul>

    补充:展示多条数据

    • 直接遍历集合

      
       <ul th:each="user:${users}">
         <li th:text="${user.id}"></li>
         <li th:text="${user.name}"></li>
         <li th:text="${user.age}"></li>
         <li th:text="${#dates.format(user.bir,'yyyy-MM-dd')}"></li>
      </ul>
    • 遍历时获取遍历状态

      
       <ul th:each="user,userStat:${users}">
         <li><span th:text="${userStat.count}"/>-<span th:text="${user.id}"/></li>   获取遍历次数  count 从1开始 index 从0开始
         <li><span th:text="${userStat.odd}"/>-<span th:text="${user.name}"/></li>   获取当前遍历是否是奇数行
         <li><span th:text="${userStat.even}"/>-<span th:text="${user.age}"/></li>   获取当前遍历是否是偶数行
         <li><span th:text="${userStat.size}"/>-<span th:text="${user.bir}"/></li>   获取当前集合的总条数
      </ul>

    1.6 有条件的显示数据

    demo.html

      <h2>4.有条件的展示数据</h2>
      <!-- th:if="${age>=25}" 作用:有条件的显示数据-->
      <div style="width: 100px;height: 100px;background: red" th:if="${age>25}">
          我是小红
      </div>
      <div style="width: 100px;height: 100px;background: blue" th:if="${age<=25}">
          我是小蓝
      </div>

    补充运算符:

      gt:  great than(大于)      >
      ge:  great equal(大于等于)  >=
      eq:  equal(等于)           ==
      lt:  less than(小于)       <
      le:  less equal(小于等于)   <=
      ne:  not equal(不等于)     !=

    1.7 传递session作用域数据

    DemoControlleer

      
      package com.study.controller;
      ​
      import com.study.entity.User;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpSession;
      import java.util.Arrays;
      import java.util.Date;
      import java.util.List;
      ​
      /**
       * @ClassName DemoController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/13 20:36
       * @Version 1.0
       */
      @Controller
      @RequestMapping("demo")
      public class DemoController {
          @RequestMapping("demo")
          //model底层封装的也是request作用域
          public String demo(HttpServletRequest request, Model model, HttpSession session){
              System.out.println("demo ok!");
              /**
               * 5.向session作用域存储数据
               */
              session.setAttribute("username","猪猪侠");
              return "demo";
          }
      }

    demo.html

      <h2>5.获取session作用域数据</h2>
      <span th:text="${session.username}"></span>

    访问路径:http://localhost:8888/spring-boot-day7/demo/demo

      
      # 总结
      -  1.使用 th:text="${属性名}"  获取对应数据,获取数据时会将对应标签中数据清空,因此最好是空标签
      -  2.使用 th:utext="${属性名}" 获取对应的数据,可以将数据中html先解析在渲染到页面
      -  3.使用 th:value="${属性名}" 获取数据直接作为表单元素value属性

    1.8 引入静态资源

    使用thymeleaf模板时项目中的静态资源默认放在resources路径下的static目录中

    demo.css

      h2{
          background: yellow;
      }

    demo.js

      
      function test() {
          alert('test');
      }

    demo.html

      
      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>用来测试Thymeleaf语法</title>
      ​
          <!--th:href="@{/}",其中,@{/}用来获取应用名称-->
      ​
          <!--引入css资源-->
          <link rel="stylesheet" th:href="@{/demo.css}">
      ​
          <!--引入js资源-->
          <script th:src="@{/demo.js}"></script>
          <script>
              test();
              /**
               * 通过js代码获取应用名:
               *    通过Thymeleaf语法获取项目名,使用Thymeleaf内联表达式 [[thymeleaf]]
               */
              let contentPath = "[[@{/}]]";
              console.log("项目名",contentPath);
          </script>
      </head>
      <body>
          <h1>测试Thymeleaf语法基本使用</h1>
          <h2>1.获取单个数据</h2>
          <!--
              th:text="${属性名}"获取request作用域数据:直接将获取到的数据以文本形式渲染到页面中
              th:utext="${属性名}"获取request作用域数据:先将获取到的数据解析成html标签,再以文本形式渲染到页面中
          -->
          String类型数据:<span th:text="${name}"/><br>
          Integer类型数据:<span th:text="${age}"/><br>
          超链接:<span th:utext="${content}"/><br>
          输入框:<br>
          <input type="text" name="username" th:value="${name}"><br>
          <input type="text" name="age" th:value="${age}">
      ​
      ​
          <h2>2.获取对象类型数据</h2>
          user.id:<span th:text="${user.id}"/><br>
          user.name:<span th:text="${user.name}"/><br>
          user.salary:<span th:text="${user.salary}"/><br>
          <!--格式化日期:${#dates.format(格式化值,'格式化的格式')}},其中,dates.format为内置函数-->
          user.birthday:<span th:text="${#dates.format(user.birthday,'yyyy-MM-dd HH:mm:ss')}"/>
      ​
          <h2>3.获取集合类型数据</h2>
          <ul>
              <!--遍历数据:th:each="变量(current_element当前遍历元素),变量(state遍历状态对象):集合"-->
              <li th:each="user,state:${users}">
                  state count: <span th:text="${state.count}"></span>
                  state odd: <span th:text="${state.odd}"></span>
                  state even: <span th:text="${state.even}"></span>
                  state size: <span th:text="${state.size}"></span>
                  id: <span th:text="${user.id}"></span>
                  name: <span th:text="${user.name}"></span>
                  salary: <span th:text="${user.salary}"></span>
                  birthday: <span th:text="${#dates.format(user.birthday,'yyyy年MM月dd日')}"></span>
              </li>
          </ul>
      ​
          <h2>4.有条件的展示数据</h2>
          <!-- th:if="${age>=25}" 作用:有条件的显示数据-->
          <div style="width: 100px;height: 100px;background: red" th:if="${age>25}">
              我是小红
          </div>
          <div style="width: 100px;height: 100px;background: blue" th:if="${age<=25}">
              我是小蓝
          </div>
      ​
          <h2>5.获取session作用域数据</h2>
          <span th:text="${session.username}"></span>
      </body>
      </html>

    或者

      
       <link rel="stylesheet" th:href="@{/css/index.css}">
       <script th:src="@{/js/jquery-min.js}"></script>

    注意: @{/}代表通过thymeleaf语法动态获取应用名

    在js代码中获取项目名:

      <script>
        const ctx = '[[@{/}]]';
      </script>

    注意:[[书写thymeleaf语法]],这里[[ ]]是thymeleaf内嵌表达式。

    最终效果:

    1.9 最终项目结构

    2. Thymeleaf测试案例

    配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    2.1 开发流程

      需求分析: 分析这个项目含有哪些功能模块
          用户模块:
              注册
              登录
              验证码
              安全退出
              真实用户
          员工模块:
              添加员工+上传头像
              展示员工列表+展示员工头像
              删除员工信息+删除员工头像
              更新员工信息+更新员工头像
      ​
      库表设计(概要设计): 1.分析系统有哪些表  2.分析表与表关系  3.确定表中字段(显性字段 隐性字段(业务字段))
          2张表 
              1.用户表 user
                  id username realname password gender
              2.员工表 employee
                  id name salary birthday  photo
          创建一个库: ems-thymeleaf
      ​
      详细设计:
          省略
      ​
      编码(环境搭建+业务代码开发)
          1.创建一个springboot项目 项目名字: ems-thymeleaf
          2.修改配置文件为 application.yml  pom.xml  2.5.0
          3.修改端口 8888  项目名: ems-thymeleaf
          4.springboot整合thymeleaf使用
              a.引入依赖:thymeleaf
              b.配置文件中指定thymeleaf相关配置
              c.编写控制器测试
          5.springboot整合mybatis
              a.引入依赖:mysql、druid、mybatis-springboot-stater
              b.配置文件中 
          6.导入项目页面
              static  存放静态资源
              templates 目录 存放模板文件
      ​
      测试
      上线部署
      维护
      发版
      ======================================================================
      ​
      用户相关:
      1.登录
      2.注册
      3.验证码
          1.验证码工具类

    2.2 springboot整合thymeleaf

    新建spring Initializr项目,引入Spring Web,项目命名为ems-thymeleaf

    pom.xml引入thymeleaf依赖

      <!--引入Thymeleaf依赖-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-thymeleaf</artifactId>
          <version>2.7.0</version>
      </dependency>

    修改application.properties为application.yml,添加如下配置:

      
      # 公共配置
      server:
        port: 8888
        servlet:
          context-path: /ems-thymeleaf
      ​
      # Thymeleaf配置
      spring:
        thymeleaf:
          cache: false
          prefix: classpath:/templates/
          suffix: .html

    新建demo.html

      
      <!DOCTYPE html>
      <html lang="en" xmlns="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>测试Thymeleaf</title>
      </head>
      <body>
          <h1>测试Thymeleaf</h1>
          <h4>获取数据:<span th:text="${msg}"></span></h4>
      </body>
      </html>

    DemoController

      
      package com.study.controller;
      ​
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      /**
       * @ClassName DemoController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 9:45
       * @Version 1.0
       */
      @Controller
      @RequestMapping("demo")
      public class DemoController {
          private static final Logger log = LoggerFactory.getLogger(DemoController.class);
          
          @RequestMapping("demo")
          public String demo(Model model){
              log.debug("demo ok");
              model.addAttribute("msg","hello,Thymeleaf!");
              return "demo";
          }
      }

    启动项目,访问路径:http://localhost:8888/ems-thymeleaf/demo/demo

    2.3 springboot整合mybatis

    pom.xml引入依赖:mysql、druid、mybatis-spring-boot-starter

      <!--mysql-->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.38</version>
      </dependency>
      ​
      <!--druid-->
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.2.4</version>
      </dependency>
      ​
      <!--mybatis-spring-boot-starter-->
      <dependency>
          <groupId>org.mybatis.spring.boot</groupId>
          <artifactId>mybatis-spring-boot-starter</artifactId>
          <version>2.2.0</version>
      </dependency>

    application.yml引入配置

      # 公共配置
      server:
        port: 8888
        servlet:
          context-path: /ems-thymeleaf
      ​
      # Thymeleaf配置
      spring:
        thymeleaf:
          cache: false
          prefix: classpath:/templates/
          suffix: .html
      ​
      # 配置数据源
        datasource:
          type: com.alibaba.druid.pool.DruidDataSource
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://localhost:3306/ems-thymeleaf?characterEncoding=UTF-8
          username: root
          password: 123456
      ​
      # 配置mybatis
      mybatis:
        mapper-locations: classpath:com/study/mapper/*.xml
        type-aliases-package: com.study.entity
      ​
      # 配置日志
      logging:
        level:
          root: info
          com.study: debug

    启动类添加注解扫描

      
      package com.study;
      ​
      import org.mybatis.spring.annotation.MapperScan;
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      ​
      @SpringBootApplication
      @MapperScan("com.study.dao")
      public class EmsThymeleafApplication {
      ​
          public static void main(String[] args) {
              SpringApplication.run(EmsThymeleafApplication.class, args);
          }
      }

    重新启动一下项目,运行不报错即可。

    2.4 导入项目页面(静态资源)

    在static目录下导入css、img、js等静态资源,导入之后Rebuild一下项目

    新建MVC配置类:不用单独开发controller来访问html页面

      
      package com.study.config;
      ​
      import org.springframework.context.annotation.Configuration;
      import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
      import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
      ​
      /**
       * @ClassName MvcConfig
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 10:35
       * @Version 1.0
       */
      @Configuration
      public class MvcConfig implements WebMvcConfigurer {
          @Override
          //通过在这里面配置,不需要再为每一个访问thymeleaf模板页面单独开发一个controller请求了
          public void addViewControllers(ViewControllerRegistry registry) {
              //ViewController: 请求路径,ViewName: 跳转视图
              registry.addViewController("login").setViewName("login");
              registry.addViewController("register").setViewName("regist");//注意与HTML页面对应
          }
      }

    启动项目,访问页面:

    (1)注册页面:http://localhost:8888/ems-thymeleaf/register

     (2)登录页面:http://localhost:8888/ems-thymeleaf/login

    2.5 验证码

    配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    验证码工具类

      
      package com.study.utils;
      ​
      import javax.imageio.ImageIO;
      import java.awt.*;
      import java.awt.geom.AffineTransform;
      import java.awt.image.BufferedImage;
      import java.io.File;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.OutputStream;
      import java.util.Arrays;
      import java.util.Random;
      ​
      /**
       *@创建人  cx
       *@创建时间  2018/11/27 17:36
       *@描述   验证码生成
       */
      public class VerifyCodeUtils{
          //使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
          public static final String VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
          private static Random random = new Random();
      ​
          /**
           * 使用系统默认字符源生成验证码
           * @param verifySize    验证码长度
           * @return
           */
          public static String generateVerifyCode(int verifySize){
              return generateVerifyCode(verifySize, VERIFY_CODES);
          }
          /**
           * 使用指定源生成验证码
           * @param verifySize    验证码长度
           * @param sources   验证码字符源
           * @return
           */
          public static String generateVerifyCode(int verifySize, String sources){
              if(sources == null || sources.length() == 0){
                  sources = VERIFY_CODES;
              }
              int codesLen = sources.length();
              Random rand = new Random(System.currentTimeMillis());
              StringBuilder verifyCode = new StringBuilder(verifySize);
              for(int i = 0; i < verifySize; i++){
                  verifyCode.append(sources.charAt(rand.nextInt(codesLen-1)));
              }
              return verifyCode.toString();
          }
      ​
          /**
           * 生成随机验证码文件,并返回验证码值
           * @param w
           * @param h
           * @param outputFile
           * @param verifySize
           * @return
           * @throws IOException
           */
          public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException{
              String verifyCode = generateVerifyCode(verifySize);
              outputImage(w, h, outputFile, verifyCode);
              return verifyCode;
          }
      ​
          /**
           * 输出随机验证码图片流,并返回验证码值
           * @param w
           * @param h
           * @param os
           * @param verifySize
           * @return
           * @throws IOException
           */
          public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException{
              String verifyCode = generateVerifyCode(verifySize);
              outputImage(w, h, os, verifyCode);
              return verifyCode;
          }
      ​
          /**
           * 生成指定验证码图像文件
           * @param w
           * @param h
           * @param outputFile
           * @param code
           * @throws IOException
           */
          public static void outputImage(int w, int h, File outputFile, String code) throws IOException{
              if(outputFile == null){
                  return;
              }
              File dir = outputFile.getParentFile();
              if(!dir.exists()){
                  dir.mkdirs();
              }
              try{
                  outputFile.createNewFile();
                  FileOutputStream fos = new FileOutputStream(outputFile);
                  outputImage(w, h, fos, code);
                  fos.close();
              } catch(IOException e){
                  throw e;
              }
          }
      ​
          /**
           * 输出指定验证码图片流
           * @param w
           * @param h
           * @param os
           * @param code
           * @throws IOException
           */
          public static void outputImage(int w, int h, OutputStream os, String code) throws IOException{
              int verifySize = code.length();
              BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
              Random rand = new Random();
              Graphics2D g2 = image.createGraphics();
              g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
              Color[] colors = new Color[5];
              Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN,
                      Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
                      Color.PINK, Color.YELLOW };
              float[] fractions = new float[colors.length];
              for(int i = 0; i < colors.length; i++){
                  colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
                  fractions[i] = rand.nextFloat();
              }
              Arrays.sort(fractions);
              g2.setColor(Color.GRAY);// 设置边框色
              g2.fillRect(0, 0, w, h);
              Color c = getRandColor(200, 250);
              g2.setColor(c);// 设置背景色
              g2.fillRect(0, 2, w, h-4);
              //绘制干扰线
              Random random = new Random();
              g2.setColor(getRandColor(160, 200));// 设置线条的颜色
              for (int i = 0; i < 20; i++) {
                  int x = random.nextInt(w - 1);
                  int y = random.nextInt(h - 1);
                  int xl = random.nextInt(6) + 1;
                  int yl = random.nextInt(12) + 1;
                  g2.drawLine(x, y, x + xl + 40, y + yl + 20);
              }
      ​
              // 添加噪点
              float yawpRate = 0.05f;// 噪声率
              int area = (int) (yawpRate * w * h);
              for (int i = 0; i < area; i++) {
                  int x = random.nextInt(w);
                  int y = random.nextInt(h);
                  int rgb = getRandomIntColor();
                  image.setRGB(x, y, rgb);
              }
              shear(g2, w, h, c);// 使图片扭曲
              g2.setColor(getRandColor(100, 160));
              int fontSize = h-4;
              Font font = new Font("Algerian", Font.ITALIC, fontSize);
              g2.setFont(font);
              char[] chars = code.toCharArray();
              for(int i = 0; i < verifySize; i++){
                  AffineTransform affine = new AffineTransform();
                  affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize/2, h/2);
                  g2.setTransform(affine);
                  g2.drawChars(chars, i, 1, ((w-10) / verifySize) * i + 5, h/2 + fontSize/2 - 10);
              }
              g2.dispose();
              ImageIO.write(image, "jpg", os);
          }
      ​
          private static Color getRandColor(int fc, int bc) {
              if (fc > 255)
                  fc = 255;
              if (bc > 255)
                  bc = 255;
              int r = fc + random.nextInt(bc - fc);
              int g = fc + random.nextInt(bc - fc);
              int b = fc + random.nextInt(bc - fc);
              return new Color(r, g, b);
          }
      ​
          private static int getRandomIntColor() {
              int[] rgb = getRandomRgb();
              int color = 0;
              for (int c : rgb) {
                  color = color << 8;
                  color = color | c;
              }
              return color;
          }
      ​
          private static int[] getRandomRgb() {
              int[] rgb = new int[3];
              for (int i = 0; i < 3; i++) {
                  rgb[i] = random.nextInt(255);
              }
              return rgb;
          }
      ​
          private static void shear(Graphics g, int w1, int h1, Color color) {
              shearX(g, w1, h1, color);
              shearY(g, w1, h1, color);
          }
      ​
          private static void shearX(Graphics g, int w1, int h1, Color color) {
              int period = random.nextInt(2);
              boolean borderGap = true;
              int frames = 1;
              int phase = random.nextInt(2);
              for (int i = 0; i < h1; i++) {
                  double d = (double) (period >> 1)
                          * Math.sin((double) i / (double) period
                          + (6.2831853071795862D * (double) phase)
                          / (double) frames);
                  g.copyArea(0, i, w1, 1, (int) d, 0);
                  if (borderGap) {
                      g.setColor(color);
                      g.drawLine((int) d, i, 0, i);
                      g.drawLine((int) d + w1, i, w1, i);
                  }
              }
      ​
          }
      ​
          private static void shearY(Graphics g, int w1, int h1, Color color) {
              int period = random.nextInt(40) + 10; // 50;
              boolean borderGap = true;
              int frames = 20;
              int phase = 7;
              for (int i = 0; i < w1; i++) {
                  double d = (double) (period >> 1)
                          * Math.sin((double) i / (double) period
                          + (6.2831853071795862D * (double) phase)
                          / (double) frames);
                  g.copyArea(i, 0, 1, h1, 0, (int) d);
                  if (borderGap) {
                      g.setColor(color);
                      g.drawLine(i, (int) d, i, 0);
                      g.drawLine(i, (int) d + h1, i, h1);
                  }
              }
          }
      }

    添加控制器UserController

      
      package com.study.controller;
      ​
      import com.study.utils.VerifyCodeUtils;
      import com.sun.deploy.net.HttpResponse;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      import javax.servlet.ServletOutputStream;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpSession;
      import java.io.IOException;
      ​
      /**
       * @ClassName UserController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 11:53
       * @Version 1.0
       */
      @Controller
      @RequestMapping("user")
      public class UserController {
          /**
           * @MethodName generateImageCode
           * @Description 生成图片验证码 
           * @param: session
           * @param: response  
           * @Author Jiangnan Cui
           * @Date 2022/6/27 12:08
           */
          @RequestMapping("generateImageCode")
          public void generateImageCode(HttpSession session, HttpServletResponse response) throws IOException {
              //1.生成4位随机数
              String code = VerifyCodeUtils.generateVerifyCode(4);
              //2.保存到session作用域
              session.setAttribute("code",code);
              //3.设置响应类型
              response.setContentType("image/png");
              //4.获得输出流
              ServletOutputStream sos = response.getOutputStream();
              //5.根据随机数生成图片
              VerifyCodeUtils.outputImage(220,60,sos,code);
          }
      }
      ​

    register.html

      
      <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
      <html xmlns:th="http://www.thymeleaf.org">
          <head>
              <title>regist</title>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
              <link rel="stylesheet" type="text/css" th:href="@{/css/style.css}" />
          </head>
          <body>
              <div id="wrap">
                  <div id="top_content">
                          <div id="header">
                              <div id="rightheader">
                                  <p>
                                      2009/11/20
                                      <br />
                                  </p>
                              </div>
                              <div id="topheader">
                                  <h1 id="title">
                                      <a href="#">main</a>
                                  </h1>
                              </div>
                              <div id="navigation">
                              </div>
                          </div>
                      <div id="content">
                          <p id="whereami">
                          </p>
                          <h1>
                              注册
                          </h1>
                          <form th:action="@{/user/register}" method="post">
                              <table cellpadding="0" cellspacing="0" border="0"
                                  class="form_table">
                                  <tr>
                                      <td valign="middle" align="right">
                                          用户名:
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="text" class="inputgri" name="username" />
                                      </td>
                                  </tr>
                                  <tr>
                                      <td valign="middle" align="right">
                                          真实姓名:
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="text" class="inputgri" name="realname" />
                                      </td>
                                  </tr>
                                  <tr>
                                      <td valign="middle" align="right">
                                          密码:
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="password" class="inputgri" name="password" />
                                      </td>
                                  </tr>
                                  <tr>
                                      <td valign="middle" align="right">
                                          性别:
                                      </td>
                                      <td valign="middle" align="left">
                                          男
                                          <input type="radio" class="inputgri" name="gender" value="1" checked="checked"/>
                                          女
                                          <input type="radio" class="inputgri" name="gender" value="0"/>
                                      </td>
                                  </tr>
                                  
                                  <tr>
                                      <td valign="middle" align="right">
                                          验证码:
                                          <img id="num" th:src="@{/user/generateImageCode}" />
                                          <a href="javascript:;" οnclick="changeImageCode()">换一张</a>
                                          <script>
                                              function changeImageCode(){
                                                  document.getElementById('num').src = '[[@{/user/generateImageCode}]]?'+(new Date()).getTime()
                                              }
                                          </script>
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="text" class="inputgri" name="code" />
                                      </td>
                                  </tr>
                              </table>
                              <p>
                                  <input type="submit" class="button" value="立即注册 &raquo;" />
                                  <a href="login.html">已有账号,返回登录</a>
                              </p>
                          </form>
                      </div>
                  </div>
                  <div id="footer">
                      <div id="footer_bg">
                      ABC@126.com
                      </div>
                  </div>
              </div>
          </body>
      </html>
      ​

    测试路径

    (1)http://localhost:8888/ems-thymeleaf/user/generateImageCode

    (2)http://localhost:8888/ems-thymeleaf/register

    2.6 用户注册

    配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    创建数据库ems-thymeleaf、表user

      
      create database ems-thymeleaf if not exists;
      user ems-thymeleaf;
      create tabble user if not exists(
        id int(20) not null auto_increment comment '用户ID',
        username varchar(100) not nulll comment '用户名',
        realname varchar(100) comment '真实姓名',
        password varchar(100) comment '密码',
        gender boolean comment '性别',
        primary key(id)
      )engine=innodb charset=utf8;

    新建实体类User

      
      package com.study.entity;
      ​
      /**
       * @ClassName User
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 15:49
       * @Version 1.0
       */
      public class User {
          private Integer id;
          private String username;
          private String realname;
          private String password;
          private Boolean gender;
      ​
          public User() {
          }
      ​
          public User(Integer id, String username, String realname, String password, Boolean gender) {
              this.id = id;
              this.username = username;
              this.realname = realname;
              this.password = password;
              this.gender = gender;
          }
      ​
          @Override
          public String toString() {
              return "User{" +
                      "id=" + id +
                      ", username='" + username + '\'' +
                      ", realname='" + realname + '\'' +
                      ", password='" + password + '\'' +
                      ", gender=" + gender +
                      '}';
          }
      ​
          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 String getRealname() {
              return realname;
          }
      ​
          public void setRealname(String realname) {
              this.realname = realname;
          }
      ​
          public String getPassword() {
              return password;
          }
      ​
          public void setPassword(String password) {
              this.password = password;
          }
      ​
          public Boolean getGender() {
              return gender;
          }
      ​
          public void setGender(Boolean gender) {
              this.gender = gender;
          }
      }
      ​

    创建UserDAO

      
      package com.study.dao;
      ​
      import com.study.entity.User;
      ​
      /**
       * @ClassName UserDAO
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 16:02
       * @Version 1.0
       */
      public interface UserDAO {
          /**
           * @MethodName findByUserName
           * @Description 根据用户名查找用户
           * @param: username
           * @return: com.study.entity.User
           * @Author Jiangnan Cui
           * @Date 2022/6/27 16:03
           */
          User findByUserName(String username);
      ​
          /**
           * @MethodName register
           * @Description 用户注册
           * @param: user
           * @Author Jiangnan Cui
           * @Date 2022/6/27 16:03
           */
          void register(User user);
      }
      ​

    创建UserDAOMapper

      
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
      ​
      <mapper namespace="com.study.dao.UserDAO">
          <!--findByUserName-->
          <select id="findByUserName" parameterType="String" resultType="User">
              select
                  id,username,realname,password,gender
              from `user`
              where username=#{username}
          </select>
      ​
          <!--register-->
          <insert id="register" parameterType="User" useGeneratedKeys="true" keyProperty="id">
              insert into `user` values(#{id},#{username},#{realname},#{password},#{gender})
          </insert>
      ​
      </mapper>

    创建UserService

      package com.study.service;
      ​
      import com.study.entity.User;
      ​
      /**
       * @ClassName UserService
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 15:48
       * @Version 1.0
       */
      public interface UserService {
          /**
           * @MethodName register
           * @Description 用户注册
           * @param: user
           * @Author Jiangnan Cui
           * @Date 2022/6/27 15:50
           */
          void register(User user);
      }

    创建UserServiceImpl

      
      package com.study.service;
      ​
      import com.study.dao.UserDAO;
      import com.study.entity.User;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Transactional;
      import org.springframework.util.DigestUtils;
      import org.springframework.util.ObjectUtils;
      import java.nio.charset.StandardCharsets;
      ​
      /**
       * @ClassName UserServiceImpl
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 16:14
       * @Version 1.0
       */
      @Service
      @Transactional
      public class UserServiceImpl implements UserService{
          //业务逻辑层调用数据访问层
          private UserDAO userDAO;
          @Autowired
          public UserServiceImpl(UserDAO userDAO) {
              this.userDAO = userDAO;
          }
      ​
          /**
           * @MethodName register
           * @Description
           * @param: user
           * @Author Jiangnan Cui
           * @Date 2022/6/27 16:22
           */
          @Override
          public void register(User user) {
              //1.根据用户名从数据库中查询是否存在已经注册的用户
              User userDB = userDAO.findByUserName(user.getUsername());
              //2.判断该用户是否存在
              if(!ObjectUtils.isEmpty(userDB))
                  throw new RuntimeException("当前用户已被注册!");//不为空时抛出异常
              //3.为空时表明用户不存在,继续注册用户
              //对明文密码进行加密:相同字符串多次使用md5加密后结果一致
              String newPassword = DigestUtils.md5DigestAsHex(user.getPassword().getBytes(StandardCharsets.UTF_8));
              user.setPassword(newPassword);
              userDAO.register(user);
          }
      }
      ​

    新建UserController

      
      package com.study.controller;
      ​
      import com.study.entity.User;
      import com.study.service.UserService;
      import com.study.utils.VerifyCodeUtils;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import javax.servlet.ServletOutputStream;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpSession;
      import java.io.IOException;
      ​
      /**
       * @ClassName UserController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 11:53
       * @Version 1.0
       */
      @Controller
      @RequestMapping("user")
      public class UserController {
          //新建日志对象
          private static final Logger log = LoggerFactory.getLogger(UserController.class);
      ​
          /**
           * @MethodName generateImageCode
           * @Description 生成图片验证码
           * @param: session
           * @param: response
           * @Author Jiangnan Cui
           * @Date 2022/6/27 12:08
           */
          @RequestMapping("generateImageCode")
          public void generateImageCode(HttpSession session, HttpServletResponse response) throws IOException {
              //1.生成4位随机数
              String code = VerifyCodeUtils.generateVerifyCode(4);
              //2.保存到session作用域
              session.setAttribute("code",code);
              //3.设置响应类型
              response.setContentType("image/png");
              //4.获得输出流
              ServletOutputStream sos = response.getOutputStream();
              //5.根据随机数生成图片
              VerifyCodeUtils.outputImage(220,60,sos,code);
          }
      ​
          private UserService userService;
          @Autowired
          public UserController(UserService userService) {
              this.userService = userService;
          }
      ​
          /**
           * @MethodName register
           * @Description 用户注册
           * @param: user
           * @param: code
           * @param: session
           * @return: java.lang.String
           * @Author Jiangnan Cui
           * @Date 2022/6/27 15:57
           */
          @RequestMapping("register")
          public String register(User user,String code,HttpSession session){
              log.debug("用户名:{},真实姓名:{},密码:{},性别:{}",
                      user.getUsername(),user.getRealname(),user.getPassword(),user.getGender());
              log.debug("用户输入的验证码: {}",code);
      ​
              try {
                  //1.判断用户输入验证码和session中的验证码是否一致
                  String sessionCode = session.getAttribute("code").toString();
                  if(!sessionCode.equalsIgnoreCase(code))
                      throw new RuntimeException("验证码输入错误!");
                  //2.注册用户
                  userService.register(user);
              } catch (RuntimeException e) {
                  e.printStackTrace();
                  return "redirect:/register";//注册失败回到注册
              }
              return "redirect:/login";//注册成功转到登录
          }
      }
      ​

    访问路径:http://localhost:8888/ems-thymeleaf/register,进行注册,成功跳转到登录页面,失败回到注册页面!

    2.7 用户登录

    配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    在UserService添加登录方法

      /**
       * @MethodName login
       * @Description 用户登录
       * @param: username
       * @param: password
       * @return: com.study.entity.User
       * @Author Jiangnan Cui
       * @Date 2022/6/27 17:02
       */
      User login(String username,String password);

    在UserServiceImpl实现登录方法

      /**
       * @MethodName login
       * @Description 用户登录
       * @param: username
       * @param: password
       * @return: com.study.entity.User
       * @Author Jiangnan Cui
       * @Date 2022/6/27 17:03
       */
      @Override
      public User login(String username, String password) {
          //1.根据用户名查询用户
          User user = userDAO.findByUserName(username);
          //2.判断用户是否存在,存在执行登录;不存在,抛出异常
          if(ObjectUtils.isEmpty(user))
              throw new RuntimeException("用户名不存在!");
          //3.在用户存在的前提下,比较密码是否正确,正确执行登录;不正确,抛出异常
          //密码加密
          String passwordSecret = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
          if(!user.getPassword().equals(passwordSecret))
              throw new RuntimeException("密码错误!");
          return user;
      }

    UserController添加登录方法

      /**
       * @MethodName login
       * @Description 用户登录
       * @param: username
       * @param: password
       * @param: session
       * @return: java.lang.String
       * @Author Jiangnan Cui
       * @Date 2022/6/27 17:11
       */
      @RequestMapping("login")
      public String login(String username,String password,HttpSession session){
          log.debug("本次登录的用户名为:{}",username);
          log.debug("本次登录的密码为:{}",password);
          try {
              //1.调用业务逻辑层进行登录
              User user = userService.login(username, password);
              //2.保存用户信息供页面调用
              session.setAttribute("user",user);
          } catch (Exception e) {
              e.printStackTrace();
              return "redirect:/login";//登录失败回到登录页面
          }
          return "redirect:/employee/lists";//登录成功,跳转到查询所有员工信息控制器路径
      }

    添加EmpployeeController

      
      package com.study.controller;
      ​
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      /**
       * @ClassName EmployeeController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 17:15
       * @Version 1.0
       */
      @Controller
      @RequestMapping("employee")
      public class EmployeeController {
          private static final Logger log = LoggerFactory.getLogger(EmployeeController.class);
      ​
          @RequestMapping("lists")
          public String list(Model model){
              log.debug("查询所有员工信息");
              return "emplist";
          }
      ​
      }
      ​

    login.html

      
      <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
      <html xmlns:th="http://www.thymeleaf.org">
          <head>
              <title>login</title>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
              <link rel="stylesheet" type="text/css"
                  href="css/style.css" />
          </head>
      ​
          <body>
              <div id="wrap">
                  <div id="top_content">
                          <div id="header">
                              <div id="rightheader">
                                  <p>
                                      2009/11/20
                                      <br />
                                  </p>
                              </div>
                              <div id="topheader">
                                  <h1 id="title">
                                      <a href="#">main</a>
                                  </h1>
                              </div>
                              <div id="navigation">
                              </div>
                          </div>
                      <div id="content">
                          <p id="whereami">
                          </p>
                          <h1>
                              欢迎进入,请登录!
                          </h1>
                          <form th:action="@{/user/login}" method="post">
                              <table cellpadding="0" cellspacing="0" border="0"
                                  class="form_table">
                                  <tr>
                                      <td valign="middle" align="right">
                                          用户名:
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="text" class="inputgri" name="username" />
                                      </td>
                                  </tr>
                                  <tr>
                                      <td valign="middle" align="right">
                                          密码:
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="password" class="inputgri" name="password" />
                                      </td>
                                  </tr>
                              </table>
                              <p>
                                  <input type="submit" class="button" value="点我登录 &raquo;" />
                                  &nbsp;&nbsp;
                                  <a href="regist.html">还没有账号,立即注册</a>
                              </p>
                          </form>
                      </div>
                  </div>
                  <div id="footer">
                      <div id="footer_bg">
                          ABC@126.com
                      </div>
                  </div>
              </div>
          </body>
      </html>
      ​

    emplists.html

      
      <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
      <html xmlns:th="http://www.thymeleaf.org">
          <head>
              <title>emplist</title>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
              <link rel="stylesheet" type="text/css" th:href="@{/css/style.css}" />
          </head>
          <body>
              <div id="wrap">
                  <div id="top_content"> 
                      <div id="header">
                          <div id="rightheader">
                              <p>
                                  2009/11/20 
                                  <br />
                                  <a th:if="${session.user!=null}" th:href="@{/user/logout}">安全退出</a>
                                  <a th:if="${session.user==null}" th:href="@{/login}">点我登录</a>
                              </p>
                          </div>
                          <div id="topheader">
                              <h1 id="title">
                                  <a href="#">main</a>
                              </h1>
                          </div>
                          <div id="navigation">
                          </div>
                      </div>
                      <div id="content">
                          <p id="whereami">
                          </p>
                          <h1>
                              欢迎 <span th:if="${session.user!=null}" th:text="${session.user.username}"></span>
                                  <span th:if="${session.user==null}" >游客</span>!
                          </h1>
                          <table class="table">
                              <tr class="table_header">
                                  <td>
                                      编号
                                  </td>
                                  <td>
                                      姓名
                                  </td>
                                  <td>
                                      头像
                                  </td>
                                  <td>
                                      工资
                                  </td>
                                  <td>
                                      生日
                                  </td>
                                  <td>
                                      操作
                                  </td>
                              </tr>
                              <tr th:each="employee,state:${employeeList}"  th:class="${state.odd?'row1':'row2'}">
                                  <td>
                                      <span th:text="${employee.id}"></span>
                                  </td>
                                  <td>
                                      <span th:text="${employee.name}"></span>
                                  </td>
                                  <td>
                                      <img th:src="@{/}+${employee.photo}" width="60">
                                  </td>
                                  <td>
                                      <span th:text="${employee.salary}"></span>
                                  </td>
                                  <td>
                                      <span th:text="${#dates.format(employee.birthday,'yyyy/MM/dd')}"></span>
                                  </td>
                                  <td>
                                      <a href="javascript:;" th:οnclick="'deleteEmployee('+${employee.id}+')'">删除</a>
      <!--                                <a th:href="@{/employee/detail?id=}+${employee.id}+'&name='+${employee.name}">更新</a>-->
      <!--                                <a th:href="@{/employee/detail(id=${employee.id},name=${employee.name})}">更新</a>-->
                                      <a th:href="@{/employee/detail(id=${employee.id})}">更新</a>
      ​
                                  </td>
                              </tr>
                              <script>
                                  function deleteEmployee(id){
                                      console.log(id);
                                      if(window.confirm('确定要删除这条记录吗?')){
                                          location.href='[[@{/employee/delete?id=}]]'+id;
                                      }
                                  }
                              </script>
                          </table>
                          <p>
                              <input type="button" class="button" value="添加" οnclick="addEmp()"/>
                              <script>
                                  function addEmp(){
                                      location.href = '[[@{/addEmp}]]';
                                  }
                              </script>
                          </p>
                      </div>
                  </div>
                  <div id="footer">
                      <div id="footer_bg">
                      ABC@126.com
                      </div>
                  </div>
              </div>
          </body>
      </html>
      ​

    测试路径:http://localhost:8888/ems-thymeleaf/login,登录成功跳转到员工列表页面,登录失败回到登录页面。

    2.8 员工列表

    配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili

    创建employee表

      create table employee if not exists(
        id int(20) not null auto_increment comment '员工ID',
        name varchar(100) not null comment '姓名',
        salary double not null comment '工资',
        birthday datetime not null comment '生日',
        photo carchar(100) not null comment '头像',
        primary key(id)
      )engine=innodb charset=utf8;

    新建实体类Employee

      
      package com.study.entity;
      ​
      import java.util.Date;
      ​
      /**
       * @ClassName Employee
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 18:11
       * @Version 1.0
       */
      public class Employee {
          private Integer id;
          private String name;
          private Double salary;
          private Date birthday;
          private String photo;
      ​
          public Employee() {
          }
      ​
          public Employee(Integer id, String name, Double salary, Date birthday, String photo) {
              this.id = id;
              this.name = name;
              this.salary = salary;
              this.birthday = birthday;
              this.photo = photo;
          }
      ​
          public Integer getId() {
              return id;
          }
      ​
          public void setId(Integer id) {
              this.id = id;
          }
      ​
          public String getName() {
              return name;
          }
      ​
          public void setName(String name) {
              this.name = name;
          }
      ​
          public Double getSalary() {
              return salary;
          }
      ​
          public void setSalary(Double salary) {
              this.salary = salary;
          }
      ​
          public Date getBirthday() {
              return birthday;
          }
      ​
          public void setBirthday(Date birthday) {
              this.birthday = birthday;
          }
      ​
          public String getPhoto() {
              return photo;
          }
      ​
          public void setPhoto(String photo) {
              this.photo = photo;
          }
      ​
          @Override
          public String toString() {
              return "Employee{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", salary=" + salary +
                      ", birthday=" + birthday +
                      ", photo='" + photo + '\'' +
                      '}';
          }
      }
      ​

    新建EmployeeDAO

      
      package com.study.dao;
      ​
      import com.study.entity.Employee;
      ​
      import java.util.List;
      ​
      /**
       * @ClassName EmployeeDAO
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 18:23
       * @Version 1.0
       */
      public interface EmployeeDAO {
          /**
           * @MethodName lists
           * @Description 员工列表
           * @return: java.util.List<com.study.entity.Employee>
           * @Author Jiangnan Cui
           * @Date 2022/6/27 18:24
           */
          List<Employee> lists();
      }
      ​

    新建EmployeeDAOMapper

      
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
      ​
      <mapper namespace="com.study.dao.EmployeeDAO">
          <!--lists-->
          <select id="lists" resultType="Employee">
              select
                  id,name,salary,birthday,photo
              from employee
          </select>
      </mapper>

    新建EmployeeService

      
      package com.study.service;
      ​
      import com.study.entity.Employee;
      ​
      import java.util.List;
      ​
      /**
       * @ClassName EmployeeService
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 18:26
       * @Version 1.0
       */
      public interface EmployeeService {
          /**
           * @MethodName lists
           * @Description 员工列表
           * @return: java.util.List<com.study.entity.Employee>
           * @Author Jiangnan Cui
           * @Date 2022/6/27 18:27
           */
          List<Employee> lists();
      }
      ​

    新建EmployeeServiceImpl

      package com.study.service;
      ​
      import com.study.dao.EmployeeDAO;
      import com.study.entity.Employee;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      import org.springframework.transaction.annotation.Transactional;
      ​
      import java.util.List;
      ​
      /**
       * @ClassName EmployeeServiceImpl
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 18:27
       * @Version 1.0
       */
      @Service
      @Transactional
      public class EmployeeServiceImpl implements EmployeeService{
          //业务逻辑层调用数据访问层
          private EmployeeDAO employeeDAO;
          @Autowired
          public EmployeeServiceImpl(EmployeeDAO employeeDAO) {
              this.employeeDAO = employeeDAO;
          }
      ​
          /**
           * @MethodName lists
           * @Description 员工列表
           * @return: java.util.List<com.study.entity.Employee>
           * @Author Jiangnan Cui
           * @Date 2022/6/27 18:29
           */
          @Override
          public List<Employee> lists() {
              return employeeDAO.lists();
          }
      }
      ​

    完善EmployeeController

      
      package com.study.controller;
      ​
      import com.study.entity.Employee;
      import com.study.service.EmployeeService;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      ​
      import java.util.List;
      ​
      /**
       * @ClassName EmployeeController
       * @Description TODO
       * @Author Jiangnan Cui
       * @Date 2022/6/27 17:15
       * @Version 1.0
       */
      @Controller
      @RequestMapping("employee")
      public class EmployeeController {
          //日志对象
          private static final Logger log = LoggerFactory.getLogger(EmployeeController.class);
      ​
          //控制层调用业务逻辑层
          private EmployeeService employeeService;
          @Autowired
          public EmployeeController(EmployeeService employeeService) {
              this.employeeService = employeeService;
          }
      ​
          /**
           * @MethodName list
           * @Description 员工列表
           * @param: model
           * @return: java.lang.String
           * @Author Jiangnan Cui
           * @Date 2022/6/27 18:04
           */
          @RequestMapping("lists")
          public String list(Model model){
              log.debug("查询所有员工信息");
              List<Employee> employeeList = employeeService.lists();
              model.addAttribute("employeeList",employeeList);
              return "emplist";
          }
      }

    测试路径:http://localhost:8888/ems-thymeleaf/employee/lists

    2.9 添加员工

    完善EmployeDAO

      /**
       * @MethodName addEmp
       * @Description 保存员工信息
       * @param: employee
       * @Author Jiangnan Cui
       * @Date 2022/6/27 20:11
       */
      void addEmp(Employee employee);

    完善EmpDAOMapper

      <!--addEmp-->
      <insert id="addEmp" parameterType="Employee" useGeneratedKeys="true" keyProperty="id">
          insert into employee values(#{id},#{name},#{salary},#{birthday},#{photo})
      </insert>

    完善EmployeeService

      /**
       * @MethodName addEmp
       * @Description
       * @param: employee
       * @Author Jiangnan Cui
       * @Date 2022/6/27 20:12
       */
      void addEmp(Employee employee);

    完善EmpEmployeeServiceImpl

      /**
       * @MethodName addEmp
       * @Description 添加员工信息
       * @param: employee
       * @Author Jiangnan Cui
       * @Date 2022/6/27 20:13
       */
      @Override
      public void addEmp(Employee employee) {
          employeeDAO.addEmp(employee);
      }

    EmployeeController

      //定义文件路径
      @Value("${photo.file.dir}")
      private String realpath;
      ​
      /**
       * @MethodName uploadPhoto
       * @Description 上传头像
       * @param: img
       * @param: originalFileName
       * @return: java.lang.String
       * @Author Jiangnan Cui
       * @Date 2022/6/27 20:23
       */
      private String uploadPhoto(MultipartFile img,String originalFileName) throws IOException {
          String fileNamePrefix = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
          String fileNameSuffix = originalFileName.substring(originalFileName.lastIndexOf("."));
          String newFileName = fileNamePrefix + fileNameSuffix;
          img.transferTo(new File(realpath,newFileName));
          return newFileName;
      }
      ​
      /**
       * @MethodName addEmp
       * @Description 添加员工信息
       * @param: employee
       * @param: img
       * @return: java.lang.String
       * @Author Jiangnan Cui
       * @Date 2022/6/27 20:15
       */
      @RequestMapping("addEmp")
      public String addEmp(Employee employee, MultipartFile img) throws IOException {
          log.debug("姓名:{},工资:{},生日:{}",employee.getName(),employee.getSalary(),employee.getBirthday());
          //获得图片原始文件名
          String originalFilename = img.getOriginalFilename();
          log.debug("头像名称:{}",originalFilename);
          log.debug("头像大小:{}",img.getSize());
          log.debug("上传路径:{}",realpath);
      ​
          //1.处理头像的上传
          String newFileName = uploadPhoto(img, originalFilename);
          //2.保存员工信息
          employee.setPhoto(newFileName);//保存头衔名字
          employeeService.addEmp(employee);
          return "redirect:/employee/lists";//保存成功跳转到员工列表
      }

    application.yml添加文件上传配置

      
      # 公共配置
      server:
        port: 8888
        servlet:
          context-path: /ems-thymeleaf
      ​
      # Thymeleaf配置
      spring:
        thymeleaf:
          cache: false
          prefix: classpath:/templates/
          suffix: .html
      ​
      # 配置数据源
        datasource:
          type: com.alibaba.druid.pool.DruidDataSource
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://localhost:3306/ems-thymeleaf?characterEncoding=UTF-8
          username: root
          password: 123456
        # 暴露哪些资源可以通过项目名直接访问
        web:
          resources:
            static-locations: classpath:/static/,file:${photo.file.dir}
      ​
      # 配置mybatis
      mybatis:
        mapper-locations: classpath:com/study/mapper/*.xml
        type-aliases-package: com.study.entity
      ​
      # 配置日志
      logging:
        level:
          root: info
          com.study: debug
      ​
      # 指定文件上传位置
      photo:
        file:
          dir: D:\Software_Development\IDEA_code\SpringBoot\ems-thymeleaf\photos
      ​

    完善MvcConfig

      
      @Configuration
      public class MvcConfig implements WebMvcConfigurer {
          @Override
          //通过在这里面配置,不需要再为每一个访问thymeleaf模板页面单独开发一个controller请求了
          public void addViewControllers(ViewControllerRegistry registry) {
              //ViewController: 请求路径,ViewName: 跳转视图
              registry.addViewController("login").setViewName("login");
              registry.addViewController("register").setViewName("regist");
              registry.addViewController("addEmp").setViewName("addEmp");
          }
      }

    addEmp.html

      
      <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
      <html xmlns:th="http://www.thymealf.org">
          <head>
              <title>添加员工信息</title>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
              <link rel="stylesheet" type="text/css"
                  href="css/style.css" />
          </head>
      ​
          <body>
              <div id="wrap">
                  <div id="top_content">
                          <div id="header">
                              <div id="rightheader">
                                  <p>
                                      2009/11/20
                                      <br />
                                  </p>
                              </div>
                              <div id="topheader">
                                  <h1 id="title">
                                      <a href="#">main</a>
                                  </h1>
                              </div>
                              <div id="navigation">
                              </div>
                          </div>
                      <div id="content">
                          <p id="whereami">
                          </p>
                          <h1>
                              添加员工信息:
                          </h1>
                          <form th:action="@{/employee/addEmp}" method="post" enctype="multipart/form-data">
                              <table cellpadding="0" cellspacing="0" border="0"
                                  class="form_table">
                                  <tr>
                                      <td valign="middle" align="right">
                                          姓名:
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="text" class="inputgri" name="name" />
                                      </td>
                                  </tr>
                                  <tr>
                                      <td valign="middle" align="right">
                                          头像:
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="file" width="" name="img" />
                                      </td>
                                  </tr>
                                  <tr>
                                      <td valign="middle" align="right">
                                          工资:
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="text" class="inputgri" name="salary" />
                                      </td>
                                  </tr>
                                  <tr>
                                      <td valign="middle" align="right">
                                          生日:
                                      </td>
                                      <td valign="middle" align="left">
                                          <input type="text" class="inputgri" name="birthday" />
                                      </td>
                                  </tr>
                              </table>
                              <p>
                                  <input type="submit" class="button" value="确认添加" />
                                  <input type="submit" class="button" value="返回列表" />
                              </p>
                          </form>
                      </div>
                  </div>
                  <div id="footer">
                      <div id="footer_bg">
                          ABC@126.com
                      </div>
                  </div>
              </div>
          </body>
      </html>
      ​

    访问路径:http://localhost:8888/ems-thymeleaf/employee/lists,添加员工信息

     待完善

  • 相关阅读:
    代码质量与安全 | 想在发布竞赛中胜出?Sonar来帮你
    青翼科技基于VITA57.1的16路数据收发处理平台产品手册
    java计算机毕业设计河南口腔医疗机构线上服务系统源码+mysql数据库+系统+lw文档+部署
    JAVACPU占用过高、内存泄漏问题排查
    C++ 漫谈哈夫曼树
    Python从入门到实践:包的使用
    SCUI Admin - 基于 Vue3 和 Element Plus 开发的 admin 前端 UI 框架,不仅免费开源,还有很多现成的业务组件、页面模板
    Echarts配置(二)
    Android 自定义view中根据状态修改drawable图片
    【教学类-16-02】20221125《世界杯七巧板A4整页-随机参考图七巧板 3份一页》(大班)
  • 原文地址:https://blog.csdn.net/xiaocui1995/article/details/125491934