SpringBoot项目复盘

简介: SpringBoot项目复盘

SpringBoot项目复盘



项目起始


  1. 确定数据库中的表、主键、各个字段及其代表的含义;确立好表与表之间的关联关系; (规范好字段的命名规则)**
  2. 按照数据库中表及其字段创建实体类及其属性;规范好命名规则
  3. 前端确定好页面长什么样,数据以什么格式交互
  4. 导入需要的各种依赖,以及做好自定义配置
  5. 准备好自己的后台模板,开始实现功能


实现各个功能


登录实现


  1. 在form表单中实现对账号、密码的输入。通过th:action="@{/user/login}"将表单数据提交至controller层中
  2. 在controller层通过匹配方法上的请求映射为@RequestMapping("/user/login")的方法。
  3. 在该方法中实现好对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">&copy; 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从而实现国际化的要求


springBoot001.png


登录开启拦截器


为了防止恶意的跳转,我们需要在用户未登录时自动拦截页面,使其跳转到登录页面,从而防止用户未登录就操作


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页面类似


目录
相关文章
|
2月前
|
前端开发 搜索推荐 Java
【Spring Boot】深度复盘在开发搜索引擎项目中重难点的整理,以及遇到的困难和总结
【Spring Boot】深度复盘在开发搜索引擎项目中重难点的整理,以及遇到的困难和总结
17 0
|
Java jenkins 应用服务中间件
价值不言而喻,SSM项目升级springBoot复盘,又是一个极小的细节
价值不言而喻,SSM项目升级springBoot复盘,又是一个极小的细节
102 0
价值不言而喻,SSM项目升级springBoot复盘,又是一个极小的细节
|
Java 应用服务中间件 文件存储
springboot创建文件无法读取问题复盘
springboot创建文件无法读取问题复盘
220 0
|
5天前
|
SQL JavaScript 前端开发
vue中使用分页组件、将从数据库中查询出来的数据分页展示(前后端分离SpringBoot+Vue)
这篇文章详细介绍了如何在Vue.js中使用分页组件展示从数据库查询出来的数据,包括前端Vue页面的表格和分页组件代码,以及后端SpringBoot的控制层和SQL查询语句。
vue中使用分页组件、将从数据库中查询出来的数据分页展示(前后端分离SpringBoot+Vue)
|
1天前
|
JavaScript Java Maven
毕设项目&课程设计&毕设项目:springboot+vue实现的在线求职管理平台(含教程&源码&数据库数据)
本文介绍了一款基于Spring Boot和Vue.js实现的在线求职平台。该平台采用了前后端分离的架构,使用Spring Boot作为后端服务
毕设项目&课程设计&毕设项目:springboot+vue实现的在线求职管理平台(含教程&源码&数据库数据)
|
1天前
|
Java 微服务 Spring
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
文章介绍了如何利用Spring Cloud Alibaba快速构建大型电商系统的分布式微服务,包括服务限流降级等主要功能的实现,并通过注解和配置简化了Spring Cloud应用的接入和搭建过程。
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
|
1天前
|
NoSQL JavaScript 前端开发
SpringBoot+Vue实现校园二手系统。前后端分离技术【完整功能介绍+实现详情+源码】
文章介绍了如何使用SpringBoot和Vue实现一个校园二手系统,采用前后端分离技术。系统具备完整的功能,包括客户端和管理员端的界面设计、个人信息管理、商品浏览和交易、订单处理、公告发布等。技术栈包括Vue框架、ElementUI、SpringBoot、Mybatis-plus和Redis。文章还提供了部分源代码,展示了前后端的请求接口和Redis验证码功能实现,以及系统重构和模块化设计的一些思考。
SpringBoot+Vue实现校园二手系统。前后端分离技术【完整功能介绍+实现详情+源码】
|
4天前
|
前端开发 JavaScript Java
SpringBoot+Vue+token实现(表单+图片)上传、图片地址保存到数据库。上传图片保存位置自己定义、图片可以在前端回显(一))
这篇文章详细介绍了在SpringBoot+Vue项目中实现表单和图片上传的完整流程,包括前端上传、后端接口处理、数据库保存图片路径,以及前端图片回显的方法,同时探讨了图片资源映射、token验证、过滤器配置等相关问题。
|
4天前
|
前端开发 数据库
SpringBoot+Vue+token实现(表单+图片)上传、图片地址保存到数据库。上传图片保存位置到项目中的静态资源下、图片可以在前端回显(二))
这篇文章是关于如何在SpringBoot+Vue+token的环境下实现表单和图片上传的优化篇,主要改进是将图片保存位置从磁盘指定位置改为项目中的静态资源目录,使得图片资源可以跨环境访问,并在前端正确回显。
|
4天前
|
前端开发 数据库
SpringBoot+Vue实现商品不能重复加入购物车、购物车中展示商品的信息、删除商品重点提示等操作。如何点击图片实现图片放大
这篇文章介绍了如何在SpringBoot+Vue框架下实现购物车功能,包括防止商品重复加入、展示商品信息、删除商品时的提示,以及点击图片放大的前端实现。
SpringBoot+Vue实现商品不能重复加入购物车、购物车中展示商品的信息、删除商品重点提示等操作。如何点击图片实现图片放大