SpringBoot项目复盘
项目起始
- 确定数据库中的表、主键、各个字段及其代表的含义;确立好表与表之间的关联关系; (规范好字段的命名规则)**
- 按照数据库中表及其字段创建实体类及其属性;规范好命名规则
- 前端确定好页面长什么样,数据以什么格式交互
- 导入需要的各种依赖,以及做好自定义配置
- 准备好自己的后台模板,开始实现功能
实现各个功能
登录实现
- 在form表单中实现对账号、密码的输入。通过th:action="@{/user/login}"将表单数据提交至controller层中
- 在controller层通过匹配方法上的请求映射为@RequestMapping("/user/login")的方法。
- 在该方法中实现好对password的判断、是否username为空、username输入有误如何提示、数据如何回显至index页面….
@RequestMapping("/user/login") public String login(Model model, HttpSession session, @RequestParam("username") String username , @RequestParam("password") String password){ if(username !=null && "123456".equals(password)){ System.out.println("username= " + username); System.out.println("密码:" + password); //将值(用户名)传进来 session.setAttribute("loginUser", username); return "redirect:/main.html"; }else{ model.addAttribute("msg","用户名或密码错误!"); return "index"; } }
后台实现数据回显的操作
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
如果后端数据判断显示为null或者密码错误。通过上述的语法就可以实现将数据回显出来,而不是一开始就显示数据
其中输入框提示的信息我们使用国际化的技术,能够实现页面的语言自由切换
<form class="form-signin" th:action="@{/user/login}"> <img class="mb-4" src="https://getbootstrap.com/docs/4.0/assets/brand/bootstrap-solid.svg" alt="" width="72" height="72"> <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1> <!--消息回显,登录失败的情况 如果msg的值为空才显示 --> <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p> <label for="inputEmail" class="sr-only">[#{login.username}]</label> <input type="email" id="inputEmail" class="form-control" th:name="username" th:placeholder="#{login.username}" required autofocus> <label for="inputPassword" class="sr-only">[#{login.password}]</label> <input type="password" id="inputPassword" th:name="password" class="form-control" th:placeholder="#{login.password}" required> <div class="checkbox mb-3"> <label> <input type="checkbox" value="remember-me" th:text="#{login.remeber}"> </label> </div> <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.tip}"></button> <p class="mt-5 mb-3 text-muted">© 2022-2023</p> <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a> <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a> </form>
通过<label for="inputEmail" class="sr-only">[#{login.username}]</label>显示提示语。
在配置中开启国际化
- 在application.properties配置文件中,通过spring.messages.basename=i18n.login开启我们配置的国际化配置
- 在自定义配置类中开启国际化方法
//往容器中注入组件 //自定义的国际化组件就会生效 @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); }
1.在config包下创建MyLocaleResolver类实现国际化的接口
public class MyLocaleResolver implements LocaleResolver { //解析请求 @Override public Locale resolveLocale(HttpServletRequest httpServletRequest) { //获取请求的语言参数 String language = httpServletRequest.getParameter("l"); Locale locale = Locale.getDefault(); //如果没有就使用 //如果请求的链接携带了国际化的参数 if(!StringUtils.isEmpty(language)){ //zh_CN String[] split = language.split("_"); //国家 ,地区 locale = new Locale(split[0], split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } }
1.在resources文件夹下创建资源包login.properties、 login_en_US.properties 、 login_zh_CN.properties从而实现国际化的要求
登录开启拦截器
为了防止恶意的跳转,我们需要在用户未登录时自动拦截页面,使其跳转到登录页面,从而防止用户未登录就操作
1.首先在config包下的myConfig类中添加拦截器配置
//添加拦截器配置 @Override public void addInterceptors(InterceptorRegistry registry) { //添加拦截器 //登录拦截器 ,拦截哪些选择 ,排除哪些选择 registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html", "/user/login","/","/css/**","/dist/**","/docs.4.0/**","/js/**","/img/**" ,"/index.html(l='zh_CN')","/index.html(l='en_US')","/user/LogOut"); }
1.在config包下同样创建LoginHandlerInterceptor类,使其实现拦截器接口。从而实现preHandle方法
public class LoginHandlerInterceptor implements HandlerInterceptor { //登录成功之后将用户的信息session传过来 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //登录成功,获取用过户的session Object loginUser = request.getSession().getAttribute("loginUser"); if(loginUser == null) { request.setAttribute("msg", "没有权限,请登录"); request.getRequestDispatcher("/index.html").forward(request, response); return false; } return true; } }
首页实现
<!doctype html > <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <link rel="icon" th:href="@{/docs/4.0/assets/img/favicons/favicon.icof}"> <title>Dashboard Template for Bootstrap</title> <link rel="canonical" href="https://getbootstrap.com/docs/4.0/examples/dashboard/"> <!-- Bootstrap core CSS --> <link th:href="@{/dist/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <link th:href="@{/docs/4.0/examples/dashboard/dashboard.css}" rel="stylesheet"> </head> <body> <!--顶部导航栏--> <div th:insert="~{common/common::topbar}"></div> <!--侧边栏--> <div class="container-fluid"> <div class="row"> <!--侧边栏--> <div th:insert="~{common/common::sidebar(active='main.html')}"></div> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom"> <h1 class="h2">Dashboard</h1> <div class="btn-toolbar mb-2 mb-md-0"> <div class="btn-group mr-2"> <button class="btn btn-sm btn-outline-secondary">Share</button> <button class="btn btn-sm btn-outline-secondary">Export</button> </div> <button class="btn btn-sm btn-outline-secondary dropdown-toggle"> <span data-feather="calendar"></span> This week </button> </div> </div> <canvas class="my-4" id="myChart" width="900" height="380"></canvas> </main> </div> </div> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script>window.jQuery || document.write('<script src="../static/js/vendor/jquery-slim.min.js"></script>')</script> <script src="../static/js/vendor/popper.min.js"></script> <script src="../static/dist/js/bootstrap.min.js"></script> <!-- Icons --> <script src="https://unpkg.com/feather-icons/dist/feather.min.js"></script> <script> feather.replace() </script> <!-- Graphs --> <script src="https://cdn.jsdelivr.net/npm/chart.js@2.7.1/dist/Chart.min.js"></script> <script> var ctx = document.getElementById("myChart"); var myChart = new Chart(ctx, { type: 'line', data: { labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], datasets: [{ data: [15339, 21345, 18483, 24003, 23489, 24092, 12034], lineTension: 0, backgroundColor: 'transparent', borderColor: '#007bff', borderWidth: 4, pointBackgroundColor: '#007bff' }] }, options: { scales: { yAxes: [{ ticks: { beginAtZero: false } }] }, legend: { display: false, } } }); </script> </body> </html>
解释
通过后端登录成功将数据return "redirect:/main.html";重定向main.html,但是main.html这个地址是我们虚构的首页地址,以防止登录成功将用户信息传入导致用户信息泄露。
只需要在自定义配置中配置即可
@Configuration public class MyConfig implements WebMvcConfigurer { /* 配置根目录下的东西可以在这里配置 */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/index.html").setViewName("index"); /*将/main.html作为一个跳板,放置用户信息被揭密,*/ registry.addViewController("/main.html").setViewName("dashboard"); } }
通过registry.addViewController("/main.html").setViewName("dashboard");调用main.html就可以跳转至dashboard首页
list页面实现
list页面作为数据显示的页面,它可以将我们后台传过来的数据通过Thymeleaf语法,将数据显示到后台
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h2>Section title</h2> <a class="btn btn-sm btn-success" th:href="@{/to/add}">添加员工</a> <div class="table-responsive"> <table class="table table-striped table-sm"> <thead> <tr> <th>id</th> <th>lastName</th> <th>Email</th> <th>gender</th> <th>department</th> <th>birth</th> <th>操作</th> </tr> </thead> <tbody> <tr th:each="li : ${list}"> <td th:text="${li.getId()}"></td> <td th:text="${li.getLastName()}"></td> <td th:text="${li.getEmail()}"></td> <td th:text="${li.getGender() == 0 ? '女' : '男'}" ></td> <td th:text="${li.getDepartment().getDepartmentName()}"></td> <td th:text="${#dates.format(li.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td> <td> <a class="btn btn-sm btn-primary" th:href="@{'/updateEmp/'+${li.getId()}}">修改</a> <a class="btn btn-sm btn-danger" th:href="@{'/deleteEmp/'+${li.getId()}}">删除</a> </td> </tr> </tbody> </table> </div> </main>
后端通过用户点击员工管理
<li class="nav-item"> <a th:class="${active=='list.html' ? 'nav-link active' :'nav-link'}" th:href="@{/list}"> <span data-feather="users"></span> 员工管理 </a> </li>
,请求就会跳转至controller匹配相应的请求映射
@Autowired private EmployeeDao employeeDao; @Autowired private DepartmentDao departmentDao; @RequestMapping("/list") public String list(Model model){ Collection<Employee> list = employeeDao.getAllEmployee(); model.addAttribute("list",list); return "list"; }
后端通过Model将数据回显给list页面
在list页面。通过Thymeleaf语法<tr th:each="li : ${list}">将数据显示出来
<td th:text="${li.getId()}"></td> <td th:text="${li.getLastName()}"></td> <td th:text="${li.getEmail()}"></td> <td th:text="${li.getGender() == 0 ? '女' : '男'}" ></td> <td th:text="${li.getDepartment().getDepartmentName()}"></td> <td th:text="${#dates.format(li.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td> <td> <a class="btn btn-sm btn-primary" th:href="@{'/updateEmp/'+${li.getId()}}">修改</a> <a class="btn btn-sm btn-danger" th:href="@{'/deleteEmp/'+${li.getId()}}">删除</a> </td> </tr>
这样,我们请求就能转发过来,用户就能看到数据
同时在list页面,我们也会有实现curd的跳转键
实现delete 和 update功能
对于delete功能,这里没什么好说的,仅仅通过点击用户后面对应的标签,将要删除的员工的id作为请求参数传到后端即可th:href="@{'/updateEmp/'+${li.getId()}}"后端通过匹配请求映射将功能实现
@RequestMapping("/deleteEmp/{id}") public String deleteEmp(@PathVariable("id")Integer id){ employeeDao.deleteEmp(id); return "redirect:/list"; }
实现update功能相较于delete功能是比较麻烦的
同样需要将要修改的员工id作为请求参数传入后端th:href="@{'/updateEmp/'+${li.getId()}}",然后后端通过查询,获取该员工的所有信息,然后通过Model将数据共享至update页面
@RequestMapping("/updateEmp/{id}") public String update(Model model,@PathVariable("id") Integer id){ System.out.println("id==" + id); Employee employee = employeeDao.getEmployeeById(id); System.out.println("emp == " + employee); model.addAttribute("emp",employee); Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("departments",departments); return "/updateEmp"; }
在updateEmp页面,需要将数据回显出来
<!--表单提交--> <form class="form-horizontal" role="form" th:action="@{/updateSucc}" method="post" > <input type="hidden" name="id" th:value="${emp.getId()}"> <div class="form-group"> <label for="lastName" class="col-sm-2 control-label">名字</label> <div class="col-sm-10"> <input th:value="${emp.getLastName()}" type="text" class="form-control" id="lastName" name="lastName" placeholder="请输入名字"> </div> </div> <div class="form-group"> <label for="lastname" class="col-sm-2 control-label">Email</label> <div class="col-sm-10"> <input th:value="${emp.getEmail()}" type="text" class="form-control" id="Email" name="Email" placeholder="请输入邮箱"> </div> </div> <div class="form-group"> <label for="gender">性别</label> <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()==0}" type="radio" class="form-check-input" 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: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"> <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" id="birth" name="birth" placeholder="请输入生日"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <div class="checkbox"> <label> <input type="checkbox"> 请记住我 </label> </div> </div> </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>
1.通过隐藏域<input type="hidden" name="id" th:value="${emp.getId()}">可以将后端传入的信息获取到
2.因为我们共享数据时是通过model.addAttribute("emp",employee);,所以在获取,然后在<input th:value="${emp.getLastName()}" type="text" class="form-control" id="lastName" name="lastName" placeholder="请输入名字">中通过th:value="${emp.getLastName()}"就可以将数据显示到页面
3.对于复杂的比如department
<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>
可以先通过deparment.id获取全部的信息,然后通过匹配emp.getDepartment().getId()获取将表单展示出来。通过th:value="${dept.getId()}"将我们需要的数据显示到页面
4.然后通过请求form表单的th:action="@{/updateSucc}"提交表单至后端.
5.后端通过请求映射匹配到相应的请求方法
@PostMapping("/updateSucc") public String updateSuccess(Employee employee){ employeeDao.save(employee); return "redirect:/list"; }
然后进行修改,最后通过重定向将请求跳转至原来的页面
add功能实现
用户通过点击添加员工将请求发送到后端,后端通过请求映射匹配到相应的请求方法,实现添加功能
<a class="btn btn-sm btn-success" th:href="@{/to/add}">添加员工</a> @GetMapping("/to/add") public String addEmp(Model model){ Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("departments",departments); return "addEmp"; }
后台页面与update页面类似