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进行返回 } }
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>
# 总结 - 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"; } }
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="立即注册 »" /> <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 if not exists `ems-thymeleaf`;
use `ems-thymeleaf`;
create table if not exists `user`(
id int(20) not null auto_increment comment '用户ID',
username varchar(100) not null 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="点我登录 »" /> <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 if not exists `employee`(
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 varchar(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"; } }
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,添加员工信息
2.10 更新员工信息
配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili
完善EmployeeController
/** * @MethodName detail * @Description 根据id查询员工详细信息 * @param: id * @param: model * @return: java.lang.String * @Author Jiangnan Cui * @Date 7:05 2022/6/29 */ @RequestMapping("detail") public String detail(Integer id,Model model){ log.debug("当前查询员工id:{}",id); //根据id查询员工详细信息 Employee employee = employeeService.findById(id); model.addAttribute("employee",employee); return "updateEmp";//查询到的员工信息显示在更新页面 } /** * @MethodName updateEmp * @Description 更新员工信息 * @param: employee * @return: java.lang.String * @Author Jiangnan Cui * @Date 7:15 2022/6/29 */ @RequestMapping("updateEmp") public String updateEmp(Employee employee,MultipartFile img) throws IOException { //1.判断是否更新头像 为空时说明头像未更新, boolean notEmpty = !img.isEmpty(); log.debug("是否更新了头像:{}",notEmpty); //不为空时说明头像更新了:需要删除老的头像,重新上传新的头像 if(notEmpty){ //1.根据id查询原始头像 String oldPhoto = employeeService.findById(employee.getId()).getPhoto(); //2.删除老的头像 File file = new File(realpath,oldPhoto); if(file.exists()) file.delete(); //3.处理新的头像上传 String originalFilename = img.getOriginalFilename(); String newFileName = uploadPhoto(img, originalFilename); //4.修改员工新的头像名称 employee.setPhoto(newFileName); } //为空时说明头像未更新,只需要更新基本信息即可 employeeService.updateEmp(employee); //更新成功后跳转到员工列表页面 return "redirect:/employee/lists"; }
完善updateEmp.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>更新员工信息</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="@{/employee/updateEmp}" 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"> <span th:text="${employee.id}"></span> <input type="hidden" name="id" th:value="${employee.id}"> </td> </tr> <tr> <td valign="middle" align="right"> 姓名: </td> <td valign="middle" align="left"> <input type="text" class="inputgri" name="name" th:value="${employee.name}"/> </td> </tr> <tr> <td valign="middle" align="right"> 当前头像: </td> <td valign="middle" align="left"> <img th:src="@{/}+${employee.photo}" width="60"> <input type="hidden" th:value="${employee.photo}" name="photo"> </td> </tr> <tr> <td valign="middle" align="right"> 选择新头像: </td> <td valign="middle" align="left"> <input type="file" name="img" /> </td> </tr> <tr> <td valign="middle" align="right"> 工资: </td> <td valign="middle" align="left"> <input type="text" class="inputgri" name="salary" th:value="${employee.salary}"/> </td> </tr> <tr> <td valign="middle" align="right"> 生日: </td> <td valign="middle" align="left"> <input type="text" class="inputgri" name="birthday" th:value="${#dates.format(employee.birthday,'yyyy/MM/dd')}"/> </td> </tr> </table> <p> <input type="submit" class="button" value="更新" /> <input type="button" class="button" value="返回列表" /> </p> </form> </div> </div> <div id="footer"> <div id="footer_bg"> ABC@126.com </div> </div> </div> </body> </html>
完善EmployeeService
/** * @MethodName findById * @Description 根据id查询员工详细信息 * @param: id * @return: com.study.entity.Employee * @Author Jiangnan Cui * @Date 7:03 2022/6/29 */ Employee findById(Integer id); /** * @MethodName updateEmp * @Description 更新员工信息 * @param: employee * @Author Jiangnan Cui * @Date 7:44 2022/6/29 */ void updateEmp(Employee employee);
完善EmployeeServiceImpl
/** * @MethodName findById * @Description 根据id查询员工信息 * @param: id * @return: com.study.entity.Employee * @Author Jiangnan Cui * @Date 7:03 2022/6/29 */ @Override public Employee findById(Integer id) { return employeeDAO.findById(id); } /** * @MethodName updateEmp * @Description 更新员工信息 * @param: employee * @Author Jiangnan Cui * @Date 7:45 2022/6/29 */ @Override public void updateEmp(Employee employee) { employeeDAO.updateEmp(employee); }
完善EmployeeDAO
/** * @MethodName findById * @Description 根据id查询员工详细信息 * @param: id * @return: com.study.entity.Employee * @Author Jiangnan Cui * @Date 7:04 2022/6/29 */ Employee findById(Integer id); /** * @MethodName updateEmp * @Description 更新员工信息 * @param: employee * @Author Jiangnan Cui * @Date 7:47 2022/6/29 */ void updateEmp(Employee employee);
完善EmployeeDAOMapper
<!--findById--> <select id="findById" parameterType="Integer" resultType="Employee"> select id,name,salary,birthday,photo from employee where id=#{id} </select> <!--updateEmp--> <update id="updateEmp" parameterType="Employee"> update employee set name=#{name},salary=#{salary},birthday=#{birthday},photo=#{photo} where id=#{id} </update>
(1)点击更新,进行员工信息更新
(2)填写要修改的员工信息,修改完成后,点击更新
(3)更新后的员工信息
2.11 删除员工信息
配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili
完善EmpController
/** * @MethodName deleteEmp * @Description 删除员工信息 * @param: id * @return: java.lang.String * @Author Jiangnan Cui * @Date 8:43 2022/6/29 */ @RequestMapping("deleteEmp") public String deleteEmp(Integer id){ log.debug("删除的员工id:{}",id); //1.删除数据 String photo = employeeService.findById(id).getPhoto(); employeeService.deleteEmp(id); //2.删除头像 File file = new File(realpath,photo); if(file.exists()) file.delete(); return "redirect:/employee/lists";//删除成功后跳转到员工列表 }
完善emplist.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/deleteEmp?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>
完善EmpService
/** * @MethodName deleteEmp * @Description 删除员工信息 * @param: id * @Author Jiangnan Cui * @Date 8:48 2022/6/29 */ void deleteEmp(Integer id);
完善EmpServiceImpl
/** * @MethodName deleteEmp * @Description 删除员工信息 * @param: id * @Author Jiangnan Cui * @Date 8:49 2022/6/29 */ @Override public void deleteEmp(Integer id) { employeeDAO.deleteEmp(id); }
完善EmployeeDAO
/** * @MethodName deleteEmp * @Description 删除员工信息 * @param: id * @Author Jiangnan Cui * @Date 8:49 2022/6/29 */ void deleteEmp(Integer id);
完善EmployeeDAOMapper
<!--deleteEmp--> <delete id="deleteEmp" parameterType="Integer"> delete from employee where id=#{id} </delete>
测试路径:http://localhost:8888/ems-thymeleaf/employee/lists,点击删除的同时,员工信息删除且photos中的头像图片也删除。
2.12 安全退出
配套视频:【编程不良人】2021年SpringBoot最新最全教程_哔哩哔哩_bilibili
完善UserController
/** * @MethodName logout * @Description 安全退出 * @return: java.lang.String * @Author Jiangnan Cui * @Date 14:08 2022/6/29 */ @RequestMapping("logout") public String logout(HttpSession session){ session.invalidate();//session失效 return "redirect:/login";//跳转到登录页面 }
完善emplist.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/deleteEmp?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>
(1)未登录
(2)登录后
(3)安全登出后,需要重新登录