一:导入模板引擎
在pom.xml文件中导入Thymeleaf依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
Thymeleaf的介绍、具体功能、拥有哪些方法、怎么接管前端可以参考官方文档
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#using-texts
二:开发注意
- 建立 Spring Initializr 项目,参考以前发布的文档
- 这个项目会自动生成 resources 文件夹
- 在这个文件夹下存在 static、templates 两个文件夹
- 我们所有的静态资源都要放在 static 包下,例:css、js (JavaScript)、img (图片)、fonts (字体)等静态资源,因为调取静态资源时,SpringBoot自动以static为父级,直接到它下面寻找对应的静态资源,引入路径:../css/xxx.css
- 网页( .html)文件放在 templates 文件夹下,因为使用了Thymeleaf接管前端,SpringBoot会自动到 templates 中寻找对应的网页
- 配置properties文件
#配置文件的真实位置 spring: messages: basename: i18n.login #关闭模板引擎的缓存 thymeleaf: cache: false mvc: format: date: yyyy-MM-dd
国际化文件位置、关闭缓存、自定义时间日期输入与显示格式
查看静态资源:
- 查看添加的webjars包的源码:http://localhost:8080/webjars/
- 查看添加到static下的静态资源:http://localhost:8080/img/xxx.img
扩展:
我们只能在static目录下放静态资源吗?不,可以放在其他地方,我们可以进到源码里看,看它支持那些文件夹,对路径的定义是什么样的,怎么创建文件夹才会被识别到,优先级的关系是什么样的。(优先级:resources>static(默认)>public)
三:项目开发
注意:所有页面的静态资源都需要使用thymeleaf进行接管!
(一)准备
- html文件放在 templates 文件夹下
- 所有静态资源(js、css、img等)放在static下
(二)首页实现
1. 在 templates 下新建 index.html 文件
index.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>index</title> <link rel="stylesheet" href="/webjars/bootstrap/4.1.0/css/bootstrap.min.css"> </head> <body> <div style="width: 10%; position: absolute;top: 30%;left: 45%"> <form th:action="@{/user/login}"> <p style="color: red" th:if="${not #strings.isEmpty(msg)}" th:text="${msg}"></p> <a th:text="#{login.user}">用户名:</a>: <input type="text" name="username"> <a th:text="#{login.passWord}">密码:</a> <input type="password" name="password" > <button type="submit" th:text="#{login.enter}" style="width:100px;height:40px;"> 登录 </button> </form> <a th:href="@{/index.html(a='zh_CN')}">中文</a> <a th:href="@{/index.html(a='en_US')}">英文</a> </div> </body> </html>
2. 实现网页跳转,有两种方法
(1) controller层实现(不推荐)
@Controller public class MyWebController{ @RequestMapping("/index") public String index(){ return "index"; } }
(2)与DemoApplication同级目录下新建config包,自定义MyWebMvcConfig继承WebMvcConfigurer,重写addViewControllers方法
@Configuration public class MyWebMvcConfic implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { //变量名称.addViewController("地址信息").setViewName("跳转的页面"); registry.addViewController("/").setViewName("index"); //设置多个路径 registry.addViewController("/index.html").setViewName("index"); registry.addViewController("/main.html").setViewName("dashboard"); } }
(三)国际化(添加与否纯凭个人喜好与公司要求)
1. resources包下新建i18n包,包中新建文件如图所示:
#login内为默认显示 login.tip=你好 #en_US为英语 login.tip=hello #zh_CN为中文 login.tip=你好
2. 配置文件中配置属性如下:
spring: messages: basename: i18n.login
3. config包下新建MyLocalResolver.class,重写resolveLocale方法、setLocale方法
package com.demo.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.thymeleaf.util.StringUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Locale; @Configuration public class MyLocalResolver implements LocaleResolver { //国际化 @Override public Locale resolveLocale(HttpServletRequest request) { //获取请求中的语言参数 String language = request.getParameter("a"); //如果参数没有就使用系统默认 Locale locale = Locale.getDefault(); if (!StringUtils.isEmpty(language)){ String[] split= language.split("_"); locale = new Locale(split[0],split[1]); } return locale; } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { } }
4. 将重写后的configration配置到springboot成为Bean组件,在MyWebMvcConfig下配置
@Configuration public class MyWebMvcConfic implements WebMvcConfigurer { //自定义国际化组件 @Bean public LocaleResolver localeResolver(){ return new MyLocalResolver(); } }
5. 在index页面添加如下代码,即可实现中英文切换
6. 如果想实现其他国家的语言切换,可以添加对应语言的login_xx_XX.properties,这个格式是固定的,xx_XX为语言种类
7. 注意:需要配置i18n文件,自定义组件LocalResolver,将组件配置到SpringBoot中
(四)实现登录功能+拦截器
1.登录功能
(1) 定义index登录页面,,添加账户名、密码输入框,添加提交按钮
<div style="width: 10%; position: absolute;top: 30%;left: 45%"> <form th:action="@{/user/login}"> <p style="color: red" th:if="${not #strings.isEmpty(msg)}" th:text="${msg}"></p> <a th:text="#{login.user}">用户名:</a>: <input type="text" name="username"> <a th:text="#{login.passWord}">密码:</a> <input type="password" name="password" > <button type="submit" th:text="#{login.enter}" style="width:100px;height:40px;"> 登录 </button> </form> <a th:href="@{/index.html(a='zh_CN')}">中文</a> <a th:href="@{/index.html(a='en_US')}">英文</a> </div>
(2) 在MyWebMvcConfig中重写拦截器方法
//拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { WebMvcConfigurer.super.addInterceptors(registry); registry.addInterceptor(new LoginHandlerInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/index.html","/","/user/login","/css/*","/js/**","/img/**"); }
(3) 对用户登录的信息进行验证
1. 添加LoginController
2. 实现登录验证
@RequestMapping("/user/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){ //具体业务 if (!StringUtils.isEmpty(username) && "123".equals(password)){ session.setAttribute("loginUser",username); return "redirect:/main.html"; }else { model.addAttribute("msg","登录失败"); return "index"; } }
(4) 添加拦截器目的
对用户登录的session信息进行验证
防止浏览器端绕过登录,直接进入到应用,或者session超时后,返回到登录页面
(5) 点击登录跳转到主页面dashboard.html页面
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>dashboard</title> <link rel="stylesheet" href="/webjars/bootstrap/4.1.0/css/bootstrap.min.css"> </head> <body> <p>欢迎[[${session.loginUser}]]</p> <a th:href="@{/index.html}">首页</a> <a th:href="@{/emps}">员工管理</a> <a th:href="@{/user/logout}">注销</a> </body> </html>
(6) MyWebMvcConfic实现跳转
@Configuration public class MyWebMvcConfic implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/index.html").setViewName("index"); registry.addViewController("/main.html").setViewName("dashboard"); } }
(五)员工列表CRUD
一:查询
(1)伪造数据库员工与部门信息
1. 与DemoApplication同级目录新建dao与pojo包
2. 在dao包下新建DepartmentDao、EmployeeDao两个类
3. 在pojo包下新建Department、Employee两个类
代码如下
DepartmentDao:
package com.demo.dao; import com.demo.pojo.Department; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.HashMap; import java.util.Map; @Repository //部门表操作 public class DepartmentDao { //模拟数据库数据 private static Map<Integer, Department> departments = null; static { departments = new HashMap<Integer, Department>();//创建一个部门表 departments.put(1,new Department(1,"教学部")); departments.put(2,new Department(2,"教研部")); departments.put(3,new Department(3,"科学部")); departments.put(4,new Department(4,"党委")); departments.put(5,new Department(5,"团委")); } //获得所有部门信息 public Collection<Department> getDepartments(){ return departments.values(); } //通过id得到部门 public Department getDepartmentById(Integer id){ return departments.get(id); } }
EmployeeDao:
package com.demo.dao; import com.demo.pojo.Department; import com.demo.pojo.Employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.HashMap; @Repository //员工表操作 public class EmployeeDao { //模拟数据库数据 private static HashMap<Integer, Employee> employees = null; //员工有所属的部门,部门表为外表 @Autowired private DepartmentDao departmentDao; static { //创建一个员工表 employees = new HashMap<Integer,Employee>(); employees.put(1,new Employee(1,"董文杰1","1181101901@qq.com",1,new Department(1,"教学部"))); employees.put(2,new Employee(2,"董文杰2","1181101902@qq.com",2,new Department(2,"教研部"))); employees.put(3,new Employee(3,"董文杰3","1181101903@qq.com",1,new Department(3,"科学部"))); employees.put(4,new Employee(4,"董文杰4","1181101904@qq.com",1,new Department(4,"党委"))); employees.put(5,new Employee(5,"董文杰5","1181101905@qq.com",2,new Department(5,"团委"))); } //id递增 public static Integer initId = 6; //增加员工 public void save(Employee employee){ if (employee.getId() == null) { employee.setId(initId++); } employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId())); employees.put(employee.getId(), employee); } //获得全部员工信息 public Collection<Employee> getall(){ return employees.values(); } //通过id查询员工 public Employee getEmployeeById(Integer id){ return employees.get(id); } //删除员工 public void delete(Integer id){ employees.remove(id); } }
Department:
package com.demo.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; //伪造数据库 //部门表 @Data @AllArgsConstructor//有参构造 @NoArgsConstructor//无参构造 public class Department { private Integer id; private String departmentName; }
Employee:
package com.demo.pojo; import lombok.Data; import lombok.NoArgsConstructor; import java.util.Date; @Data @NoArgsConstructor public class Employee { private Integer id; private String name; private String email; private Integer gender;//1男,2女 private Department department; private Date birth; public Employee(Integer id, String name, String email, Integer gender, Department department) { this.id = id; this.name = name; this.email = email; this.gender = gender; this.department = department; //默认创建日期 this.birth = new Date(); } }
(2) 在templates包下新建emp包,在emp包下新建list.html文件
list.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/webjars/bootstrap/4.1.0/css/bootstrap.min.css"> <title>list</title> </head> <body> <table class="table table-bordered table-hover"> <a class="btn btn-sm btn-success" th:href="@{/emp}">添加员工</a> <thead> <tr> <th>id</th> <th>name</th> <th>email</th> <th>gender</th> <th>department</th> <th>birth</th> <th>操作</th> </tr> </thead> <tbody> <tr th:each="emp:${emps}"> <td th:text="${emp.getId()}"></td> <td th:text="${emp.getName()}"></td> <td th:text="${emp.getEmail()}"></td> <td th:text="${emp.getGender()==1?'男':'女'}"></td> <td th:text="${emp.getDepartment().getDepartmentName()}"></td> <td th:text="${#dates.format(emp.getBirth(),'yyyy.MM.dd HH:mm')}"></td> <td> <a th:href="@{'/emp/'+${emp.getId()}}"><button class="btn btn-sm btn-primary">编辑</button></a> <a th:href="@{'/delemp/'+${emp.getId()}}"><button class="btn btn-sm btn-danger">删除</button></a> </td> </tr> </tbody> </table> </body> </html>
(3) 前往controller层新建EmployeeController控制类
EmployeeController:
@Autowired EmployeeDao emploeeDao; @Autowired DepartmentDao departmentDao; @RequestMapping("/emps") public String list(Model model){ Collection<Employee> employees = emploeeDao.getall(); model.addAttribute("emps",employees); return "emp/list"; }
二:添加
在查询列表list页面添加 新增 按钮
(1) emp包下新建add.html
add.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>add</title> <link rel="stylesheet" href="/webjars/bootstrap/4.1.0/css/bootstrap.min.css"> </head> <body> <form class="form-horizontal" th:action="@{/emp}" method="post" style="position: absolute;left: 5%;width: 90%"> <div class="form-group"> <label for="name" class="col-sm-2 control-label">Name</label> <div class="col-sm-10"> <input type="text" class="form-control" name="name" id="name" placeholder="name"> </div> </div> <div class="form-group"> <label for="email" class="col-sm-2 control-label">Email</label> <div class="col-sm-10"> <input type="text" class="form-control" name="email" id="email" placeholder="email"> </div> </div> <div class="form-group"> <label for="birth" class="col-sm-2 control-label">Birth</label> <div class="col-sm-10"> <input type="text" class="form-control" name="birth" id="birth" placeholder="birth"> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Gender</label><br/> <div class="form-check form-check-inline"> <input type="radio" class="form-check-input" name="gender" value="1"> <label class="form-check-label">男</label> </div> <div class="form-check form-check-inline"> <input type="radio" class="form-check-input" name="gender" value="2"> <label class="form-check-label">女</label> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Department</label> <!--我们在controller接收的是一个Employee,所以我们需要提交的是其中的一个属性--> <select class="form-control" name="department.id"> <option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}">教学部</option> </select> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default">添加</button> </div> </div> </form> </body> </html>
(2) 点击新增按钮,在EmployeeController实现页面跳转
@GetMapping("/emp") public String toAddPage(Model model){ //查询部门信息 Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("departments",departments); return "/emp/add"; }
(3) 填写新增员工信息后点击提交,controller层EmployeeController实现添加操作
EmployeeController:
@PostMapping("/emp") public String addEmp(Employee employee){ //添加的操作 forward //调用底层业务方法保存员工信息 emploeeDao.save(employee); return "redirect:/emps"; }
注意:跳转时都是"/emp",为什么会分成两个方法,因为注解不一样,PostMapping处理method为post的请求,而Get处理普通请求;
三:修改
在员工信息后添加 修改 按钮
(1) emp包下新建update.html
update.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>update</title> <link rel="stylesheet" href="/webjars/bootstrap/4.1.0/css/bootstrap.min.css"> </head> <body> <form class="form-horizontal" th:action="@{/updateEmp}" method="post" style="position: absolute;left: 5%;width: 90%"> <input type="hidden" name="id" th:value="${emp.getId()}"> <div class="form-group"> <label for="name" class="col-sm-2 control-label">Name</label> <div class="col-sm-10"> <input th:value="${emp.getName()}" type="text" class="form-control" name="name" id="name" placeholder="name"> </div> </div> <div class="form-group"> <label for="email" class="col-sm-2 control-label">Email</label> <div class="col-sm-10"> <input th:value="${emp.getEmail()}" type="text" class="form-control" name="email" id="email" placeholder="email"> </div> </div> <div class="form-group"> <label for="birth" class="col-sm-2 control-label">Birth</label> <div class="col-sm-10"> <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm')}" type="text" class="form-control" name="birth" id="birth" placeholder="birth"> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Gender</label><br/> <div class="form-check form-check-inline"> <input th:checked="${emp.getGender()==1}" type="radio" class="form-check-input" name="gender" value="1"> <label class="form-check-label">男</label> </div> <div class="form-check form-check-inline"> <input th:checked="${emp.getGender()==2}" type="radio" class="form-check-input" name="gender" value="2"> <label class="form-check-label">女</label> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">Department</label> <!--我们在controller接收的是一个Employee,所以我们需要提交的是其中的一个属性--> <select class="form-control" name="department.id"> <option th:selected="${dept.getId()==emp.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}">教学部</option> </select> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default">确认修改</button> </div> </div> </form> </body> </html>
(2) 点击修改按钮,在EmployeeController实现页面跳转
@GetMapping("/emp/{id}") public String toUpdateEmp(@PathVariable("id") Integer id, Model model){ Employee employee = emploeeDao.getEmployeeById(id); model.addAttribute("emp",employee); //查询部门信息 Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("departments",departments); return "/emp/update"; }
(3) 修改员工信息后点击提交,controller层EmployeeController实现修改操作
EmployeeController:
//员工信息修改 @RequestMapping("/updateEmp") public String updateEmp(Employee employee){ emploeeDao.save(employee); return "redirect:/emps"; }
三:删除
在员工信息后添加 删除 按钮
(1) 点击删除按钮,在EmployeeController实现功能
//删除员工 @GetMapping("/delemp/{id}") public String delEmp(@PathVariable("id") Integer id){ emploeeDao.delete(id); return "redirect:/emps"; }
注意:删除与添加和修改不一样,删除直接依据当前员工id查询即可删除。
完整目录与代码结构如下
LoginHandlerInterceptor
MyLocalResolver
MyWebMvcConfic
EmployeeController {{{
}}}
LoginController
DepartmentDao
EmployeeDao {{{
}}}
Department
Employee
login.properties
所有的java代码都已截图,还有兴趣去添加没有找到网页(404)或者服务器错误(500)的小伙伴可以借鉴一下我写的跳转的代码,自定义html文件,实现404或者500后的提示。