0%

【SpringBoot】项目实例:员工管理系统

简单用个实例来加深SpringBoot的使用

一、基础准备

1.1 创建JavaBean

  • (1)导入lombok依赖
1
2
3
4
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

在SpringBoot中导入lombok依赖不能使用,还要进行插件安装
“Settings” –> “Plugins” –> 搜索”lomnok” –> 安装

  • (2)Department类
1
2
3
4
5
6
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String departmentName;
  • (3)Employee类
1
2
3
4
5
6
7
8
9
10
11
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Department department;
private Date date;
}

1.2 创建对应的DAO类

为了方便,就暂时不整合Mybatis,就将数据存放在Java类中,并直接实现其方法

  • (1)DepartmentDao类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class DepartmentDao {
//模拟数据库的数据

private static Map<Integer, Department> departmentMap = null;

static {
departmentMap = new HashMap<>();

departmentMap.put(101, new Department(101, "教学部"));
departmentMap.put(102, new Department(102, "市场部"));
departmentMap.put(103, new Department(103, "教研部"));
departmentMap.put(104, new Department(104, "运营部"));
departmentMap.put(105, new Department(105, "后勤部"));
}

//查询所有部门
public static Collection<Department> getDepartMents(){
return departmentMap.values();
}

//通过ID查找部门
public static Department getDepartmentById(Integer id){
return departmentMap.get(id);
}
}
  • (2)EmployeeDao类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class EmployeeDao {

//模拟数据库的数据

private static Map<Integer, Employee> employees;

static{
employees = new HashMap<>();

employees.put(101, new Employee(101, "C酱", "CC.com", 1, DepartmentDao.getDepartmentById(101)));
employees.put(102, new Employee(102, "咕料", "gu.com", 0, DepartmentDao.getDepartmentById(102)));
employees.put(103, new Employee(103, "定春", "sadaharu.com", 0, DepartmentDao.getDepartmentById(103)));
employees.put(104, new Employee(104, "YJJ", "yjj.com", 0, DepartmentDao.getDepartmentById(104)));
employees.put(105, new Employee(105, "盐取", "tukubi.com", 0, DepartmentDao.getDepartmentById(105)));
}

//主键自增模拟
private static Integer initId = 106;

//添加员工
public static void addEmployee(Employee employee, Integer id){
if (employee.getId() == null){
employee.setId(initId++);
}
if (employee.getDepartment() == null){
employee.setDepartment(DepartmentDao.getDepartmentById(id));
}

employees.put(employee.getId(), employee);
}

//查询所有员工
public static Collection<Employee> getEmployees(){
return employees.values();
}

//通过ID查询员工
public static Employee getEmployeeById(Integer id){
return employees.get(id);
}

//删除员工
public static void delete(Integer id){
employees.remove(id);
}
}

1.3 拓展WebMvc配置类

1
2
3
4
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

}

1.4 实例需要的静态资源下载

  • 百度网盘 提取码:vi6a

  • asserts放在静态资源文件夹

  • 其他html放在templates文件夹下


二、设置主页

虽然将index.html直接放在静态资源文件夹就可以自动设置主页,但真正的开发中一般将网页和静态资源区分,所以会将页面放在templates文件夹中

因此需要导入模板引擎thymeleaf启动器

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

2.1 通过Controller跳转

1
2
3
4
5
6
7
8
9
10
@Controller
public class IndexController {

//跳转首页
@RequestMapping({"/", "/index.html"})
public String getIndex(){
return "index";
}

}

2.2 通过拓展WebMvc配置类跳转

1
2
3
4
5
6
7
8
9
10
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

//视图控制器:控制网页跳转
@Override
public void addViewControllers(ViewControllerRegistry registry){
//跳转到首页
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}

2.3 解决CSS样式丢失问题

CSS样式丢失原因为thymeleaf模板引擎取值的方式不同

  • 修改方法如下

也顺便把其他页面的本地资源位置重新按照thymeleaf方式修改


三、国际化设置

所谓国际化,就是支持页面语言的切换

3.1 创建配置文件

  • (1)在resources文件夹下创建名为i18n的文件夹(国际化 –> Internationalization –> 首字母i 和 尾字母之间隔了18个字母)
  • (2)并在i18n文件夹下创建login.properties(名字随意)【默认语言】,login_zh_CN.properties【中文】,login_en_US.propertirs【英文】,会自动合并为login
  • (3)添加网页中需要语言的参数,IDEA中有便捷方式快速添加

3.2 配置信息

  • (1)源码分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class MessageSourceAutoConfiguration {

private static final Resource[] NO_RESOURCES = {};

@Bean
@ConfigurationProperties(prefix = "spring.messages") //配置前缀
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}

@Bean //从MessageSourceProperties中获取信息源
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
messageSource.setBasenames(StringUtils
.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
||能配置的内容
public class MessageSourceProperties {

private String basename = "messages"; //信息名

private Charset encoding = StandardCharsets.UTF_8; //编码集
}

总结:在配置文件中填写配置信息的位置spring.messages.basename=””

  • (2)在配置文件设置信息的位置
1
2
3
spring:
messages:
basename: i18n.login #信息位置

3.3 修改主页提取信息

3.4 语言切换设置

  • (1)源码分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
WebMvcAutoConfiguration.java
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale()); //如果用户配置了地区分解器,就使用用户
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver(); //没有配置则新建一个
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}

||跳转
public class AcceptHeaderLocaleResolver implements LocaleResolver { //自定义地区分解器,实现localeResolver接口
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = getDefaultLocale();
if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
return defaultLocale;
}
Locale requestLocale = request.getLocale();
List<Locale> supportedLocales = getSupportedLocales();
if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) {
return requestLocale;
}
Locale supportedLocale = findSupportedLocale(request, supportedLocales);
if (supportedLocale != null) {
return supportedLocale;
}
return (defaultLocale != null ? defaultLocale : requestLocale);
}
}

总结:新建一个自定义的地区分解器,将次注入的WebMvc配置类中

  • (2)修改主页
    • 添加链接重新跳转会主页,并传递地区参数信息
  • (3)自定义localeResolver(地区分解器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyLocaleResolver implements LocaleResolver {

//解析请求
@Override
public Locale resolveLocale(HttpServletRequest request) {
//获取请求的参数
String language = request.getParameter("l");

Locale locale = Locale.getDefault();//如果没有就使用默认的
if (language != null){
//举例:zh_CN
String[] split = language.split("_"); //按“_”进行分割

//locale: 国家,地区
locale = new Locale(split[0], split[1]);
}
return locale;
}

@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

}
}
  • (4)将localeResolver注入WebMvc配置类中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

//视图控制器:控制网页跳转
@Override
public void addViewControllers(ViewControllerRegistry registry){
//跳转到首页
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}

//注入地区分解器
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}

四、编写登录后的页面

4.1 编写主页

4.2 编写Controller跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class LoginController {

@RequestMapping("/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
//具体的业务逻辑判端,瞎写
if ("莱特雷".equals(username) && "123".equals(password)){
return "dashboard";
}
model.addAttribute("msg", "用户名或密码出现错误"); //登录失败,向前端发送消息
return "index";
}
}

4.3 隐藏账号密码

  • (1)模拟登录出现问题
  • (2)解决问题
    • 在网页跳转在套一层跳转
    • 用Controller跳转 / 拓展WebMvc配置类编写
1
2
3
4
5
6
7
8
9
10
Controller修改
@RequestMapping("/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model modeln){
//具体的业务逻辑判端,瞎写
if ("莱特雷".equals(username) && "123".equals(password)){
return "redirect:/main.html"; //将页面跳转到/main.html(作为一个中间跳转)
}
model.addAttribute("msg", "用户名或密码出现错误"); //登录失败,向前端发送消息
return "index";
}
1
2
3
4
5
6
7
8
9
10
11
12
WebMvc配置页面跳转
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

//视图控制器:控制网页跳转
@Override
public void addViewControllers(ViewControllerRegistry registry){
//跳转到首页
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");//隐藏登录后的用户密码
}

五、添加拦截器

添加中间跳转隐藏密码出现新的问题:直接页面输入localhost:8080/main.html会绕过登录界面,此时需要配置拦截器来拦截下来

5.1 创建自定义的拦截器

  • 类实现HandlerInterceptor接口,重写方法
1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyInterceptor implements HandlerInterceptor {

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//登录成功后:应该有用户的Session
Object login = request.getSession().getAttribute("UserLogin");
if (login == null){
request.setAttribute("msg", "没有权限,请先登录");
request.getRequestDispatcher("/index.html").forward(request, response);//跳转回index页面
return false;
}
return true; //true:放行,进行跳转;false:不放行
}
}

5.2 修改Controller跳转

  • 为了用户登录后返回一个Session,方便拦截器拦截
1
2
3
4
5
6
7
8
9
10
@RequestMapping("/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){
//具体的业务逻辑判端,瞎写
if ("莱特雷".equals(username) && "123".equals(password)){
session.setAttribute("UserLogin", username);
return "redirect:/main.html";
}
model.addAttribute("msg", "用户名或密码出现错误"); //登录失败,向前端发送消息
return "index";
}

5.3 注册拦截器

  • 在WebMvc配置类中,注入拦截器(方法重写)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

//视图控制器:控制网页跳转
@Override
public void addViewControllers(ViewControllerRegistry registry){
//跳转到首页
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");//隐藏登录后的用户密码
}

//注入地区分解器
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}

//注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/", "/index.html", "/login", "/asserts/**");
//拦截所有请求,但不拦截主页,登录页,以及静态资源
}
}

六、员工管理页面

为了方便管理,将展示员工list的页面放在”emp”文件夹下

6.1 编写Controller跳转

1
2
3
4
5
6
7
8
9
10
11
@Controller
public class EmployeeController {

//查询所有员工信息,封装到Model,并返回前端
@RequestMapping("/emp/getEmps")
public String getEmps(Model model){
Collection<Employee> employees = EmployeeDao.getEmployees();
model.addAttribute("emps", employees);
return "emp/list";
}
}

6.2 修改页面

(1)代码复用

  • 由于dashboard.html 和 list.html 有大量的重复代码(顶部导航栏、侧边栏)
  • 为了方便修改,将重复代码提取,进行统一修改

(2)修改复用代码

6.3 解决高亮问题

点击页面时,总是首先那个按钮会发亮,点击员工管理没发亮

  • (1)lsit页面,main页面修改
\
  • (2)复用代码设置高亮

6.4 员工表单

修改list.html页面,将后端的数据提取出来,并放在一个表单中


七、添加员工功能

7.1 添加按钮

修改list.html页面,增加一个“添加员工”按钮,并跳转Contoller进行业务处理

7.2 添加员工页面

复制list.html进行修改,变成add.html页面,把表内容删除,变成表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<!--表单-->
<form th:action="@{/emp/addEmp}" method="post"> <!--页面跳转-->
<div class="form-group">
<label>LastName</label>
<input type="text" name="lastName" class="form-control" placeholder="莱特雷">
</div>
<div class="form-group">
<label>Email</label>
<input type="text" name="email" class="form-control" placeholder="letere.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="0">
<label class="form-check-label"></label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="1">
<label class="form-check-label"></label>
</div>
</div>
<div class="form-group">
<label>Department</label>
<select class="form-group" name="department.id">
<!--遍历departments集合,并添加value参数,来保证传递会后端的是部门ID-->
<option th:each="dpet:${departments}" th:text="${dpet.getDepartmentName()}" th:value="${dpet.getId()}"></option>
</select>
</div>
<div class="form-group">
<label>Date</label>
<input type="text" name="date" class="form-control" placeholder="2020-12-01">
<!--注意日期格式为SpringBoot配置文件中设置,若没有设置,默认为yy/MM/dd -->
</div>
<button type="submit" class="btn btn-sm btn-primary">添加</button>
</form>
</main>

7.3 Controller创建相应的业务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Controller
public class EmployeeController {

//跳转到添加员工页面
@GetMapping("/emp")
public String toAddEmp(Model model){
Collection<Department> departments = DepartmentDao.getDepartMents();
model.addAttribute("departments", departments); //向前端传递部门名称
return "emp/add";
}

//后端接收数据,添加员工,并返回员工列表主页
@RequestMapping("/emp/addEmp")
public String addEmp(Employee employee){
EmployeeDao.addEmployee(employee, employee.getDepartment().getId());
return "redirect:/emp/getEmps";
}
}
  • 效果图

八、修改员工功能

8.1 修改按钮

修改list.html,添加一个修改员工按钮

  • 效果图

8.2 创建修改页面

复制add.html进行修改

8.3 Controller创建相应业务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Controller
public class EmployeeController {

//跳转到修改员工页面
@RequestMapping("/emp/{id}")
public String toUpdateEmp(@PathVariable("id") Integer id, Model model){ //接受前端传来的员工id
Employee employee = EmployeeDao.getEmployeeById(id);
Collection<Department> departments = DepartmentDao.getDepartMents();

model.addAttribute("emp", employee);
model.addAttribute("departments", departments); //向前端传递员工信息作为默认值
return "emp/update";
}

//后端接受数据,修改员工,并返回员工列表主页
@RequestMapping("/emp/updateEmp")
public String updateEmp(Employee employee){
EmployeeDao.addEmployee(employee, employee.getDepartment().getId());
return "redirect:/emp/getEmps";
}
}
  • 效果图

九、删除员工功能和收尾

9.1 Controller添加删除员工业务

1
2
3
4
5
6
7
8
9
10
11
@Controller
public class EmployeeController {

//删除员工并跳转回员工列表主页
@RequestMapping("/delemp/{id}")
public String deleteEmp(@PathVariable("id") Integer id){
EmployeeDao.delete(id);
return "redirect:/emp/getEmps";
}

}

9.2 添加错误页面

  • 在templates文件夹下创建”error”文件夹,将对应的错误页面改名为错误代码,放进”error”文件夹,thymeleaf会自动跳转,不用填写相应跳转

9.3 添加注销功能

  • (1)修改commen.html页面
  • (2)Controller添加相应业务
1
2
3
4
5
6
7
8
9
10
11
@Controller
public class LoginController {

//注销登录
@RequestMapping("/logout")
public String logout(HttpSession session){
session.invalidate();//销毁Session
return "redirect:index.html";
}

}