SpringBoot学习笔记

SpringBoot

  • 基于Spring开发,本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。
  • 核心思想:约定大于配置
  • 集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz等等)

微服务架构

单体应用架构

  • 将应用中的所有应用服务都封装在一个应用中
  • 优点
    • 易于开发和测试
    • 方便部署
    • 需要扩展时,只需要将war复制多份,然后放到多个服务器上,再做个负载均衡即可
  • 缺点
    • 修改时需要停止整个服务,重新打包部署
  • 将每个功能元素独立出来,并将独立出来的功能元素动态组合。只对功能元素进行复制
  • 优点
    • 节省了调用资源
    • 每个功能元素的服务都是一个可替换的、可独立升级的软件代码

微服务架构

第一个SpringBoot程序

快速生成网站:Spring Initializr

  • 填写信息 下载jar包
  • 在IDEA中导入jar包

在IDEA中新建项目,选择Spring Initializr,输入项目名,项目位置,组名等信息后,点击next

在这里插入图片描述

在当前页面勾选Spring Web,以便项目可以在网页上运行

项目结构

controller、dao、service包要建在Application.java的同级目录下

在controller包下建立HelloController.java

package com.example.springboot.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//自动装配
@RestController
public class HelloController {
    //接口:http://localhost:8080/hello
    @RequestMapping("/hello")
    public String hello(){
        //调用业务,接受前端的参数
        return "Hello,World!";
    }
}

运行Application.java,运行结果如下

在网页中输入接口http://localhost:8080/hello

自动装配原理

  • xxxxAutoConfiguration:向容器中自动配置组件
  • xxxxProperties:自动配置属性类,装配配置文件中自定义的内容

SpringBoot所有自动配置都是在启动时扫描并加载spring.factories,所有的自动配置类都在里面,但不一定生效,需要判断条件是否成立。当导入对应的start时,会生成对应的启动器,启动器使自动装配生效,完成自动配置

  • springboot在启动时,从类路径下/META-INF/spring.factories获取指定的值
  • 将这些自动配置的类导入容器,使自动配置生效,进行自动配置
  • 整合JavaEE,解决方案和自动配置的文件都在spring-boot-autoconfigure-2.7.1.jar包下,它会将所有需要导入的组件,以类名的方式返回并添加到容器
  • 在容器中也存在许多xxxAutoConfiguration的文件(@Bean),这些类给容器中导入了场景需要的所有组件,并自动配置
  • 自动配置类免去了手动编写配置文件的工作

SpringBoot配置

配置文件

  • application.properties
    • 语法结构: key=value
  • application.yml(application.yaml)
    • 语法结构: key:(空格)value

配置文件的作用:修改SpringBoot自动配置的默认值

YAML

yaml配置:

# 普通的key-value
key: value
# 对象
student:
  name: name
  age: 3
# 行内写法
teacher: {name: name,age: 3}
# 数组
pets:
  - cat
  - dog

yaml可以直接给实体类赋值

//Person.java
package com.example.pojo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;
@Component
@ConfigurationProperties(prefix = "person")//将自动配置信息封装为实体类,将配置文件中的person的属性与实体类中的属性一一对应
public class Person {
    private String name;
    private Integer age;
    private Date birth;
    private Map<String,Object> map;
    private List<Object> list;
    private Dog dog;
    
    public Person() {
    }
    
    public Person(String name, Integer age, Date birth, Map<String, Object> map, List<Object> list, Dog dog) {
        this.name = name;
        this.age = age;
        this.birth = birth;
        this.map = map;
        this.list = list;
        this.dog = dog;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public Integer getAge() {
        return age;
    }
    
    public void setAge(Integer age) {
        this.age = age;
    }
    
    public Date getBirth() {
        return birth;
    }
    
    public void setBirth(Date birth) {
        this.birth = birth;
    }
    
    public Map<String, Object> getMap() {
        return map;
    }
    
    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
    
    public List<Object> getList() {
        return list;
    }
    
    public void setList(List<Object> list) {
        this.list = list;
    }
    
    public Dog getDog() {
        return dog;
    }
    
    public void setDog(Dog dog) {
        this.dog = dog;
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birth=" + birth +
                ", map=" + map +
                ", list=" + list +
                ", dog=" + dog +
                '}';
    }
}
//Dog.java
package com.example.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Dog {
    private String name;
    private Integer age;
    
    public Dog() {
    }
    
    public Dog(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public Integer getAge() {
        return age;
    }
    
    public void setAge(Integer age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
person:
  name: 张三
  age: 23
  birth: 2000/01/01
  map: {k1: v1,k2: v2}
  list:
    - code
    - music
  dog:
    name: 秀儿
    age: 3

@ConfigurationProperties和@Value对比

@ConfigurationProperties@Value
功能批量注入配置文件中的属性手动指定
松散绑定支持不支持
SPEL不支持支持
JSR303数据校验支持不支持
复杂类型封装支持不支持
  • 松散绑定:在yaml中,last-name和lastName是一样的,-后的字母默认大写
  • JSR303数据校验(@Validated):在字段上增加一层过滤器验证,以保证数据的合法性

@Validated常用注解

注解描述
@Null被注释的元素必须为null
@NotNull被注释的元素必须不为null
@AssertTrue被注释的元素必须为true
@AssertFalse被注释的元素必须为false
@Min(value)被注释的元素必须一个字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须一个字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max,min)被注释的元素的大小必须指的范围内
@Digits(integer,fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内。interger指定整数精度,fraction指定小数精度
@Past被注释的元素必须一个过去的日期
@Future被注释的元素必须是一个将来的日期
@Pattern(value)被注释的元素必须符合指定的正则表达式

多环境配置&配置文件的位置

SpringBoot默认配置文件优先级

  1. 项目路径下的config文件夹配置文件
  2. 项目路径下配置文件
  3. 资源路径下的config文件夹配置文件
  4. 资源路径下配置文件

当同优先级下的yml和properties都配置了端口,且没有激活其他环境时,默认会使用properties文件中端口

SpringBoot Web开发

需要解决的问题:

  • 静态资源导入
  • 首页
  • jsp,模板引擎Thymeleaf
  • 装配扩展SpringMVC
  • 增删改查
  • 拦截器
  • 国际化

静态资源

  • 在SpringBoot中处理静态资源的方式
    • webjars 映射—>localhost:8080/webjars
    • public,static,/**,resources 映射—>localhost:8080/
    • 优先级:resources>static(默认)>public

模板引擎

导入依赖

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

将html页面放在resources/templates包下

//IndexController.java
package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

//在template目录下的所有页面,只能通过controller跳转 需要模板引擎支持(Thymeleaf)
@Controller
public class IndexController {
    @RequestMapping("test")
    public String test(Model model){
        model.addAttribute("msg","hello");
        return "test";
    }
}
<!--test.html-->
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--所有的html元素都可以被Thymeleaf替换接管-->
<!--@thymesVar id="msg" type="String"-->
<div th:text="${msg}"></div>
</body>
</html>

这条注释可以消除th:text=“{msg}”msg爆红的问题

运行Spring03WebApplication,在浏览器中输入接口[Title](http://localhost:8080/test),运行结果如下

在这里插入图片描述

Thymeleaf语法

参考博客:Thymeleaf的基本语法 - 简书 (jianshu.com)

关键字功能举例
th:id替换id<input th:id="'XXX'+${collect.id}"/>
th:text文本替换<div th:text="${msg}"></div>
th:utext文本替换,可识别html标签<div th:utext="${msg}"></div>
th:object替换对象<div th:object="${session.user}"/>
th:each属性赋值<h3 th:each="user:${users}"></h3>
th:if判断条件<a th:if="${userId==collect.userId}">

案例:员工管理系统

静态资源:狂神springboot静态资源

创建数据表

实体类

//Employee.java
package com.example.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

//员工表
@Data
@NoArgsConstructor//创建无参构造方法
public class Employee {
    private Integer employeeId;
    private String employeeName;
    private String email;
    private Integer gender;//0为女,1为男
    private Department department;
    private Date date;
    
    public Employee(Integer employeeId, String employeeName, String email, Integer gender, Department department) {
        this.employeeId = employeeId;
        this.employeeName = employeeName;
        this.email = email;
        this.gender = gender;
        this.department = department;
        //默认创建日期
        this.date=new Date();
    }
    
    public Employee(String employeeName, String email, Integer gender, Department department) {
        this.employeeName = employeeName;
        this.email = email;
        this.gender = gender;
        this.department = department;
        this.date=new Date();
    }
}
//Department.java
package com.example.dao;

import com.example.pojo.Department;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

//部门Dao
@Repository
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 Collection<Department> getDepartments(){
        return departmentMap.values();
    }
    //通过id获取部门
    public Department getDepartmentById(Integer id){
        return departmentMap.get(id);
    }
}

Dao

//DepartmentDao.java
package com.example.dao;

import com.example.pojo.Department;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

//部门Dao
@Repository//在Spring中配置扫描包地址,然后生成dao层的bean,之后被注入到ServiceImpl中
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 Collection<Department> getDepartments(){
        return departmentMap.values();
    }
    //通过id获取部门
    public Department getDepartmentById(Integer id){
        return departmentMap.get(id);
    }
}
//EmployeeDao.java
package com.example.dao;

import com.example.pojo.Department;
import com.example.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

//员工Dao
@Repository//在Spring中配置扫描包地址,然后生成dao层的bean,之后被注入到ServiceImpl中
public class EmpolyeeDao {
    //模拟数据库中的数据
    private static Map<Integer,Employee> employeeMap=null;
    //员工有所属部门
    @Autowired
    private DepartmentDao departmentDao;
    static {
        employeeMap=new HashMap<>();//创建员工表
        employeeMap.put(1001,new Employee(1001,"张三","123@email.com",1,new Department(101,"技术部")));
        employeeMap.put(1002,new Employee(1002,"李四","1343@email.com",1,new Department(102,"财务部")));
        employeeMap.put(1003,new Employee(1003,"刻晴","12213@email.com",0,new Department(105,"法务部")));
        employeeMap.put(1004,new Employee(1004,"王五","1134141@email.com",1,new Department(103,"人事部")));
        employeeMap.put(1005,new Employee(1005,"可莉","352143@email.com",0,new Department(101,"技术部")));
        employeeMap.put(1006,new Employee(1006,"小鹿","1451413@email.com",0,new Department(103,"人事部")));
        employeeMap.put(1007,new Employee(1007,"胡强","352143@email.com",1,new Department(104,"市场部")));
        
    }
    //主键自增
    private static Integer initid=1008;
    //增加员工&保存对员工信息的修改
    public void saveEmployee(Employee employee){
        if(employee.getEmployeeId()==null){
            employee.setEmployeeId(++initid);
        }
        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getDepartmentId()));
        employeeMap.put(employee.getEmployeeId(),employee);
    }
    //查询全部员工信息
    public Collection<Employee> getEmployees(){
        return employeeMap.values();
    }
    //通过id查询员工
    public Employee getEmployeeById(Integer id){
        return employeeMap.get(id);
    }
    //通过id删除员工
    public void deleteEmployeeById(Integer id){
        employeeMap.remove(id);
    }
}

首页实现

在resources/template下的html文件无法由config类直接访问,需要建立控制类

//IndexController.java
package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
    @RequestMapping({"/","/index"})
    public String index(){
        return "index";
    }
}

或者修改config类

//MvcConfig.java
package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    //视图跳转
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
}

页面国际化

国际化乱码问题解决:SpringBoot国际化配置出现乱码问题

配置i18n文件

# login_zh_CN.properties
login.btn=登录
login.password=请输入密码
login.remember=记住密码
login.tip=请登录
login.title=登录界面
login.username=请输入用户名
# login_en_US.properties
login.btn=Sign In
login.password=Please enter your password
login.remember=Remember me
login.tip=please sign in
login.title=Login
login.username=Please enter your username

login.properties为默认语言,可在login_zh_CN.propertieslogin_en_US.properties中任意选择

自定义国际化组件LocaleResolver

//MyLocaleResolver.java
package com.example.config;

import org.springframework.web.servlet.LocaleResolver;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

public class MyLocaleResolve implements LocaleResolver {
    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        //获取请求中的语言参数
        String language = request.getParameter("language");
        Locale locale = Locale.getDefault();//language为空,则使用默认的语言环境
        
        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) {
    
    }
}

MVcConfig.java中注册bean

//自定义国际化组件
@Bean
public LocaleResolver localeResolver(){
    return new MyLocaleResolve();
}

修改index.xml

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta http-equiv="Content-Type" content="text/html; 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="">
		<title>[[#{login.title}]]</title>
		<!-- Bootstrap core CSS -->
		<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
		<!-- Custom styles for this template -->
		<link th:href="@{/css/signin.css}" rel="stylesheet">
	</head>

	<body class="text-center">
		<form class="form-signin" action="dashboard.html">
			<img class="mb-4" src="img/bootstrap-solid.svg" alt="" width="72" height="72">
			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
			<label class="sr-only" ></label>
			<input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
			<label class="sr-only"></label>
			<input type="password" class="form-control" th:placeholder="#{login.password}" required="">
			<div class="checkbox mb-3">
				<label>
          			<input type="checkbox">[[#{login.remember}]]
        		</label>
			</div>
			<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
			<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
			<a class="btn btn-sm" th:href="@{/index.html(language='zh_CN')}">中文</a>
			<a class="btn btn-sm" th:href="@{/index.html(language='en_US')}">English</a>
		</form>

	</body>

</html>

登录功能实现

编写控制器LoginController.java

package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.thymeleaf.util.StringUtils;

@Controller
public class LoginController {
    @RequestMapping("/user/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        Model model){
        //判断用户名&密码是否正确
        if(!StringUtils.isEmpty(username)&&"123".equals(password)){
            //登录成功
            return "redirect:/main.html";
        }
        else{
            //登陆失败
            model.addAttribute("msg","用户名或密码错误");
            return "/index";
        }
    }
}

不能将@Controller注解换成@RestController,因为@RestController是@Controller+@ResponseBody的结合,返回的是return后面的值(json类型),会导致页面无法正常显示

MvcConfig.java中增加视图跳转语句

registry.addViewController("/main.html").setViewName("dashboard");

避免登录成功后跳转至dashboard.html页面时,域名写有用户名和密码。

如果dashboard样式显示错误,可对下方语句进行修改

<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/dashboard.css}" rel="stylesheet">

登录拦截器

由于在编写控制器时控制了dashboard.html的页面跳转,在实际使用过程中,只需要输入http://localhost:8080/management/main.html即可跳转至dashboard界面。因此我们需要增加一个拦截器

编写拦截器LoginHandlerInterceptor

package com.example.config;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //登录成功后,用户会产生session
        Object user = request.getSession().getAttribute("loginUser");
        if(user==null){
            //没有登录
            request.setAttribute("msg","没有权限,请先登录!");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;//true放行,false不放行
        }
        else
            return true;
    }
}

MvcConfig中配置拦截器

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**")//拦截的对象
                .excludePathPatterns(
                        "/index.html","/","/user/login",
                        "/css/*","/js/*","/img/*")//放行的对象;
    }

增删改查功能实现

提取公共页面

dashboard.htmllist.html中的公共部分(顶部导航栏和侧边栏)提取出来,放入常用资源包commons下的commons.html中,用th:fragment给他们命名

<!--topbar-->
<!--顶部导航栏-->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
    <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a>
    <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
    <ul class="navbar-nav px-3">
        <li class="nav-item text-nowrap">
            <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">注销</a>
        </li>
    </ul>
</nav>
<!--sidebar-->
<!--侧边栏-->
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
    <div class="sidebar-sticky">
        <ul class="nav flex-column">
            <li class="nav-item">
                <a class="nav-link active" th:href="@{/main.html}">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home">
                        <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                        <polyline points="9 22 9 12 15 12 15 22"></polyline>
                    </svg>
                    Dashboard <span class="sr-only">(current)</span>
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file">
                        <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path>
                        <polyline points="13 2 13 9 20 9"></polyline>
                    </svg>
                    Orders
                </a>
            </li>
            <li class="nav-item ">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shopping-cart">
                        <circle cx="9" cy="21" r="1"></circle>
                        <circle cx="20" cy="21" r="1"></circle>
                        <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
                    </svg>
                    Products
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link"  th:href="@{/employees}">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users">
                        <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
                        <circle cx="9" cy="7" r="4"></circle>
                        <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                        <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
                    </svg>
                    Customers
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bar-chart-2">
                        <line x1="18" y1="20" x2="18" y2="10"></line>
                        <line x1="12" y1="20" x2="12" y2="4"></line>
                        <line x1="6" y1="20" x2="6" y2="14"></line>
                    </svg>
                    Reports
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-layers">
                        <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
                        <polyline points="2 17 12 22 22 17"></polyline>
                        <polyline points="2 12 12 17 22 12"></polyline>
                    </svg>
                    Integrations
                </a>
            </li>
        </ul>

        <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
            <span>Saved reports</span>
            <a class="d-flex align-items-center text-muted" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line></svg>
            </a>
        </h6>
        <ul class="nav flex-column mb-2">
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                        <polyline points="14 2 14 8 20 8"></polyline>
                        <line x1="16" y1="13" x2="8" y2="13"></line>
                        <line x1="16" y1="17" x2="8" y2="17"></line>
                        <polyline points="10 9 9 9 8 9"></polyline>
                    </svg>
                    Current month
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                        <polyline points="14 2 14 8 20 8"></polyline>
                        <line x1="16" y1="13" x2="8" y2="13"></line>
                        <line x1="16" y1="17" x2="8" y2="17"></line>
                        <polyline points="10 9 9 9 8 9"></polyline>
                    </svg>
                    Last quarter
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                        <polyline points="14 2 14 8 20 8"></polyline>
                        <line x1="16" y1="13" x2="8" y2="13"></line>
                        <line x1="16" y1="17" x2="8" y2="17"></line>
                        <polyline points="10 9 9 9 8 9"></polyline>
                    </svg>
                    Social engagement
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                        <polyline points="14 2 14 8 20 8"></polyline>
                        <line x1="16" y1="13" x2="8" y2="13"></line>
                        <line x1="16" y1="17" x2="8" y2="17"></line>
                        <polyline points="10 9 9 9 8 9"></polyline>
                    </svg>
                    Year-end sale
                </a>
            </li>
        </ul>
    </div>
</nav>

dashboard.htmllist.html中原先关于顶部导航栏和侧边栏的语句替换为下方代码

仅替换<nav>标签下的代码即可

<!--顶部导航栏-->
<div th:replace="~{commons/commons::topbar}"></div>
<!--侧边栏-->
<div th:replace="~{commons/commons::sidebar}"></div>

设置选中高亮

在最初的网页中,无论当前处于什么界面,只用“首页”存在高亮,在使用中无法清楚地观察当前的页面。为了解决这一问题,我们可以在对应的html文件中增加参数传递,并在commons.html中使用th:class="${}"配合三元运算进行判断,实现高亮状态的切换。

修改dashboard.html中侧边栏的语句

<div th:replace="~{commons/commons::sidebar(active='main.html')}"></div>

其中,main.htmlMvcConfig.javadashboard.html对应的视图跳转名称一致

修改list.html中侧边栏的语句

<div th:replace="~{commons/commons::sidebar(active='list.html')}"></div>

修改commons.html中对应代码,代码位置及修改如下

在这里插入图片描述

<a th:class="${active=='main.html'?'nav-link active':'nav-link'}" th:href="@{/main.html}">

在这里插入图片描述

<a th:class="${active=='list.html'?'nav-link active':'nav-link'}"  th:href="@{/employees}">

/employeesEmployeeController.java定义的Mapper接口

EmployeeController.java代码如下

package com.example.controller;

import com.example.dao.EmpolyeeDao;
import com.example.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Collection;

@Controller
public class EmployeeController {
    @Autowired
    EmpolyeeDao empolyeeDao;
    @RequestMapping("/employees")
    public String List(Model model){
        Collection<Employee> employees = empolyeeDao.getEmployees();
        model.addAttribute("employees",employees);
        return "employees/list";
    }
}

展示员工列表

修改list.html原有的表格代码,代码位置及修改如下

<table class="table table-striped table-sm">
    <thead>
        <tr>
            <th>工号</th>
            <th>姓名</th>
            <th>电子邮箱</th>
            <th>性别</th>
            <th>部门信息</th>
            <th>入职日期</th>
        </tr>
    </thead>
    <tbody>
        <!--@thymesVar id="employees" type="List<com.example.pojo.Employee>"-->
        <!--@thymesVar id="employee" type="com.example.pojo.Employee"-->
        <tr th:each="employee:${employees}">
            <td th:text="${employee.getEmployeeId()}"/>
            <td th:text="${employee.getEmployeeName()}"/>
            <td th:text="${employee.getEmail()}"/>
            <td th:text="${employee.getGender()==0?'':''}"/>
            <td th:text="${employee.getDepartment().getDepartmentName()}"/>
            <td th:text="${#dates.format(employee.getDate(),'yyyy-MM-dd')}"/>
        </tr>
    </tbody>
</table>

<td th:text="${employee.getGender()==0?'女':'男'}"/>将数据表中对于性别的描述(0/1)转换为(女/男)

<td th:text="${#dates.format(employee.getDate(),'yyyy-MM-dd')}"/>将日期的输出格式进行定义

employees来源于EmployeeController.java中的查询方法,代码如下

package com.example.controller;

import com.example.dao.EmpolyeeDao;
import com.example.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Collection;

@Controller
public class EmployeeController {
    @Autowired
    EmpolyeeDao empolyeeDao;
    @RequestMapping("/employees")
    public String List(Model model){
        Collection<Employee> employees = empolyeeDao.getEmployees();
        model.addAttribute("employees",employees);
        return "employees/list";
    }
}

为了方便后续的增删改操作,我们可以在显示页面增加按钮:添加、删除、修改

首先,我们需要在原有的员工表中增加一列,即在<th>入职日期</th>后增加如下代码

<th>操作</th>

增加操作列之后,我们需要在表格中增加对应的按钮,即在<td th:text="${#dates.format(employee.getDate(),'yyyy-MM-dd')}"/>之后增加如下代码

<td>
    <a class="btn btn-sm btn-danger">删除</a>
    <a class="btn btn-sm btn-primary">编辑</a>
</td>

同时,我们需要在页面上增加“添加员工”按钮,即在<div class="table-responsive">上方添加如下代码

<a class="btn btn-sm btn-success">添加员工</a>

运行结果

添加员工

在实现员工列表展示时,我们在主页增加了“添加员工”的按钮,我们只需要实现它,就可以实现添加员工的功能。

首先,我们要对“添加员工”按钮的代码进行修改,为其增加一个链接

<a class="btn btn-sm btn-success" th:href="@{/employee}">添加员工</a>

此时,点击“添加员工”按钮后,会向后台发送一个请求/employee,我们需要在EmployeeController.java中处理这个请求

@Autowired
DepartmentDao departmentDao;
@GetMapping("/employee")
public String toAddEmployee(Model model){
    //查询部门信息
    Collection<Department> departments=departmentDao.getDepartments();
    model.addAttribute("departments",departments);
    return "employees/addEmployee";
}

这个方法可以接受前端的请求,并将页面跳转至addEmployee.htmladdEmployee.html主体代码如下

<form th:action="@{/employee}" method="post">
    <div class="form-group">
        <label>姓名</label>
        <input type="text" name="employeeName" class="form-control" placeholder="请输入姓名">
    </div>
    <div class="form-group">
        <label>电子邮箱</label>
        <input type="text" name="email" class="form-control" placeholder="请输入电子邮箱">
    </div>
    <div class="form-group">
        <label class="form-label">性别</label>
        <input type="radio" name="gender" value="0"><input type="radio" name="gender" value="1"></div>
    <div class="form-group">
        <label>部门</label>
        <!--@thymesVar id="departments" type="java.util.List<com.example.pojo.Department>"-->
        <!--@thymesVar id="department" type="com.example.pojo.Department"-->
        <select class="form-control" name="department.departmentId" >
            <option selected>--请选择部门--</option>
            <option th:each="department:${departments}" th:value="${department.getDepartmentId()}">
                [[${department.getDepartmentName()}]]
            </option>
        </select>
    </div>
    <button class="btn btn-primary">提交</button>
</form>

上述代码为表单代码,其余代码可参考list.html,将其中表格代码换成上述代码即可

点击addEmployee.html中的“提交”按钮,页面会发送一个请求,返回至EmployeeController.java。此时,我们再编写一个方法,用于增加employee

@PostMapping("/employee")
public String AddEmployee(Employee employee){
    empolyeeDao.saveEmployee(employee);
    return "redirect:/employees";
}

修改员工信息

与”添加员工“类似,要实现对员工信息的修改,我们只需要实现<a class="btn btn-sm btn-primary">编辑</a>对应的功能即可

首先,我们需要在改标签下增加一个链接,用于跳转至修改页面

<a class="btn btn-sm btn-primary" th:href="@{/employee/{employeeId}(employeeId=${employee.getEmployeeId()})}">编辑</a>

其他格式的RESTful语句如下

th:href="@{/employee(id=${employee.getEmployeeId()})}"
<!--上面这句会用'?'拼接在url后传递参数,在后端处理时需要使用其他方法处理-->
th:href="@{/employee/{employee}/(id=${employee.getEmployeeId()})}"

th:href="@{'/employee/'+${employee.getEmployeeId()}}"

增加链接之后,我们需要在EmployController.java中编写对应的方法,以处理请求,实现页面跳转

@GetMapping("/employee/{employeeId}")
//通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中
public String toUpdateEmployee(@PathVariable("employeeId") Integer employeeId, Model model){
    Employee employee=empolyeeDao.getEmployeeById(employeeId);
    model.addAttribute("employee",employee);
    Collection<Department> departments=departmentDao.getDepartments();
    model.addAttribute("departments",departments);
    return "employees/updateEmployee";
}

如果使用th:href="@{/employee(id=${employee.getEmployeeId()})}",需要使用@RequestParam进行参数的绑定

由于在“添加员工”时,我们已经定义过名为/employee的Map映射,因此我们需要修改RESTful语句,以定义不同名称的映射,这里我们将它修改为

th:href="@{/employee/(employeeId=${employee.getEmployeeId()})}"

修改EmployController.java中的toUpdateEmployee方法

@GetMapping("/employee/")
public String toUpdateEmployee(@RequestParam("employeeId") Integer employeeId, Model model){
 Employee employee=empolyeeDao.getEmployeeById(employeeId);
 model.addAttribute("employee",employee);
 Collection<Department> departments=departmentDao.getDepartments();
 model.addAttribute("departments",departments);
 return "employees/updateEmployee";
}

主要区别在于使用了@RequestParam替换原来的@pathVariable

关于两种传参方式的选择可以参考:@RequestParam和@PathVariable的用法与区别

使用@RequestParam可以实现在修改页面显示数据,但是在后续提交修改时会报错,目前还没有解决这个问题

编写完处理方法后,我们需要编写updateEmployee.html,即修改页面,主体代码如下

<!--@thymesVar id="employee" type="com.example.pojo.Employee"-->
<form th:action="@{/updateEmployee}" method="post">
    <div class="form-group">
        <label>姓名</label>
        <input type="hidden" name="employeeId" th:value="${employee.getEmployeeId()}">
        <input type="text" th:value="${employee.getEmployeeName()}" name="employeeName" class="form-control" placeholder="请输入姓名">
    </div>
    <div class="form-group">
        <label>电子邮箱</label>
        <input type="text" th:value="${employee.getEmail()}" name="email" class="form-control" placeholder="请输入电子邮箱">
    </div>
    <div class="form-group">
        <label class="form-label">性别</label>
        <input type="radio" th:checked="${employee.getGender()==0}" name="gender" value="0"><input type="radio" th:checked="${employee.getGender()==1}" name="gender" value="1"></div>
    <div class="form-group">
        <label>部门</label>
        <!--@thymesVar id="departments" type="java.util.List<com.example.pojo.Department>"-->
        <!--@thymesVar id="department" type="com.example.pojo.Department"-->
        <select class="form-control" name="department.departmentId" >
            <option selected >[[${employee.getDepartment().getDepartmentName()}]]</option>
            <option th:each="department:${departments}" th:value="${department.getDepartmentId()}"
                    th:if="${employee.getDepartment().getDepartmentName()}!=${department.getDepartmentName()}">
                [[${department.getDepartmentName()}]]
            </option>
        </select>
    </div>
    <button class="btn btn-primary">提交</button>
</form>

上方代码仅包含展示的表单,其余代码可参考addEmployee.html

当我们点击修改页面的提交按钮时,前端会向后端发送请求,要求保存修改并跳转至员工列表页面。我们可以编写控制方法实现请求

@PostMapping("/updateEmployee")
public String UpdateEmployee(Employee employee){
    empolyeeDao.saveEmployee(employee);
    return "redirect:/employees";
}

当前端发送请求时,后端会执行该方法,保存前端的修改并跳转至员工展示页面

删除员工

相对于增、改、查,删除的功能实现就显得异常简单。

首先,我们需要修改“删除”标签的对应代码,为其增加一个链接

<a class="btn btn-sm btn-danger" th:href="@{/deleteEmployee(employeeId=${employee.getEmployeeId()})}">删除</a>

增加了这个链接之后,当我们点击“删除”时,前端会发送一个请求,此时我们需要在后端接受并处理这个请求,即在EmployeeController.java中增加一个方法

@GetMapping("/deleteEmployee")
public String deleteEmployee(@RequestParam Integer employeeId){
    empolyeeDao.deleteEmployeeById(employeeId);
    return "redirect:/employees";
}

至此,删除的功能也实现了

注销

为了方便后续用户切换,我们可以增加一个注销功能。在我们原先的顶部导航栏右侧有一个”注销“按钮,我们只需要对他进行修改,使其点击之后跳转至登录页面即可

<a class="nav-link" th:href="@{/index.html}">注销</a>

“注销”按钮的代码在commons/commons.html文件中

整合JDBC

在创建项目时勾选Spring Web、JDBC APIMySQL Driver

项目创建完成后,编写application.yaml,在里面配置数据源

spring:
  datasource:
    username: #{username}
    password: #{password}
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8

创建controller包以及JdbcController.java,读取数据库中的信息

package com.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
public class JdbcController {
    @Autowired
    JdbcTemplate jdbcTemplate;
    //查询数据库的所有信息
    @GetMapping("/userList")
    public List<Map<String,Object>> userList(){
        String sql="select * from user";
        List<Map<String, Object>> mapList = jdbcTemplate.queryForList(sql);
        return mapList;
    }
}

没有实体类可以使用Map获取数据库中的信息

运行项目,在浏览器中输入http://localhost:8080/userList,得到如下界面

整合MyBatis

整合包:mybatis-spring-boot-starter

<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

在创建项目时选择下列依赖

application.propertiesapplication.yaml中配置datasource相关参数

spring:
  datasource:
    username: #{username}
    password: #{password}
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/#{database-name}?useUnicode=true&characterEncoding=utf-8

在使用时,需要在mapper类前添加@Mapper注解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值