提示:新建项目时,要导入 thymeleaf 和 web 的启动器以及 lombok 依赖。
静态资源和 html 页面的放置位置:
使用 Thymeleaf 模板引擎:
xmlns:th="http://www.thymeleaf.org"
;th:
,让 Thymeleaf 替换接管。Thymeleaf 表达式:Link URL Expressions :
@{/URL}
,一定注意:路径要以斜杠开头。
编写两个实体类:Department 和 Employee。
一个实体类对应一个 Dao,编写两个 Dao:DepartmentDao 和 EmployeeDao。
@Repository
public class DepartmentDao {
// 模拟数据库中的数据
private static Map<Integer, Department> departments = new HashMap<>();
static {
departments.put(1001, new Department(1001, "教学部"));
departments.put(1002, new Department(1002, "研发部"));
departments.put(1003, new Department(1003, "市场部"));
departments.put(1004, new Department(1004, "后勤部"));
}
// 查询所有的部门
public Collection<Department> selectAllDepartments() {
return departments.values();
}
// 根据 ID 查询部门
public Department selectDepartmentById(int id) {
return departments.get(id);
}
// 根据部门名称查询部门 ID
public Integer selectIdByDepartName(String departmentName) {
Collection<Department> values = departments.values();
for (Department value : values) {
if (departmentName.equals(value.getDepartmentName())) {
return value.getId();
}
}
return 0;
}
}
@Repository
public class EmployeeDao {
@Autowired
private DepartmentDao departmentDao;
// 模拟数据库中的数据
private static Map<Integer, Employee> employees = new HashMap<>();
static {
employees.put(1, new Employee(1, "小明", "123456@qq.com", 1, new Department(1001, "教学部")));
employees.put(2, new Employee(2, "小强", "100211@qq.com", 1, new Department(1002, "研发部")));
employees.put(3, new Employee(3, "大杜", "789456@qq.com", 0, new Department(1003, "市场部")));
employees.put(4, new Employee(4, "张三", "000111@qq.com", 0, new Department(1004, "后勤部")));
employees.put(5, new Employee(5, "月光", "448833@qq.com", 1, new Department(1002, "研发部")));
}
private int initIndex = 6;
// 增加员工
public boolean addEmployee() {
int index = initIndex++;
employees.put(index, new Employee(index, "员工" + index, "111222@qq.com",
new Random().nextInt(2),
departmentDao.selectDepartmentById(1001 + (new Random().nextInt(4)))));
return true;
}
// 修改员工信息
public boolean updateEmployee(Employee employee) {
// 使员工的部门 id 与部门名称对应起来
employee.setDepartment(new Department(
departmentDao.selectIdByDepartName(employee.getDepartment().getDepartmentName()),
employee.getDepartment().getDepartmentName())
);
// 覆盖原来的数据
employees.put(employee.getId(), employee);
System.out.println(employees.get(employee.getId()));
return true;
}
// 查询全部员工
public Collection<Employee> selectAllEmployees() {
return employees.values();
}
// 根据 ID 查询员工
public Employee selectEmployeeById(int id) {
return employees.get(id);
}
// 删除员工
public boolean deleteEmployee(int id) {
employees.remove(id);
return true;
}
}
方式一:可以在 controller 中配置
@RequestMapping({"/", "/index.html"})
public String index() {
return "index";
}
说明:访问
/
和/index.html
都会跳转到首页。
方式二:可以在扩展配置类中配置,使 url 与要跳转的页面一一映射
说明:配置首页时,一般用扩展配置类进行配置。
运行结果
设置前端页面
编写后端代码
结果显示
注意:可以看到登录成功和失败都可以达到想要的效果,但是两种情况的 url 都一样,接下来进行一点改进。
改进:登录成功时,重定向到 /main.html
请求,再通过配置,使得 /main.html
请求与登录成功页面相映射。
效果
总结:
http://localhost:8080/Sun/main.html
,也可以直接跳转到成功页面,因此需要增加拦截器;# 关闭模板引擎的缓存
spring.thymeleaf.cache=false
# 设置项目虚拟路径
server.servlet.context-path=/Sun
拦截器进行拦截操作的依据就是:是否进行了登录操作。因此,在登录成功后,在 session 中存放指定信息。
实现拦截器的方法:
HandlerInterceptor
接口;如果未经登录,直接在地址栏输入 http://localhost:8080/Sun/main.html
,会被拦截,效果
后端
@Controller
public class MyController {
@Autowired
private EmployeeDao employeeDao;
// 查询全部员工
@RequestMapping("/select")
public String select(Model model) {
Collection<Employee> employees = employeeDao.selectAllEmployees();
model.addAttribute("employees", employees);
return "list";
}
}
注意:
- 注解 @RequestMapping 中的路径会改变 url;
- model 对象将数据存放在 request 域中,所以在请求转发时会携带 model 对象的数据;
- 这里的
return "list"
为请求转发,会经过视图解析器。
前端
效果
后端
// 增加员工
@RequestMapping("/add")
public String add() {
employeeDao.addEmployee();
return "redirect:/select";
}
前端
效果
后端
// 跳转到修改员工信息页面
@RequestMapping("/update/{employeeId}")
public String update(@PathVariable("employeeId") int id, Model model) {
Employee employee = employeeDao.selectEmployeeById(id);
model.addAttribute("employee", employee);
return "update";
}
// 修改员工信息
@PostMapping("/update")
public String updateEmployee(Employee employee) {
employeeDao.updateEmployee(employee);
return "redirect:/select";
}
前端:展示信息页面
前端:修改员工信息页面
说明:
- 跳转到修改员工信息页面时,会携带该员工的所有信息,通过 value 属性可以显示初始值;
- 后端接收的参数为 Employee 对象,但是员工的 id 不需要被修改,因此,将 id 属性放在隐藏域中;
- 前端在提交属性的值时,如果该属性是一个对象,无法直接提交,必须提交的是对象的单个属性,如:
department.departmentName
。
效果
后端
// 删除员工
@RequestMapping("/delete/{employeeId}")
public String delete(@PathVariable("employeeId") int id) {
employeeDao.deleteEmployee(id);
return "redirect:/select";
}
前端
效果
通过在 session 中移除登录信息,实现退出登录的功能。
后端
// 注销
@RequestMapping("/user/logout")
public String logout(HttpSession session) {
session.removeAttribute("loginUser");
return "redirect:/index.html";
}
前端
效果
在修改员工信息后提交表单时,出现 Failed to convert from type [java.lang.String] to type [java.util.Date]
错误:
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
声明。请求转发和重定向区别:
区别 | 请求转发 | 重定向 |
---|---|---|
行为 | 服务端行为 | 客户端行为 |
速度 | 快 | 慢 |
请求个数 | 一个请求 | 两个不同的请求 |
URL 地址 | 不改变 | 改变 |
Request 域的信息 | 可以访问(因为是同一个请求) | 不能访问 |
WEB-INF 文件夹 | 可以访问 | 不能访问 |
说明:
- 请求转发:客户端 – 请求 – 服务端(请求转发) – 同一个请求 – 目标页面;
- 重定向:客户端 – 请求 – 服务端 – 重定向响应 – 客户端(重定向) – 新的请求 – 服务端 – 目标页面。
前端在提交属性的值时,如果该属性是一个对象,无法直接提交,必须提交的是对象的单个属性;
Spring 用自动装配(从容器中取对象)代替了 new 对象。因此,创建对象时,可以 new 对象,也可以自动装配对象;
自动装配 @Autowired 不能在静态变量上使用,因为当创建类,类加载器加载静态变量时,Spring 上下文尚未加载,所以类加载器不会在 bean 中正确注入静态类,注入结果为 null ;
定制错误界面的方法:在 templates 包下新建 error 文件夹,将错误界面如 404.html
、500.html
放到 error 文件夹下即可。