这里我们使用lombok帮助我们自动生成pojo包的getter和setter等函数结构
在IDEA中安装lombok插件
等待插件安装完成后,在项目pom中导入lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
在整合mybets之前,我们手动构建pojo和dao层进行模拟数据库
使用相应的注解来完善pojo结构
@Data
注解用于生成属性的getter与setter@AllArgsConstructor
用于生成有参构造函数@NoArgsConstructor
用于生成无参构造函数Department.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String departmentName;
}
Employee.java
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Department department;
private Date birth;
}
在自定义MVC配置中重写一个 addViewControllers
,用来添加一些基本的视图控制器
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/index/").setViewName("index");
}
}
测试访问 http://localhost:8080/index
设置IDEA的文件编码
在 resources 目录下创建 i18n
目录,用来存放多语言的配置文件,并创建如下文件结构
login.properties
是我们的主配置文件, login_en_XX.properties
是我们的指定的各个语言的配置文件
点击可视化配置 Resource Bundle
,添加一个配置键值对,这里我们命名login.tip
在编辑器的右边输入对 login.tip
进行各个语言的配置,会分别映射到我们刚才创建好的几个语言的配置文件中
按照如上步骤,我们把页面的几个标签对应的也配置好
在配置文件 application.properties
中绑定多语言配置文件位置
spring.messages.basename=i18n.login
使用 thymeleaf 模板语法中的 th:XX="#{}"
对各个标签进行接管,例如
<label class="sr-only" th:text="#{login.username}">Username</label>
#{}
中填写刚才我们在配置文件中添加的键
在config中自定义一个转换器 MyLocaleResolver
,根据用户请求的参数进行设置页面的语言
public class MyLocaleResolver implements LocaleResolver {
//解析请求
@Override
public Locale resolveLocale(HttpServletRequest request) {
//获取请求中的语言参数
String language = request.getParameter("lang");
Locale locale = Locale.getDefault(); //如果没有就使用默认的
//如果请求的链接携带了国际化的参数
if (!StringUtils.isEmpty(language)){
//分割字符串
String[] lang_split = language.split("_");
locale = new Locale(lang_split[0], lang_split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
在我们之前自定义的mvc类中,使用 @Bean
注解将我们的转换器 MyLocaleResolver
配置到spring容器内
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/index/").setViewName("index");
}
//将自定义的国际化组件注册到spring bean中
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}
接下来在HTML模板中使用thymeleaf模板语法中的 th:href="@{xxx.html(key=value)}"
,设置跳转请求
<a class="btn btn-sm" th:href="@{/index.html(lang='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(lang='en_US')}">English</a>
重新启动项目,查看效果
成功实现多语言的切换
创建一个Controller,我这里命名为 LoginController
,编写基本的结构,返回一个字符串进行测试
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String Login(){
return "OK";
}
}
在HTML中提交的表单中修改请求url
<form class="form-signin" action="/user/login" method="POST">
测试
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String Login(
@RequestParam("username") String username,
@RequestParam("password") String password,
Model model
){
if(("admin".equals(username) && "123123".equals(password))){
session.setAttribute("loginUser", username);
//重定向至main页面
return "redirect:/main";
}else{
model.addAttribute("msg", "用户名或密码错误"); //将错误信息渲染至页面
return "index";
}
}
}
这里暂时没有整合数据库,所以直接判断页面提交的值是否等于预定义的值,如果等于则重定向到main页面,否则返回登录页并渲染错误信息
前端页面新增一个p标签用于显示错误信息,使用 thymeleaf
模板引擎进行渲染
<!--如果msg值为空则不显示错误信息-->
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
添加一个main页面的路由,这里我在自定义的mvc配置类中重写了一个视图控制器,也可以自己新增一个controller进行路由
registry.addViewController("/main").setViewName("dashboard");
自定义一个拦截器LoginHandlerInterceptor
,并实现 HandlerInterceptor
的preHandle
方法,自定义拦截的逻辑
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//拦截逻辑
Object loginUser = request.getSession().getAttribute("loginUser");
if(loginUser == null){
//设置提示信息
request.setAttribute("msg","没有权限,请先登录");
//跳转回到主页
request.getRequestDispatcher("/").forward(request, response);
return false;
}
return true;
}
}
在我们自定义的springmvc配置类中注册我们的拦截器 LoginHandlerInterceptor
//注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//设置拦截的黑名单和白名单
//我们还要给静态资源设置白名单,不然显示不出来
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/", "/index*","/user/login", "/css/**", "/img/**", "/js/**");
}
登录成功后取session中的用户名显示到main页面
<a>[[${session.loginUser}]]</a>
测试拦截器、登录验证、主页显示
我们从main页面种可以看出,页面的顶部栏和侧边栏的样式的固定的,所以我们可以将这两个部分独立成模块,在创建新的页面时我们可以直接复用预定义好的模块,减少代码量
我们新建一个 base.html
的页面,用于定义我们模块的代码,使用 th:fragment
标签定义模块的名称,在其他页面使用 th:insert
或 th:replace
引用模板
th:insert
:保留自己的主标签,保留th:fragment的主标签。th:replace
:不要自己的主标签,保留th:fragment的主标签。定义模板
<!--顶部栏-->
<nav th:fragment="topBar" class="navbar"></nav>
<!--侧边栏-->
<nav th:fragment="leftBar" class="col-md-2"></nav>
引用模板
<!--顶部栏-->
<div th:replace="~{commons/dashboard_base :: topBar}"></div>
<!--侧边栏-->
<div th:replace="~{commons/dashboard_base :: leftBar}"></div>
在点击侧边栏的功能时候,需要在跳转页面后将指定的项高亮显示,例如下图
在引用模板时候增加一个页面变量作为标识,如下代码
<div th:replace="~{commons/dashboard_base :: leftBar(barType='emps')}"></div>
在上面的代码当中,我们定义了一个barType
的变量,作为页面类型的标识
接下来我们在base模板中对页面传递过来的变量进行识别
<li class="nav-item">
<a th:class="${barType == 'main'? 'nav-link active': 'nav-link'}" th:href="@{/main}"></a>
</li>
<li class="nav-item">
<a th:class="${barType == 'emps'? 'nav-link active': 'nav-link'}" th:href="@{/emps/info}"></a>
</li>
在上面的代码当中,我们使用了thymeleaf
的三元运算符进行渲染,如果传递过来的barType
符合预期设定的值,则在标签的class中增加active
实现高亮
定义一个Controller,使用Autowired
标识将EmployeeDao
注入到当前Controller中,用于实现对employee的pojo的一些操作,调用了getEmpsInfo
函数获取所有员工信息的值,并将所有员工信息作为model传递到页面中,代码如下
EmployeeController.java
@Controller
public class EmployeeController {
@Autowired
EmployeeDao employeeDao; //注入
@GetMapping("/emps/info")
public String showInfo(Model model){
Collection<Employee> empsInfo = employeeDao.getEmpsInfo();
model.addAttribute("empsInfo", empsInfo);
return "employee/info";
}
}
在info页面中定义一个table标签,使用 th:each
对controller传递到页面的员工信息进行遍历,并渲染到页面,代码实现如下
emps/info.html
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>性别</th>
<th>部门</th>
<th>生日</th>
<th>操作</th>
</tr>
</thead>
<tr th:each="emp:${empsInfo}">
<th th:text="${emp.id}"></th>
<td th:text="${emp.lastName}">2</td>
<td th:text="${emp.email}">3</td>
<td th:text="${emp.gender}">4</td>
<td th:text="${emp.department.departmentName}">5</td>
<!--格式化日期-->
<td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}"></td>
<td>
<button type="button" class="btn btn-primary btn-sm">编辑</button>
<button type="button" class="btn btn-danger btn-sm">删除</button>
</td>
</tr>
</table>
/emps/info.html
<h2><a style="color: white;" class="btn btn-sm btn-info" th:href="@{/emps/add}">添加员工</a></h2>
控制器返回add页面,并携带部门名称信息
@Autowired
EmployeeDao employeeDao; //注入Dao
@Autowired
DepartmentDao departmentDao;
@GetMapping("/emps/add")
public String addEmp(Model model){
Collection<Department> departments = departmentDao.getDepartments(); //传递部门信息到前端
model.addAttribute("departments", departments);
return "employee/add";
}
add.html页面主体数据
<!--添加员工表单-->
<form th:action="@{/emps/add}" th:method="POST">
<div class="form-group">
<label>LastName</label>
<input name="lastName" type="text" class="form-control" placeholder="b5ck">
</div>
<div class="form-group">
<label>Email</label>
<input name="email" type="email" class="form-control" placeholder="XXXXX@qq.com">
</div>
<div class="form-group">
<label>Gender</label><br/>
<div class="form-check form-check-inline">
<input class="form-check-input " type="radio" name="gender" value="1" checked="true">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="0">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>department</label>
<select class="form-control" name="department.id">
<option th:each="dep:${departments}" th:value="${dep.getId()}" th:text="${dep.getDepartmentName()}"></option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input NAME="birth" type="text" class="form-control date-cell" placeholder="2020-01-01">
</div>
<button type="submit" class="btn btn-primary">添加</button>
页面效果
携带用户填写的数据,控制器接收请求
@PostMapping("/emps/add")
public String addEmpPost(Employee employee, RedirectAttributes model){
employeeDao.save(employee);
model.addFlashAttribute("add",true);
return "redirect:/emps/info";
// return "employee/info";
}
在上述代码中,接收到了用户提交表单的数据,转换为Employee
对象,并调用前面代码中注入的employeeDao
中的save函数将对象写入现有的数据中。
写入成功后,我们传递一个名称为add
的model值至页面,用于标识添加成功,在页面添加相应的提示,因为这里使用的是redirect
进行重定向页面,所以需要使用RedirectAttributes对象来传递model
值至页面。
在info页面中添加一个提示框标签
<!--添加成功提示框-->
<div th:if="${add == true}" class="alert alert-success alert-dismissable">
<button type="button" class="close" data-dismiss="alert"
aria-hidden="true">
×
</button>
员工信息添加成功!
</div>
该标签中使用th:if
语句对model对象add
进行判断,值为true时才对该div标签进行渲染,页面效果如下
controller全部代码
@Controller
public class EmployeeController {
@Autowired
EmployeeDao employeeDao; //注入Dao
@Autowired
DepartmentDao departmentDao;
@GetMapping("/emps/info")
public String showInfo(Model model){
Collection<Employee> empsInfo = employeeDao.getEmpsInfo();
model.addAttribute("empsInfo", empsInfo);
return "employee/info";
}
@GetMapping("/emps/add")
public String addEmp(Model model){
Collection<Department> departments = departmentDao.getDepartments(); //传递部门信息到前端
model.addAttribute("departments", departments);
return "employee/add";
}
@PostMapping("/emps/add")
public String addEmpPost(Employee employee, RedirectAttributes model){
employeeDao.save(employee);
model.addFlashAttribute("add",true);
return "redirect:/emps/info";
}
}
确认框
<!-- 模态框(Modal) -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">确定要删除该员工的信息吗?</h4>
</div>
<div class="modal-body">
<label>员工ID</label>
<input th:value="${emp.id}" disabled="true" class="form-control" type="text" name="" >
<label style="margin-top: 10px">员工姓名</label>
<input th:value="${emp.lastName}" disabled="true" class="form-control" type="text" name="">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<a class="btn btn-danger btn-default" th:href="@{/emps/delete/}+${emp.id}">确定删除</a>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal -->
</div>
点击删除按钮,触发模态框
<button class="btn btn-danger btn-sm" data-target="#myModal" data-toggle="modal">删除</button>
控制器
//删除员工信息
@GetMapping("/emps/delete/{id}")
public String toDelete(@PathVariable("id") Integer id, RedirectAttributes model) {
String lastName = employeeDao.getEmployeeById(id).getLastName();
employeeDao.delete(id);
model.addFlashAttribute("msg", "员工 " + lastName + " 信息已被删除");
return "redirect:/emps/info";
}
测试删除
使用th:herf
渲染链接,并携带当前行的员工的id号
<a class="btn btn-primary btn-sm" th:href="@{/emps/update/}+${emp.id}">编辑</a>
控制器代码如下
@GetMapping("/emps/update/{id}")
public String updateEmpInfo(@PathVariable("id") Integer id, Model model){
Employee employeeById = employeeDao.getEmployeeById(id);
Collection<Department> departments = departmentDao.getDepartments(); //获取所有部门信息
System.out.println(employeeById);
model.addAttribute("emp", employeeById);
model.addAttribute("deps", departments);
return "employee/update";
}
使用{}
在请求路径中取id值,通过@PathVariable
将id值赋值给变量
根据员工id获取该员工的所有信息,获取所有部门的信息,并渲染至页面中;
update.html页面主体代码如下
<!--更新员工表单-->
<form th:action="@{/emps/update/} + ${emp.id}" th:method="POST">
<div class="form-group">
<label>LastName</label>
<input th:value="${emp.lastName}" name="lastName" type="text" class="form-control" placeholder="b5ck">
</div>
<div class="form-group">
<label>Email</label>
<input th:value="${emp.email}" name="email" type="email" class="form-control" placeholder="123@qq.com">
</div>
<div class="form-group">
<label>Gender</label><br/>
<div class="form-check form-check-inline">
<input th:checked="${emp.gender == 1}" class="form-check-input " type="radio" name="gender" value="1" checked="true">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<!-- 自动选中lable -->
<input th:checked="${emp.gender == 0}" class="form-check-input" type="radio" name="gender" value="0">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label >department</label>
<select class="form-control" name="department.id">
<option th:each="dep:${deps}" th:selected="${emp.department.id == dep.getId()}" th:value="${dep.getId()}" th:text="${dep.getDepartmentName()}"></option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input th:value="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm:ss')}" NAME="birth" type="text" class="form-control date-cell" placeholder="2020-01-01">
</div>
<button type="submit" class="btn btn-primary">更新</button>
</form>
前端代码与add.html页面相似,不同的是表单中的值使用th:value
从控制器中传递的model取出赋值搭到对应的位置供用户修改;
性别lable中使用th:checked="${emp.gender == 1}"
对gender
值进行判断,使得lable标签能自动选中,而部门select
下拉框使用th:selected
对当前emp对象的部门ID值与dep值相比较,使得option
标签能被自动选择。
实现效果
定义一个用于接收修改请求的控制器
@PostMapping("/emps/update/{id}")
public String toUpdateEmpInfo(@PathVariable("id") Integer id, Employee employee, RedirectAttributes model){
//由于前端传递过来的只有部门ID号,需要重新将部门名称赋值
employee.setDepartment(departmentDao.getDepartmentsById(employee.getDepartment().getId()));
employeeDao.updateInfo(id, employee);
model.addFlashAttribute("msg", employee.getLastName() +" 信息更新成功");
return "redirect:/emps/info";
}
同样的从URL中取ID值,调用employeeDao
中的updateInfo
方法进行员工信息的更新。
修改用户AA
的邮箱为123@qq.com,部门为后勤部,点击更新。
更新成功!
添加控制器
Controller/LoginController.java
@RequestMapping("/user/logout")
public String Logout(HttpSession session){
session.invalidate(); //清除该session会话信息
return "redirect:/index";
}
在模板页面 dashboard_base.html
中添加注销按钮样式,并提交请求
<a th:href="@{/user/logout}" class="btn btn-warning">Sign out</a>
在templates
目录下创建一个error
目录,创建一个404.html
页面,当请求发生404状态时,springboot会自动重定向到404.html
页面中
测试404
其他错误状态也是如此,例如500.html
、403.html