1.创立SpringBoot我的项目,引入依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>ems-thymeleaf</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ems-thymeleaf</name> <description>ems-thymeleaf</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>2.5.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
2.批改配置文件application.yml
server: port: 8989 servlet: context-path: /emsspring: thymeleaf: cache: false suffix: .html prefix: classpath:/templates/ datasource: driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://localhost:3306/mydata?useUnicode=true&characterEncoding=UTF-8 username: root password: 835081 web: #裸露哪些资源能够通过我的项目名拜访 resources: static-locations: classpath:/static/,file:${img.file.dir}logging: level: root: info com.example.emsthymeleaf: debugmybatis: mapper-locations: classpath:/mapper/*.xml type-aliases-package: com.example.emsthymeleaf.entity configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 上传文件门路img: file: dir: D:\develop\javademo\baizhidemo\ems-thymeleaf\file
3.创立Config文件,将对html文件的拜访对应到Controller
这样做的目标是为了简化拜访thymeleaf的html文件,不用为每个html文件独自创立controller申请
package com.example.emsthymeleaf.config;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class MvcConfig implements WebMvcConfigurer { //不必再为每一个thymeleaf模板独自开发一个controller申请了 @Override public void addViewControllers(ViewControllerRegistry registry) { //viewController:申请门路,viewName:跳转视图 registry.addViewController("login").setViewName("login"); registry.addViewController("regist").setViewName("regist"); }}
4.设置随机图形验证码
- 引入验证码生成工具VerifyCodeUtils
- 在controller中创立生成验证码实例
package com.example.emsthymeleaf.controller;import com.example.emsthymeleaf.utils.VerifyCodeUtils;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;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;@Controller@RequestMapping("/user")public class UserController { @GetMapping("/generateImageCode") public void generateImageCode(HttpServletResponse response, HttpSession session) throws IOException { //生成随机数 String code = VerifyCodeUtils.generateVerifyCode(4); //存入session session.setAttribute("code",code); //依据随机数生成随即图片 response.setContentType("image/png"); ServletOutputStream outputStream = response.getOutputStream(); VerifyCodeUtils.outputImage(220,60,outputStream,code); }}
- 在thymeleaf的html文件中设置拜访随机验证码
<td valign="middle" align="right"> 验证码: <img id="num" th:src="@{/user/generateImageCode}" /> <a href="javascript:;" onclick="changeImageCode()">换一张</a> <script> function changeImageCode(){ document.getElementById("num").src='[[@{/user/generateImageCode}]]?' + (new Date()).getTime() } </script></td>
5.用户注册
- 引入lombok
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version></dependency>
- 创立实体类User
package com.example.emsthymeleaf.entity;import lombok.Data;@Datapublic class User { private Integer id; private String username; private String realname; private String password; private boolean gender;}
- controller
@PostMapping("/regist")public String regist(User user, String code,HttpSession session) throws UnsupportedEncodingException { log.debug("用户信息:" + user); log.debug("验证码:" + code); try { userSerivce.regist(user,code,session); }catch (Exception ex){ return "redirect:/regist?err=" + URLEncoder.encode(ex.getMessage(),"UTF-8"); } return "redirect:/login";}
- service
package com.example.emsthymeleaf.service;import com.example.emsthymeleaf.entity.User;import javax.servlet.http.HttpSession;public interface UserSerivce { void regist(User user, String code, HttpSession session);}
- serviceImpl
package com.example.emsthymeleaf.service;import com.example.emsthymeleaf.dao.UserDao;import com.example.emsthymeleaf.entity.User;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import org.springframework.util.DigestUtils;import javax.servlet.http.HttpSession;import java.nio.charset.StandardCharsets;@Service@Transactionalpublic class UserServiceImpl implements UserSerivce { private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class); private UserDao userDao; @Autowired public UserServiceImpl(UserDao userDao) { this.userDao = userDao; } @Override public void regist(User user, String code, HttpSession session) { //判断用户输出的验证码和session中存储的验证码是否雷同 log.debug("session code: {}",session.getAttribute("code")); if (!code.equalsIgnoreCase(session.getAttribute("code").toString())) { throw new RuntimeException("验证码谬误!"); } //判断用户名是否已注册过 if (userDao.getUserByUserName(user.getUsername()) != null) { throw new RuntimeException("用户名已注册过了!"); } //给明码加密 user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes(StandardCharsets.UTF_8))); //将输出插入到数据表 userDao.save(user); }}
- dao
package com.example.emsthymeleaf.dao;import com.example.emsthymeleaf.entity.User;public interface UserDao { void save(User user); User getUserByUserName(String username);}
- xml
<?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.example.emsthymeleaf.dao.UserDao"> <insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id"> insert into user values(#{id},#{username},#{realname},#{password},#{gender}) </insert> <select id="getUserByUserName" parameterType="String" resultType="User"> select id,username,realname,password,gender from user where username=#{username} </select></mapper>
- 注册页面.html
<div id="content"> <p id="whereami"> </p> <h1> 注册<span style="color: darkred;" th:text="${#request.getParameter('err')}"></span> </h1> <form th:action="@{/user/regist}" 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="0" checked="checked"/> 女 <input type="radio" class="inputgri" name="gender" value="1"/> </td> </tr> <tr> <td valign="middle" align="right"> 验证码: <img id="num" th:src="@{/user/generateImageCode}" /> <a href="javascript:;" onclick="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 th:href="@{/user/generateImageCode}">已有账号,返回登录</a> </p> </form></div>
6. 用户登录和退出
6.1 用户登录
controller
@RequestMapping("/login")public String login(String username, String password, HttpSession session) throws UnsupportedEncodingException { try { User user = userSerivce.login(username, password); session.setAttribute("user", user); } catch (Exception e) { e.printStackTrace(); return "redirect:/login?err=" + URLEncoder.encode(e.getMessage(), "UTF-8"); } return "redirect:/employee/list";}
service
@Overridepublic User login(String username, String password) { User user = userDao.getUserByUserName(username); if (ObjectUtils.isEmpty(user)) throw new RuntimeException("用户不存在"); String newPassword = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)); if (!user.getPassword().equals(newPassword)) throw new RuntimeException(("明码输出谬误")); return user;}
html
<div id="content"> <p id="whereami"> </p> <h1> 欢送进入,请登录! </h1> <p style="color:red;" th:if="${#request.getParameter('err')}" th:text=" '谬误:' + ${#request.getParameter('err')}"></p> <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 th:href="@{/regist}">还没有账号,立刻注册</a> </p> </form></div>
6.2 用户退出
controller
//用户退出@RequestMapping("/logout")public String logout( HttpSession session){ session.invalidate(); return "redirect:/login";}
html
<a th:href="@{/user/logout}">平安退出</a>
7. 员工治理
7.1 增加员工
- 批改config文件,通过浏览器可能拜访到addEmp.html页面
import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class MvcConfig implements WebMvcConfigurer { //不必再为每一个thymeleaf模板独自开发一个controller申请了 @Override public void addViewControllers(ViewControllerRegistry registry) { //viewController:申请门路,viewName:跳转视图 registry.addViewController("login").setViewName("login"); registry.addViewController("regist").setViewName("regist"); registry.addViewController("addEmp").setViewName("addEmp"); }}
- 增加员工的controller
package com.example.emsthymeleaf.controller;import com.example.emsthymeleaf.entity.Employee;import com.example.emsthymeleaf.service.EmployeeService;import org.apache.commons.io.FilenameUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.multipart.MultipartFile;import java.io.File;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;@Controller@RequestMapping("/employee")public class EmployeeController { @Autowired private EmployeeService employeeService; private static final Logger log = LoggerFactory.getLogger(EmployeeController.class); @Value("${img.file.dir}") private String realPath; @RequestMapping("/save") public String save(Employee employee, MultipartFile img) throws IOException { log.debug("employee: {}",employee); log.debug("file name: {}", img.getOriginalFilename()); //创立新文件名 String newImgName = setNewImgName(img); //将头像文件存储到文件夹 img.transferTo(new File(realPath,newImgName)); //存储新的文件名 employee.setPhoto(newImgName); //存储员工信息 employeeService.save(employee); return "redirect:/employee/list"; } //生成新的文件名 private String setNewImgName(MultipartFile img) { //前缀 String fileNamePrefix = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()); //后缀 String fileNameSuffix = FilenameUtils.getExtension(img.getOriginalFilename()); //新文件名 String newImgName = fileNamePrefix + "." + fileNameSuffix; return newImgName; }}
如何确定文件的上传门路
在application.yml中确定文件的上传门路
# 上传文件门路img: file: dir: D:\develop\javademo\baizhidemo\ems-thymeleaf\file
在java文件中援用它
@Value("${img.file.dir}") private String realPath;
serviceImpl
@Overridepublic void save(Employee employee) { employeeDao.save(employee);}
dao
void save(Employee employee);
dao.xml
<insert id="save" parameterType="Employee" useGeneratedKeys="true" keyProperty="id"> insert into employee values (#{id},#{name},#{photo},#{salary},#{birthday})</insert>
html
<form th:action="@{/employee/save}" 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>
7.2 员工列表
controller
@RequestMapping("list")public String list(Model model) { model.addAttribute("list", employeeService.list()); return "emplist";}
serviceImpl
@Overridepublic List<Employee> list() { return employeeDao.list();}
dao
List<Employee> list();
dao.xml
<select id="list" resultType="Employee"> select id, name, photo, salary, birthday from `employee`</select>
html
<div id="content"> <p id="whereami"> </p> <h1> 欢送 小陈! </h1> <table class="table"> <tr class="table_header"> <td> 编号 </td> <td> 姓名 </td> <td> 头像 </td> <td> 工资 </td> <td> 生日 </td> <td> 操作 </td> </tr> <tr class="row1" th:each="emp:${list}"> <td> <span th:text="${emp.id}"></span> </td> <td> <span th:text="${emp.name}"></span> </td> <td> <img th:src="@{/}+${emp.photo}" width="60"> </td> <td> <span th:text="${emp.salary}"></span> </td> <td> <span th:text="${#dates.format(emp.birthday,'yyyy/MM/dd')}"></span> </td> <td> <a href="javascript:;" th:onclick="'deleteEmployee(' + ${emp.id} + ')'">删除</a> <a th:href="@{/employee/details(id=${emp.id})}">更新</a> </td> </tr> <script> function deleteEmployee(id){ if(window.confirm("是否删除这条记录?")){ location.href='[[@{/employee/delete?id=}]]' + id } } </script> </table> <p> <input type="button" class="button" value="增加" onclick="addEmp()"/> <script type="text/javascript"> function addEmp() { window.location ='[[@{/addEmp}]]' } </script> </p></div>
thymeleaf须要留神的点
- 在<img>标签中获取以后门路的动态文件:
<img th:src="@{/}+${emp.photo}" width="60">
,将门路和文件名连接起来 须要批改application.yml文件,裸露哪些资源能够通过我的项目名拜访
resources: static-locations: classpath:/static/,file:${img.file.dir}
<a>
标签链接到带参数的地址:<a href="localhost:8989/ems/employee/details?id=[从th参数中获取的id值]">更新</a>
,在th中应该写为:<a th:href="@{/employee/details(id=${emp.id})}">更新</a>
- 将标签中的链接改为响应click事件的html语句:
<a href="javascript:;" th:onclick="'deleteEmployee(' + ${emp.id} + ')'">删除</a><script> function deleteEmployee(id){ if(window.confirm("是否删除这条记录?")){ location.href='[[@{/employee/delete?id=}]]' + id } }</script>
7.3 编辑员工信息
7.3.1 依据id获取员工信息
controller
//依据id获取员工信息@RequestMapping("details")public String details(Integer id, Model model) { log.debug("id:{}", id); Employee employee = employeeService.findById(id); model.addAttribute("employee", employee); return "updateEmp";}
service&serviceImpl
Employee findById(Integer id);
@Overridepublic Employee findById(Integer id) { return employeeDao.findById(id);}
dao
Employee findById(Integer id);
<select id="findById" parameterType="Integer" resultType="Employee"> select id, name, photo, salary, birthday from employee where id = #{id}</select>
html显示编辑员工信息页面
<form th:action="@{/employee/update}" 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" th:value="${employee.id}" name="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" name="photo" th:value="${employee.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>
留神两个暗藏类型的input,因为<span>和<img>标签的信息无奈通过申请传递,所以这两个标签上面须要设置暗藏类型的input,用于传递属性值
7.3.2 更新员工信息
controller
//更新员工信息@RequestMapping("update")public String update(Employee employee, MultipartFile img){ log.debug("员工信息:{}",employee); //判断是否更新了头像信息 if (!img.isEmpty()){ //更新头像信息 try { String newImgName = uploadImg(img); employee.setPhoto(newImgName); } catch (IOException e) { e.printStackTrace(); } } employeeService.update(employee); return "redirect:/employee/list";}/** * 存储上传的文件并生成新的文件名 * @param img * @return 返回新的文件名 * @throws IOException */private String uploadImg(MultipartFile img) throws IOException { //前缀 String fileNamePrefix = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()); //后缀 String fileNameSuffix = FilenameUtils.getExtension(img.getOriginalFilename()); //新文件名 String newImgName = fileNamePrefix + "." + fileNameSuffix; //将头像文件存储到文件夹 img.transferTo(new File(realPath, newImgName)); return newImgName;}
service&serviceImpl
void update(Employee employee);
@Overridepublic void update(Employee employee) { employeeDao.update(employee);}
dao
void update(Employee employee);
<update id="update" parameterType="Employee"> update employee set name=#{name},photo=#{photo},salary=#{salary},birthday=#{birthday} where id = #{id}</update>
7.3.3 删除员工信息
controller
//删除员工信息@RequestMapping("/delete")public String delete(Integer id){ log.debug("id={}",id); employeeService.delete(id); return "redirect:/employee/list";}
service&serviceImpl
void delete(Integer id);
@Overridepublic void delete(Integer id) { employeeDao.delete(id);}
dao
void delete(Integer id);
<delete id="delete" parameterType="Integer"> delete from employee where id=#{id}</delete>
8. 拦截器,拦挡未登录用户
定义拦截器
package com.example.emsthymeleaf.interceptors;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); if (session.getAttribute("user") == null){ response.sendRedirect(request.getContextPath() + "/login"); return false; } return true; }}
配置拦截器
package com.example.emsthymeleaf.config;import com.example.emsthymeleaf.interceptors.MyInterceptor;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class MvcConfig implements WebMvcConfigurer { //配置拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/user/**","/login","regist"); } //不必再为每一个thymeleaf模板独自开发一个controller申请了 @Override public void addViewControllers(ViewControllerRegistry registry) { //viewController:申请门路,viewName:跳转视图 registry.addViewController("login").setViewName("login"); registry.addViewController("regist").setViewName("regist"); registry.addViewController("addEmp").setViewName("addEmp"); }}
9. 其余一些细节
在html中获取session中的用户名
欢送 <span th:text="${#session.getAttribute('user').realname}"></span>!