• SpringBoot 学习(三)Web 开发


    3. SpringBoot Web 开发

    3.1 导入静态资源

    (1) webjars

    • 导入 jquery 依赖

      <dependency>
          <groupId>org.webjarsgroupId>
          <artifactId>jqueryartifactId>
          <version>3.6.0version>
      dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • 访问 jquery.js 文件

      http://localhost:8080/webjars/jquery/3.6.0/jquery.js

    (2) WebProperties ( SpringBoot 3.5.4 )

    • 静态资源默认路径(访问优先级由高到低排序)

      classpath:/META-INF/resources/
      classpath:/resources/
      classpath:/static/
      classpath:/public/
      
      • 1
      • 2
      • 3
      • 4

      一般 public 目录存放公共资源,如各模块需要调用的 js;static 目录存放静态资源,如图片;

      resources 目录存放上传的文件。

    3.2 配置首页

    • 将 index.html 文件放在静态资源默认路径下,一般放在 static 目录下。
    • 标签页图标 favicon.ico 放在 static 目录下。

    3.3 模板引擎

    3.3.1 配置模板引擎 Thymeleaf

    (1) 导入依赖
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-thymeleafartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    (2) 将 html 文件放在对应目录下
    public static final String DEFAULT_PREFIX = "classpath:/templates/";
    public static final String DEFAULT_SUFFIX = ".html";
    
    • 1
    • 2
    (3) html 文件引入约束
    <html lang="en" xmlns:th="http://www.thymeleaf.org/">
    
    • 1

    3.3.2 Thymeleaf 语法

    3.4 扩展 SpringMVC

    • config 目录下自定义配置类

      // 扩展 SpringMvc,不能加 @EnableWebMvc
      @Configuration
      public class MyMvcConfig implements WebMvcConfigurer {
          @Bean
          public ViewResolver myViewResolver() {
              return new MyViewResolver();
          }
          // 自定义一个视图解析器
          public static class MyViewResolver implements ViewResolver {
              @Override
              public View resolveViewName(String viewName, Locale locale) throws Exception {
                  return null;
              }
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

    3.5 国际化

    (1) 配置 i18n 文件

    (2) 自定义组件

    • 添加 html 国际化按钮

      在这里插入图片描述

    • 编写组件

      public class MyLocaleResover implements LocaleResolver {
      
          // 解析请求
          @Override
          public Locale resolveLocale(HttpServletRequest request) {
              // 获取请求中的语言参数
              String language = request.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 request, HttpServletResponse response, Locale locale) {
      
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22

    (3) 将组件注册到 Spring 中

    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("index");
            registry.addViewController("/index.html").setViewName("index");
        }
    
        // 国际化组件注册到Spring
        @Bean
        public LocaleResolver localeResolver() {
            return new MyLocaleResover();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    (4) 添加配置

    # application.properties
    # 配置文件位置
    spring.messages.basename=i18n.login
    
    • 1
    • 2
    • 3

    3.6 登录实现

    (1) 设置 html 输入框 name

    <input type="text" class="form-control" name="username" th:placeholder="#{login.username}" required="" autofocus="">
    <input type="password" class="form-control" name="password" th:placeholder="#{login.password}" required="">
    
    • 1
    • 2

    (2) 设置表单提交路径

    <form class="form-signin" th:action="@{/user/login}">
    
    • 1

    (3) 编写登录控制器

    @Controller
    public class LoginController {
    
        @RequestMapping("/user/login")
        public String login(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            Model model, HttpSession session) {
            // 用户名不为空,密码为 123456
            if (!StringUtils.isEmpty(username) && "123456".equals(password)) {
                // 向 session 传入登录标识
                session.setAttribute("loginUser", username);
                // 重定向到面板
                return "redirect:/main.html";
            } else {
                // 向 msg 传入信息
                model.addAttribute("msg", "用户名或密码错误");
                return "index";
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    (4) 编写登录拦截器

    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) {
                // 向 msg 传入信息
                request.setAttribute("msg", "没有权限");
                // 跳转到 index
                request.getRequestDispatcher("/index.html").forward(request, response);
                return false;
            } else {
                return true;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    (5) 添加登录拦截器

    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            // / 可访问到 index.html
            registry.addViewController("/").setViewName("index");
            // index.html 可访问到 index
            registry.addViewController("/index.html").setViewName("index");
            // /main.html 可访问到 dashboard
            registry.addViewController("/main.html").setViewName("dashboard");
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 添加登录拦截器
            registry.addInterceptor(new LoginHandlerInterceptor())
                    // 设置拦截对象
                    .addPathPatterns("/**")
                    // 排除拦截对象
                    .excludePathPatterns("/index.html", "/", "/user/login","/css/**", "/js/**", "/img/**");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.7 列表展示

    3.7.1 提取公共页面

    (1) 提取代码
    
    <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
        ...
    nav>
    <nav class="col-md-2 d-none d-md-block bg-light sidebar"  th:fragment="sidebar">
        ...
    nav>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    (2) 插入代码
    
    <div th:replace="~{commons/commons::topbar}">div>
    
    <div th:replace="~{commons/commons::sidebar(active='main.html')}">div>
    
    • 1
    • 2
    • 3
    • 4
    
    <div th:insert="~{commons/commons::topbar}">div>
    
    <div th:insert="~{commons/commons::sidebar(active='list.html')}">div>
    
    • 1
    • 2
    • 3
    • 4
    (3) 接收参数
    <a th:class="${active == 'main.html'?'nav-link active':'nav-link'}" th:href="@{/index.html}">
        ...
    a>
    <a th:class="${active == 'list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">
        ...
    a>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.7.3 渲染列表

    (1) 控制器获取数据
    @Controller
    public class EmployeeController {
    
        @Autowired
        EmployeeDao employeeDao;
        
        @Autowired
        DepartmentDao departmentDao;
        
        @RequestMapping("/emps")
        public String list(Model model) {
            Collection<Employee> employees = employeeDao.getAll();
            model.addAttribute("emps", employees);
            return "emp/list";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    (2) 渲染列表
    <table class="table table-striped table-sm">
       <thead>
          <tr>
             <th>idth>
             <th>lastNameth>
             <th>emailth>
             <th>genderth>
             <th>departmentth>
             <th>birthth>
             <th>操作th>
          tr>
       thead>
       <tbody>
          <tr th:each="emp:${emps}">
             <td th:text="${emp.getId()}"/>
             <td th:text="${emp.getLastName()}"/>
             <td th:text="${emp.getEmail()}"/>
             <td th:text="${emp.getGender()==0?'女':'男'}"/>
             <td th:text="${emp.getDepartment().departmentName}"/>
             <td th:text="${#dates.format(emp.getBirth(),'YYYY-MM-DD HH:mm:ss')}"/>
             <td>
                <button class="btn btn-sm btn-primary">编辑button>
                <button class="btn btn-sm btn-danger">删除button>
             td>
          tr>
       tbody>
    table>
    
    • 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

    3.8 添加记录

    (1) 添加界面

    <form th:action="@{/emp}" method="post">
       <div class="form-group">
          <label>LastNamelabel>
          <input type="text" name="lastName" class="form-control" placeholder="why">
       div>
       <div class="form-group">
          <label>Emaillabel>
          <input type="text" name="email" class="form-control" placeholder="123456@qq.com">
       div>
       <div class="form-group">
          <label>Genderlabel>
          <div class="form-check form-check-inline">
             <input type="radio" class="form-check-input" name="gender" value="0">
             <label class="form-check-label">label>
          div>
          <div class="form-check form-check-inline">
             <input type="radio" class="form-check-input" name="gender" value="1">
             <label class="form-check-label">label>
          div>
       div>
       <div class="form-group">
          <label>Departmentlabel>
          <select class="form-control" name="department.id">
             <option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}">option>
          select>
       div>
       <div class="form-group">
          <label>Birthlabel>
          <input type="text" name="birth" class="form-control" placeholder="2021-01-01">
       div>
       <button type="submit" class="btn btn-primary">添加button>
    form>
    form>
    
    • 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
    • 注意 HTML 元素名称和实体属性名称的对应

    (2) 编写控制器

    @Controller
    public class EmployeeController {
    
        @Autowired
        EmployeeDao employeeDao;
    
        @Autowired
        DepartmentDao departmentDao;
    
        @GetMapping("/emp")
        public String toAddPage(Model model) {
            // 获取部门数据
            Collection<Department> departments = departmentDao.getDepartments();
            model.addAttribute("departments", departments);
            return "emp/add";
        }
    
        @PostMapping("/emp")
        public String addEmp(Employee employee) {
            System.out.println("receive_emp ==>" + employee);
            // 保存员工数据
            employeeDao.save(employee);
            return "redirect:/emps";
        }
    
    }
    
    • 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

    3.9 修改记录

    (1) 编写请求链接

    
    <a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">编辑a>
    
    • 1
    • 2

    (2) 编写页面跳转控制器

    // EmployeeController.java
    @GetMapping("/emp/{id}")
    public String toUpdateEmp(@PathVariable("id") Integer id, Model model) {
        // 获取员工数据
        Employee employee = employeeDao.getEmployeeById(id);
        model.addAttribute("emp", employee);
    
        // 获取部门数据
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
    
        return "emp/update";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (3) 添加修改界面

    
    <form th:action="@{/updateEmp}" method="post">
        
       <input type="hidden" name="id" th:value="${emp.getId()}">
       <div class="form-group">
          <label>LastNamelabel>
          <input th:value="${emp.getLastName()}" type="text" name="lastName" class="form-control" placeholder="why">
       div>
       <div class="form-group">
          <label>Emaillabel>
          <input th:value="${emp.getEmail()}" type="text" name="email" class="form-control" placeholder="123456@qq.com">
       div>
       <div class="form-group">
          <label>Gender label>
          <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 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>
       <div class="form-group">
          <label>Departmentlabel>
          <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>Birthlabel>
           
          <input th:value="${#dates.format(emp.getBirth(), 'yyyy-MM-dd HH:mm')}" type="text" name="birth" class="form-control" placeholder="2021-01-01">
       div>
       <button type="submit" class="btn btn-primary">修改button>
    form>
    
    • 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

    (4) 编写修改员工控制器

    @PostMapping("/updateEmp")
    public String updateEmp(Employee employee) {
        employeeDao.save(employee);
        return "redirect:/emps";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.10 删除记录

    (1) 编写请求链接

    
    <a class="btn btn-sm btn-danger" th:href="@{/deleteEmp/}+${emp.getId()}">删除a>
    
    • 1
    • 2

    (2) 编写删除控制器

    @GetMapping("/deleteEmp/{id}")
    public String deleteEmp(@PathVariable("id") Integer id) {
        employeeDao.deleteEmp(id);
        return "redirect:/emps";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.11 错误页面和注销

    (1) 错误页面

    • templates 下添加 error 路径

    • 将错误页面以错误代码命名放入 error 目录下

    (1) 注销

    ● 编写链接
    
    <a class="nav-link" th:href="@{/user/logout}">注销a>
    
    • 1
    • 2
    ● 编写控制器
    // LoginController.java
    @RequestMapping("/user/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "redirect:/index.html";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

  • 相关阅读:
    python实现鼠标实时坐标监测
    Nacos注册中心的部署与用法详细介绍
    习题1.23
    Flink的算子列表状态的使用
    Kafka消费组无法消费问题排查实战
    时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测
    第2次作业
    力扣每日一题2022-09-07简单题:重新排列单词间的空格
    Obsidian 国内插件安装指北
    C#:实现分枝绑定背包求解器算法(附完整源码)
  • 原文地址:https://blog.csdn.net/qq_42651415/article/details/133272229