SpringBoot入门篇 03、员工管理系统(基于内存)(二)

简介: SpringBoot入门篇 03、员工管理系统(基于内存)(二)

五、功能实现


1、登陆功能


简单实现

前提描述


实现输入用户名及密码来进行跳转页面,若是用户名不为空及密码正确进行跳转页面,否则回到初始页面并显示错误信息!!!


核心部分


html中使用到thymleaf中的几个表达式


表单url:th:action="@{/user/login}"

接收请求域中参数:th:text="${msg}"

使用其自带工具类strings(不为空即显示):th:if="#!strings.isEmpty(msg)"

LoginController:


@Controller
public class LoginController {
    @RequestMapping("/user/login")
    public String hello(@RequestParam("email")String email,
                        @RequestParam("password")String password,
                        Model model) {
        //判断是否接收到用户名及密码是否正确
        if(!StringUtils.isEmpty(email) && "123456".equals(password)){
            return "redirect:/main.html";
        }else{
            model.addAttribute("msg","用户名或密码有错误!");
            return "index";
        }
    }
}



注意唯一要说明的是用户名不为空以及密码正确进行的是重定向,改main.html是不存在的,通过自定义配置类中设置跳转路径进行跳转。通过这种形式可以给出一种感觉提交表单是跳转到main.html页面


配置类:


@Configuration
public class MyMvcConfig implements  WebMvcConfigurer{
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
      ...
        //以这种形式来进行跳转
        registry.addViewController("/main.html").setViewName("dashboard");
    }
    //自定义的国际化组件就生效了!自动注入到spring容器中
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}


效果:


成功跳转情况:




失败的跳转情况:



添加拦截器

拦截器添加的作用是什么呢?


我们在上面进行简单实现登录,通过简单校验中账号密码,如通过跳转路径/main.html则会直接跳转到/dashboard.html了。中间会经过一个main.html作为跳板(并不是一个真实页面)。此时就有个问题,若是直接访问main.html不就直接跳转过去到登录后的界面了嘛。




解决措施:通过添加拦截器进行拦截,若是直接访问这个main.html,则要进行session校验,若存在直接放行,不存在拦截


首先做一个前提条件:在登陆成功跳转之时创建session并传给浏览器。


//只需要在对应控制器中添加HttpSession参数,并执行方法生成session即可
session.setAttribute("loginUser",email);


自定义拦截器类:


public class MyInterceptor implements HandlerInterceptor {
    //访问指定请求前进行拦截判断,return true放行,return false不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object loginUser = request.getSession().getAttribute("loginUser");
        if(loginUser!=null){//若是有该session,通过
            return true;
        }else{
            request.setAttribute("msg","没有权限,无法登录");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }
    }
}


自定义拦截器生成之后我们则回到自定义mvc配置类中重写方法:


@Configuration
public class MyMvcConfig implements  WebMvcConfigurer{
    ...
  @Override
    public void addInterceptors(InterceptorRegistry registry) {
     //使用registry来添加刚刚自定义的拦截器,并且添加拦截路径(addPathPatterns),不拦截的资源(excludePathPatterns)
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/index.html","/","/user/login","/css/**","/image/**","/js/**");
    }
}


由于上面登陆采用跳板形式,所以仅仅不拦截/user/login即可,拦截main.html时进行拦截校验!!!


效果:



2、展示员工列表


首先说明这部分需要完成的内容


将头部部分、侧边栏部分进行组件化,单独抽取一个页面(通过使用th:flagment="①"以及th:replace="~(组件页面前缀::①)"来获取组件)

跳转页面侧边栏对应部分高亮(通过在th:replace中添加请求参数,在组件部分中使用三元运算符中处理高亮)

编写一个controller来返回数据到页面中,应当跳转视图到list.html

使用th:each来遍历列表内容,并使用${单个实体}来展示数据,其中数字表示性别使用三元运算符解决,其中的日期使用thymeleaf模板中的例如${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')

组件化思路:


    抽取对应重复使用的单独模块到公共页面(该页面通常放置到commons目录中),指定模块使用th:fragment="headBar"表示,在其他页面如图表页面、展示列表页面使用组件时,例如:th:replace="~{/commons/commons::sideBarMenu}"即可使用组件!!!


标签高亮的思路:


    对于bootstap高亮只需要在其class中添加active即可产生高亮。基于上面组件化,我们在如th:replace="...(key=value)"中添加请求参数,紧接着在公共页面的指定标签如a标签部分的th:class其中进行运算符判断是否有改参数,有的话高亮,无的话不高亮。


Controller部分:


@Controller
public class EmployeeController {
    @Autowired
    private EmployeeDao employeeDao;//这里为了简便直接调用dao
    @RequestMapping("/employee/emps")
    public String queryAll(Model model){
        Collection<Employee> employees = employeeDao.queryAllEmployee();//该方法只是通过获取模拟数据来进行返回
        model.addAttribute("emps",employees);
        return "list";
    }
}


页面遍历列表:


<tr th:each="emp:${emps}" >
    <td th:text="${emp.getId()}"></td>
    <td th:text="${emp.getLastName()}"></td>
    <td th:text="${emp.getEmail()}"></td>
    <!-- 三元运算符 -->
    <td th:text="${emp.getGender()}==0?'男':'女'"></td>
    <td th:text="${emp.getDepartment().getDepartmentName()}"></td>
    <!-- 使用自带的dates类进行格式化 -->
    <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
    <td>
        <button class="btn btn-sm btn-primary">编辑</button>
        <button class="btn btn-sm btn-danger">删除</button>
    </td>
</tr>


效果:




3、添加员工


首先大致介绍流程


需要创建一个新的添加页面add.html,并且在list.html基础上添加一个按钮用来跳转页面

整体是需要两个controller的,第一个是用来进行跳转到add.html,其中需要controller原因是添加页面中需要选择部门,其中的遍历所有部门需要在controller中查询并返回到add.html。

第二个controller是为了填写完add.html页面时,再次点击提交,controller中会执行保存该员工的信息到虚拟map数据结构中,进行重定向到员工列表展示页面(也经过了一个controller重新查询并跳转)。

list.html中的添加按钮:


<!-- 添加一个添加成员按钮-->
<div>
    <a th:href="@{/empadd}" class="btn btn-sm btn-success">添加</a>
</div>


add.html中的form表单:


<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
    <!-- 添加一个表单 -->
    <form style="margin-top: 10px" method="post" th:action="@{/empadd}">
        <!-- 账号邮箱 -->
        <div class="mb-3">
            <label for="exampleInputEmail1" class="form-label">LastName</label>
            <input th:name="lastName" type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
        </div>
        <div class="mb-3">
            <label for="exampleInputPassword1" class="form-label">Email</label>
            <input th:name="email" type="email" class="form-control" id="exampleInputPassword1">
        </div>
        <!-- 单选框 -->
        <div class="mb-3">
            <label for="exampleInputPassword1" class="form-label">Gender</label><br>
            <div class="form-check form-check-inline">
                <input th:name="gender" class="form-check-input" type="radio" id="inlineCheckbox1"  value="0">
                <label class="form-check-label" for="inlineCheckbox1">男</label>
            </div>
            <div class="form-check form-check-inline">
                <input th:name="gender" class="form-check-input" type="radio" id="inlineCheckbox2"  value="1">
                <label class="form-check-label" for="inlineCheckbox2">女</label>
            </div>
        </div>
        <!-- 选择框 -->
        <div class="mb-3">
            <label for="exampleInputPassword1" class="form-label">department</label><br>
            <select class="form-select" aria-label="Default select example" th:name="department.id">
                <!-- 进行遍历所有部门 -->
                <option th:each="dep:${deps}" th:text="${dep.getDepartmentName()}" th:value="${dep.getId()}">Open this select menu</option>
            </select>
        </div>
        <!-- 生日日期-->
        <div class="mb-3">
            <label for="exampleInputPassword1" class="form-label">Birth</label><br>
            <input th:name="birth" type="text" class="form-control" aria-describedby="emailHelp">
        </div>
        <button type="submit" class="btn btn-primary">提交</button>
    </form>



两个controller:控制跳转页面及重定向(保存employee)


@Controller
public class EmployeeController {
    @Autowired
    private EmployeeDao employeeDao;
    @Autowired
    private DepartmentDao departmentDao;
    ...
    //跳转到添加页面
    @GetMapping("/empadd")
    public String toAddPage(Model model){
        //查询部门信息,并保存到request域中,跳转视图
        Collection<Department> departments = departmentDao.queryAllDepartment();
        model.addAttribute("deps",departments);
        return "emp/add";
    }
    //执行添加员工操作,并且重新加载并展示所有员工信息
    @PostMapping("/empadd")
    public String addEmployee(Employee employee){
        employeeDao.addEmployee(employee);
        System.out.println("employee=>"+employeeDao);
        //重定向
        return "redirect:/emps";
    }
}


效果:



额外注意点


若是提交表单中,对于日期格式是很严格的,springboot中默认为yyyy/MM/dd,若是填写了如1998-07-20就会报错


解决方案:


yaml配置文件中进行配置指定的日期格式即可


# 提交表单中默认日期格式
spring.mvc.format.date=yyyy-MM-dd



4、修改员工


前提描述


在list.html页面中点击指定员工的编辑按钮跳转到update.html中,其中经过一个controller,该controller根据请求参数id来查询到指定员工的信息以及查询所有的部门信息,返回到update.html中

根据传来的员工信息及部门信息,预先填写好指定的表单,点击修改按钮前,注意添加一个隐藏域用来传递员工的id

因为我们模拟数据的是HashMap,添加员工使用put方法,若是key不变,重复put达到一个覆盖效果,也就是更新的效果,秒妙妙!!!,我们复用一个controller中的添加方法即可!!!

list.html中的编辑按钮:rest风格


<!-- rest风格请求,例如:http://localhost:8080/changlu/emp/101 -->
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">编辑</a>


Controller编写:


@Controller
public class EmployeeController {
    @Autowired
    private EmployeeDao employeeDao;
    @Autowired
    private DepartmentDao departmentDao;
    ...
    //跳转update页面 rest风格,获取id值
    @GetMapping("/emp/{id}")
    public String toUpdatePage(@PathVariable("id")Integer id,Model model){
        //根据参数id查询指定的员工信息
        Employee employee = employeeDao.queryEmployeeById(id);
        model.addAttribute("emp",employee);
        //查询所有的部门信息,之后需要遍历部门的名称
        Collection<Department> departments = departmentDao.queryAllDepartment();
        model.addAttribute("deps",departments);
        return "emp/update";
    }
}



update.html:添加一个隐藏域以及实现自动填写表单


<!-- 添加一个隐藏域,用于传输id, -->
<input type="hidden" name="id" th:value="${emp.getId()}">
 <!-- 覆盖lastName -->
<input th:value="${emp.getLastName()}" th:name="lastName" ...>
 <!-- 覆盖单选框,使用到判断 -->
<input th:checked="${emp.getGender()==0}" th:name="gender"
 <!-- 覆盖选择框,使用到判断 -->
<select class="form-select" aria-label="Default select example" th:name="department.id">
  <option th:selected="${emp.getDepartment().getId()==dep.getId()}" th:each="dep:${deps}" ...>
 <!-- 覆盖日期框,使用到判断 -->
<input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" th:name="birth" ...>


效果:



5、删除员工


前提描述


在list.html中添加一个按钮即可,使用jquery来添加一个确定事件

发送请求到controller进行删除即可,最后重定向到列表展示页面

list.html


<!-- 绑定弹窗事件,用于删除的确认与否 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script>
    $(function () {
        $("a[class='btn btn-sm btn-danger']").click(function () {
            return confirm("确定删除吗?");
        });
    });
</script>
<!-- 同样是rest风格进行请求 -->
<a class="btn btn-sm btn-danger" th:href="@{/delemp/}+${emp.getId()}">删除</a>


Controller类:


@Controller
public class EmployeeController {
    @Autowired
    private EmployeeDao employeeDao;
    @Autowired
    private DepartmentDao departmentDao;
    ...
    //删除员工
    @GetMapping("/delemp/{id}")
    public String delEmp(@PathVariable("id") Integer id){
        employeeDao.deleteEmployeeById(id);
        //重定向到展示列表controller并跳转展示页
        return "redirect:/emps";
    }
}



效果:



错误页

在springboot中,我们可以将错误页放置到template目录中的error目录里,当访问出现对应异常时会跳转到指定错误页面!!!



效果:



注销登录

前面我们在登录成功时生成session,后面又使用了拦截器对部分页面进行拦截。那么我们再添加一个注销的功能


/template/commons/commons.html
<!-- 这是headBar部分的标签 -->
<a class="nav-link" th:href="@{/logout}">Sign out</a>



loginController:使用invalidate()立即销毁session即可


@RequestMapping("/logout")
public String logout(HttpSession session){
    //立即销毁session
    session.invalidate();
    return "redirect:/index.html";
}


效果:点击的是右上角的sign out



注意点

controller中重定向如:return “redirect:/emps” :后需要有/,会自动调整虚拟目录的名称(根据template的目录跳转而不是请求)

若只是普通跳转视图层:return "emps" ,不需要加/

href的话,thymeleaf中th:href="@{/emps}",加不加/都没关系,自动定位到虚拟目录后



ModelAndView视图使用setViewName("error/error")设置。

相关文章
|
1月前
|
Java 数据库连接 测试技术
SpringBoot入门 - 添加内存数据库H2
SpringBoot入门 - 添加内存数据库H2
60 3
SpringBoot入门 - 添加内存数据库H2
|
1月前
|
缓存 Java Linux
如何解决 Linux 系统中内存使用量耗尽的问题?
如何解决 Linux 系统中内存使用量耗尽的问题?
140 48
|
7天前
|
运维 监控 Java
为何内存不够用?微服务改造启动多个Spring Boot的陷阱与解决方案
本文记录并复盘了生产环境中Spring Boot应用内存占用过高的问题及解决过程。系统上线初期运行正常,但随着业务量上升,多个Spring Boot应用共占用了64G内存中的大部分,导致应用假死。通过jps和jmap工具排查发现,原因是运维人员未设置JVM参数,导致默认配置下每个应用占用近12G内存。最终通过调整JVM参数、优化堆内存大小等措施解决了问题。建议在生产环境中合理设置JVM参数,避免资源浪费和性能问题。
32 3
|
22天前
|
机器学习/深度学习 人工智能 缓存
【AI系统】推理内存布局
本文介绍了CPU和GPU的基础内存知识,NCHWX内存排布格式,以及MNN推理引擎如何通过数据内存重新排布进行内核优化,特别是针对WinoGrad卷积计算的优化方法,通过NC4HW4数据格式重排,有效利用了SIMD指令集特性,减少了cache miss,提高了计算效率。
39 3
|
25天前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
26天前
|
机器学习/深度学习 人工智能 算法
【AI系统】内存分配算法
本文探讨了AI编译器前端优化中的内存分配问题,涵盖模型与硬件内存的发展、内存划分及其优化算法。文章首先分析了神经网络模型对NPU内存需求的增长趋势,随后详细介绍了静态与动态内存的概念及其实现方式,最后重点讨论了几种节省内存的算法,如空间换内存、计算换内存、模型压缩和内存复用等,旨在提高内存使用效率,减少碎片化,提升模型训练和推理的性能。
45 1
|
1月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
74 16
|
1月前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
1月前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
70 13
|
1月前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
52 4