SpringBoot-基础入门

==SpringBoot2和SpringBoot1.x 有区别,一个是基于JDB1.8,一个是基于JDK1.5,具体其他区别可以上网查询资料了解==

一、Spring与SpringBoot

1、Spring能做什么

1.1、Spring的能力

Spring的能力

1.2、Spring的生态

  • 覆盖了:
    • web开发
    • 数据访问
    • 安全控制
    • 分布式
    • 消息服务
    • 移动开发
    • 批处理
    • ……

1.3、Spring5的重大升级

1.3.1、响应式编程

响应式编程

1、3、2、内部源码设计

基于Java8的一些新特性,如:接口默认实现。重新设计源码架构。

2、为什么用SpringBoot

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.

能快速创建出生产级别的Spring应用

2.1、SpringBoot优点

  • Create stand-alone Spring applications

    • 创建独立Spring应用
  • Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)

    • 内嵌web服务器
  • Provide opinionated ‘starter’ dependencies to simplify your build configuration

    • 自动starter依赖,简化构建配置
  • Automatically configure Spring and 3rd party libraries whenever possible

    • 自动配置Spring以及第三方功能
  • Provide production-ready features such as metrics, health checks, and externalized configuration

    • 提供生产级别的监控、健康检查及外部化配置
  • Absolutely no code generation and no requirement for XML configuration

    • 无代码生成、无需编写XML

SpringBoot是整合Spring技术栈的一站式框架

SpringBoot是简化Spring技术栈的快速开发脚手架

2.2、SpringBoot的缺点

  • 人称版本帝,迭代快,需要时刻关注变化
  • 封装太深,内部原理复杂,不容易精通

3、时代背景

3.1、微服务

James Lewis and Martin Fowler (2014) 提出微服务完整概念。https://martinfowler.com/microservices/

In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.

简而言之,微服务架构样式是一种将单个应用程序开发为一组小型服务的方法,每个小型服务在自己的进程中运行并与轻量级机制(通常是HTTP资源API)进行通信。这些服务围绕业务功能构建,并且可以 由全自动部署机制独立部署。有一个集中管理的最低限度的这些服务,可以用不同的编程语言和使用不同的数据存储技术。

  • 微服务是一种架构风格
  • 一个应用拆分为一组小型服务
  • 每个服务运行在自己的进程内,也就是可独立部署和升级
  • 服务之间使用轻量级HTTP交互
  • 服务围绕业务功能拆分
  • 可以由全自动部署机制独立部署
  • 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术

3.2、分布式

分布式

3.2.1、分布式的困难
  • 远程调用
  • 服务发现
  • 负载均衡
  • 服务容错
  • 配置管理
  • 服务监控
  • 链路追踪
  • 日志管理
  • 任务调度
  • ……
3.2.2、分布式的解决

==SpringBoot+SpringCloud==

分布式解决

3.3、云原生

3.3.1、上云的困难
  • 服务自愈
  • 弹性伸缩
  • 服务隔离
  • 自动化部署
  • 灰度发布
  • 流量治理
  • ……
3.3.2、上云的解决

上云的解决

4、如何学习SpringBoot

官网文档https://docs.spring.io/spring-boot/docs/current/reference/html/

4.1、官网文档架构

参考文档2

参考文档2

4.2、查看版本新特性

版本新特性

二、SpringBoot入门

1、系统要求

  • java8 & 兼容java14 .
  • Maven 3.3+

1.1、Maven设置

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
<!-- 设置仓库的路径 -->
<localRepository> <!-- 仓库的路径 --> </localRepository>

<mirrors>
<!-- 配置阿里云的镜像 -->
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>

<profiles>
<!-- 配置JDK的版本 -->
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>

2、第一个SpringBoot项目

2.1、创建Maven项目

后面还有其他创建SpringBoot项目的方式

2.2、引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

2.3、创建主程序

1
2
3
4
5
6
7
8
9
//主程序类,注意主启动类的位置
@SpringBootApplication//告诉SpringBoot这是一个SpringBoot应用
public class MainApplication {

public static void main(String[] args) {
//固定写法
SpringApplication.run(MainApplication.class, args);
}
}

2.4、测试

直接运行主程序

2.5、简化配置

==为了将简化配置,可以将所有配置全部写到一个配置文件中,文件名默认为:application.properties==

文件的具体使用方法可以去下面地址查询使用方法:

https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html

1
2
#设置Tomcat启动的端口号,创建一个application.properties文件用来修改SpringBoot的配置
server.port=8888

2.6、简化配置

  • 在pom.xml配置文件中继续添加以下配置

    1
    2
    3
    4
    5
    6
    7
    8
    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
  • 使用Maven的生命周期将Maven项目clean、package,将项目打包成为可执行的jar包

    1
    2
    找到打包的jar包目录下打开cmd,使用Java的命令执行jar包
    java -jar 【打包的jar包的文件名】
  • 注意点:如果不能启动,可以尝试取消cmd的快速编辑模式,怎么取消cmd的快速编辑模式可以上网查询

三、了解自动配置原理

1、SpringBoot特点

1.1、依赖管理

  • 父项目做依赖管理

    从SpringBoot入门能了解到,Maven的pom.xml文件需要导入SpringBoot的依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!-- 依赖管理 -->
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.5</version>
    </parent>

    <!--
    spring-boot-starter-parent的父项目
    几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制
    -->
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.5</version>
    </parent>
  • 开发导入starter场景启动器

    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
    <!-- 导入SpringWeb开发所需要的包 -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    1、
    当导入依赖的时候,artifactId标签中填写的一般是 spring-boot-starter-* : *就某种场景
    例如:spring-boot-starter-web,导入的是SpringWeb开发的场景
    spring-boot-starter-test,导入的是代码测试环境所需要的开发环境
    其他场景可以去官网查询:https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter

    2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入

    3、SpringBoot所有支持的场景

    4、见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
    例如:MyBatisPlus的:mybatis-plus-boot-starter

    5、所有场景启动器最底层的依赖
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.4.5</version>
    <scope>compile</scope>
    </dependency>
  • 无需关注版本号,自动版本仲裁

    • 引入依赖默认都可以不写版本
    • 引入非版本仲裁的jar,要写版本号。
  • 可以修改默认版本号

    1
    2
    3
    4
    5
    6
    1、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
    2、在当前项目里面重写配置,
    <properties>
    <!-- Spring -->
    <mysql.version>8.0.19</mysql.version>
    </properties>

1.2、自动配置

  • 自动配置好Tomcat

    • 引入Tomcat依赖

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      <!-- 方式一 -->
      <!-- 第一步 -->
      <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.4.5</version>
      </parent>
      <!-- 第二步,引入web开发所需要的所有东西 -->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

      <!-- 方式二,只引入Tomcat -->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.4.5</version>
      <scope>compile</scope>
      </dependency>
    • 配置Tomcat

  • 自动配置好SpringMVC

    • 引入SpringMVC全套组件(如web.xml文件中的dispatchServlet,文件上传的:StandardServletMultipartResolver,等等)
    • 自动配好SpringMVC常用组件(功能)
  • 自动配置好Web常见功能,如:字符编码问题

    • SpringBoot帮我们配置好了所有web开发的常见场景
  • 默认的包结构

    • 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来,如果所在包的级别大于主程序,那么将不会被扫描出来,如果需要扫描,则改变默认的扫描的路径

    • 无需以前的包扫描配置

    • 想要改变扫描路径

      • @SpringBootApplication(scanBasePackages=”com.atguigu”)

      • 或者@ComponentScan 指定扫描路径,具体使用可以去找Spring注解驱动开发的笔记,或找后面的笔记

        1
        2
        3
        4
        5
        @SpringBootApplication//告诉SpringBoot这是一个SpringBoot应用
        //通过查看@SpringBootApplication注解可以知道,上面一个注解相当于下面三个注解
        @SpringBootConfiguration
        @EnableAutoConfiguration
        @ComponentScan
  • 各种配置拥有默认值
    • 默认配置最终都是映射到某个类上,如:MultipartProperties
    • 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
  • 按需加载所有自动配置项
    • Spring中有非常多的starter
    • 引入了哪些场景这个场景的自动配置才会开启
    • SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
  • ……….

2、容器组件功能

2.1、组件添加

==这里的组件添的部分简单使用方法,在Spring注解驱动开发的笔记中还有以下复杂的用发,该笔记中也有Spring注解驱动开发中没有的部分,两者笔记可以相互借鉴使用==

2.1.1、Configuration
  • 基本使用

  • Full模式与Lite模式

    • 最佳实战

      • 配置类组件之间没有依赖关系用Lite模式,加速容器启动,减少判断(同一个配置类之间的Bean没有相互调用的关系使用Lite模式)
      • 配置类组件之间有依赖关系,方法会被调用得到之前的单实例,用Full(同一个配置类之间的Bean有相互调用的关系使用Full模式)
    • 实例

      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
      /**
      * 1、配置类里面使用@Bean注解标注在方法上给容器注册组件,默认也是单实例的
      * 2、配置类本身也是组件
      * 3、@Configuration注解中有个属性proxyBeanMethods默认值为true:代理Bean方法,
      * 设置为true,每次获取Bean的时候都会检查容器是否有该Bean,设置为false,不会检查是否有该Bean
      * Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
      * Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
      * 组件依赖必须使用Full模式默认。其他默认是否Lite模式
      */
      @Configuration//告诉SpringBoot这是一个配置类 == Spring的配置文件
      public class MyConfig {

      /**
      * Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
      * @return
      */
      @Bean//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
      public User user() {
      return new User("张三", 18);
      }

      @Bean//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
      public Pet pet() {
      return new Pet("Tomcat");
      }
      }

2.1.2、@Bean、@Component、@Controller、@Service、@Repository
2.1.3、@ComponentScan、@Import

==这里只有@Import注解的简单使用,在Spring注解驱动开发中还有其他一些高级的用法==

1
2
3
4
//@Import({User.class, DataSource.class})  给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
@Configuration//告诉SpringBoot这是一个配置类 == Spring的配置文件
@Import({Car.class, DataSource.class})
public class MyConfig { }
2.1.4、@Conditional
  • 条件装配:满足Conditional指定的条件,则进行组件注入

Conditional

  • @Conditional测试:

    这里只测试@Conditional的子注解@ConditionalOnBean@ConditionalOnMissingBean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Configuration//告诉SpringBoot这是一个配置类 == Spring的配置文件
    //@ConditionalOnBean(name = "user")//当配置类中有name为user的bean的时候,该配置类不起作用,可以写在方法上和类上
    @ConditionalOnMissingBean(name = "user")//当配置类中没有name为user的bean的时候,该配置类不起作用,可以写在方法上和类上
    public class MyConfig {

    @Bean//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
    public User user() {
    return new User("张三", 18);
    }

    @Bean//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
    public Pet pet() {
    return new Pet("Tomcat");
    }
    }
  • 测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public static void main(String[] args) {
    //返回IOC容器
    ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);

    //2、查看容器里面的组件,获取组件中的全部容器
    String[] names = context.getBeanDefinitionNames();

    //3、从容器中获取组件,如果有类型为User的Bean将不报错,如果报错,则没有类型为User的Bean
    context.getBean(User.class);
    //System.out.println(context.getBean(Car.class));
    }

2.2、原生配置文件引入

==引入Spring的配置文件==

2.2.1、@ImportResource

格式:@ImportResource(“classpath:文件”)

  • Spring的原生配置文件:applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user01" class="com.lf.boot.pojo.User"/>

    </beans>
  • SpringBoot配置类引入applicationContext.xml配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Configuration//告诉SpringBoot这是一个配置类 == Spring的配置文件
    //@ConditionalOnBean(name = "user")//当配置类中有name为user的bean的时候,该配置类不起作用,可以写在方法上和类上
    @ConditionalOnMissingBean(name = "user")//当配置类中没有name为user的bean的时候,该配置类不起作用,可以写在方法上和类上
    @ImportResource("classpath:applicationContext.xml")
    public class MyConfig {

    @Bean//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
    public User user() {
    return new User("张三", 18);
    }

    @Bean//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
    public Pet pet() {
    return new Pet("Tomcat");
    }
    }

2.3、properties配置绑定

如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;

  • 使用Java的原生方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Test1 {
    @Test
    public class getProperties {
    public static void main(String[] args) throws FileNotFoundException, IOException {
    Properties pps = new Properties();
    pps.load(new FileInputStream("a.properties"));
    Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
    while(enum1.hasMoreElements()) {
    String strKey = (String) enum1.nextElement();
    String strValue = pps.getProperty(strKey);
    System.out.println(strKey + "=" + strValue);
    //封装到JavaBean。
    }
    }
    }
    }
2.3.1、@ConfigurationProperties + @Component

@ConfigurationProperties,使用该注解必须要将前缀传入 prefix = “前缀名”

@Component

两个注解需要同时写在JavaBean上

想使用该注解需要将使用的类添加到Spring中,可以加上@component注解将类加入Spring中

该注解可以让JavaBean和properties配置文件绑定,让Spring给JavaBean中的属性自动赋值,值的数据从properties文件中获取

  • properties属性文件:

    1
    2
    mycar.brand=测试
    mycar.price=999
  • JavaBean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.lf.boot.pojo;

    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    /**
    * 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
    * 只有将类交给Spring管理才能使用@ConfigurationProperties注解
    * prefix:如果properties文件中对应的属性有前缀,可以使用该属性传入前缀的值,在给属性自动赋值的时候后就会自动将前缀的字符和属性的字符连接起来 去配置文件中寻找,如果没有前缀,直接把注解加上就行了
    * 注意:需要有对应的get和set方法
    */
    @Component
    @ConfigurationProperties(prefix = "mycar")
    public class Car {

    private String brand;
    private Integer price;
    // .......对应的get和set方法
    }
  • 测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //@RestController 注解相当于 @ResponseBody 和 @requestMapping两个注解的结合的使用,
    //使用了@RestController注解的类下的所有方法返回的东西全部输出在浏览器的页面上
    @RestController
    public class HelloController {
    @Autowired
    private Car car;

    @RequestMapping("/car")
    public Car car() {
    return car;
    }
    }
2.3.2、@EnableConfigurationProperties + @ConfigurationProperties

@EnableConfigurationProperties,该注解写在配置类上

@ConfigurationProperties,该注解写在JavaBean上

@EnableConfigurationProperties(哪个类需要开启的属性的配置的.class)

作用

一:开启配置绑定功能

二:把传入的组件自动注册到容器中

  • properties属性文件:

    1
    2
    mycar.brand=测试
    mycar.price=999
  • JavaBean

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.lf.boot.pojo;

    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    /**
    * 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
    * 只有将类交给Spring管理才能使用@ConfigurationProperties注解
    * prefix:如果properties文件中对应的属性有前缀,可以使用该属性传入前缀的值,在给属性自动赋值的时候后就会自动将前缀的字符和属性的字符连接起来 去配置文件中寻找,如果没有前缀,直接把注解加上就行了
    * 注意:需要有对应的get和set方法
    */
    @ConfigurationProperties(prefix = "mycar")
    public class Car {

    private String brand;
    private Integer price;
    // .......对应的get和set方法
    }
  • 配置类:

    1
    2
    3
    4
    5
    @Configuration//告诉SpringBoot这是一个配置类 == Spring的配置文件
    @EnableConfigurationProperties(Car.class)
    //1、开启Car配置绑定功能
    //2、把这个Car这个组件自动注册到容器中
    public class MyConfig { }
  • 测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //@RestController 注解相当于 @ResponseBody 和 @requestMapping两个注解的结合的使用,
    //使用了@RestController注解的类下的所有方法返回的东西全部输出在浏览器的页面上
    @RestController
    public class HelloController {
    @Autowired
    private Car car;

    @RequestMapping("/car")
    public Car car() {
    return car;
    }
    }

3、自动配置原理入门

3.1、引导加载自动配置类

1
2
3
4
5
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication { }
3.1.1、@SpringBootConfiguration

查看@SpringBootConfiguration注解的源码,发现里面有@Configuration(标注配置类的注解)这个注解。代表当前是一个配置类

3.1.2、@ComponentScan

指定扫描哪些包,具体使用可以看Spring注解驱动开发的笔记

3.1.3、@EnableAutoConfiguration
  • @EnableAutoConfiguration注解源码:

    1
    2
    3
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration { }
- @AutoConfigurationPackage(自动配置包)

    
1
2
3
4
5
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage { }

//该注解利用register给容器中添加一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。
- @Import(AutoConfigurationImportSelector.class)

3.2、按需开启自动配置项

3.3、修改默认配置

==修改application.properties配置文件==

3.4、最佳实践

4、开发小技巧

4.1、Lombok

  • 第一步:在idea中下载Lombok插件

  • 第二步:在Maven中添加依赖

    1
    2
    3
    4
    5
    <!--简化开发,快速生成get和set方法,不需要写版本号,SpringBoot已经管理了该包的版本-->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    </dependency>

    Lombok常用注解

4.2、dev-tools

1
2
3
4
5
6
7
<!--devtools热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope> <!-- 需要设置为true,否则不生效-->
</dependency>

项目或者页面修改以后:Ctrl+F9;

还可以使用 JRebel 插件,该插件在系统用户为中文的时候不能使用

4.3、Spring Initailizr(项目初始化向导)

使用idea的项目初始化向导,能选择我们需要的开发场景、自动依赖引入、自动创建项目结构、自动编写好主配置类

  • 使用初始化向导创建项目

    初始化向导

  • 项目目录结构

    项目目录结构

SpringBoot-核心功能

一、配置文件

==具体的实现看1.2.4实例==

1、文件类型

1.1、properties

和以前的properties配置文件的写法一样

1.2、yaml

1.2.1、简介

YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:”Yet Another Markup Language”(仍是一种标记语言)。

yaml文件非常适合来做以数据中心的配置文件

1.2.2、基本语法
  • key: value;kv之间有空格
  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • ‘#’表示注释
  • 字符串无需加引号,如果要加,’’与””表示字符串内容 会被 转义/不转义
1.2.3、数据类型

==: 后面需要有空格==

  • 字面量:单个的、不可再分的值。date、boolean、string、number、null

    1
    k: v
  • 对象:键值对的集合。map、hash、set、object

    1
    2
    3
    4
    5
    6
    行内写法:  k: {k1:v1,k2:v2,k3:v3}
    #或
    k:
    k1: v1
    k2: v2
    k3: v3
  • 数组:一组按次序排列的值。array、list、queue

    1
    2
    3
    4
    5
    6
    行内写法:  k: [v1,v2,v3]
    #或者
    k:
    - v1
    - v2
    - v3
1.2.4、实例
  • JavaBean环境

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @ConfigurationProperties(prefix = "person")
    @Component
    @Data
    public class Person {
    private String username;
    private Boolean boss;
    private Date birth;
    private Integer age;
    private String[] interests;
    private List<String> animal;
    private Map<String, Object> score;
    private Set<Double> salaries;
    private Pet pet;
    private List<Pet> pets;
    private Map<String, List<Pet>> allPets;
    }
    // ==================================================================
    public class Pet {
    private String name;
    private Double weight;
    }
  • yaml文件

    • String、Boolean、Integer、Date 等类型的数据

      1
      2
      3
      4
      5
      6
      7
      8
      #最前面加一个person,代表下面所有的变量将会加上person这个前缀
      person:
      #String类型写法
      username: 韩信
      #Boolean类型
      boss: true
      #日期类型
      birth: 2021/2/2
    • 数组、List<基本类型和包装类型>、Set< 基本类型和包装类型 >

      1
      2
      3
      4
      5
      6
      7
      8
      #最前面加一个person,代表下面所有的变量将会加上person这个前缀
      person:
      #方式一:
      interests: [篮球,足球]
      #方式二:通过 - 来区分数据,每个 - 都代表一个值
      interests:
      - 羽毛球
      - 乒乓球
    • Map集合:Map< 基本类型等包装类型 , 基本类型等包装类型 >

      1
      2
      3
      4
      5
      6
      7
      8
      9
      #最前面加一个person,代表下面所有的变量将会加上person这个前缀
      person:
      #Map集合:Map<String, 基本类型等包装类型>
      #Map集合写法一:JSON格式写法
      score: {english: 70 , math: 90}
      #Map集合写法二:一行一个 key: value
      score:
      english: 60
      math: 80
    • 对象类型(JavaBean)

      1
      2
      3
      4
      5
      #最前面加一个person,代表下面所有的变量将会加上person这个前缀
      person:
      pet:
      属性:
      属性:
    • List< JavaBean >

      1
      2
      3
      4
      5
      6
      7
      8
      #最前面加一个person,代表下面所有的变量将会加上person这个前缀
      person:
      #一个 - 代表一个JavaBean的数据
      pets:
      - name: 猫1
      weight: 99.99
      - name: 猫2
      weight: 77.99
    • 复杂的Map集合:Map< String , List< JavaBean >>

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      #最前面加一个person,代表下面所有的变量将会加上person这个前缀
      person:
      allPets:
      sick:
      #写法方式一
      - { name: 生病的猫1,weight: 88.88 }
      #写法方式二
      - name: 生病的猫2
      weight: 989.89
      health:
      - { name: 健康的猫1,weight: 9999.9999 }

1.3、编写yaml配置文件提示问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 编写yaml配置文件提示的包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<build>
<plugins>
<!--Maven打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<!--不打包:spring-boot-configuration-processor,编写配置文件提示-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<proc>none</proc>
</configuration>
</plugin>
</plugins>
</build>

1.4、注意

==在yaml配置文件中编写的配置信息,在SpringBoot的properties配置文件中也能配置,如果两者对同一个属性同时进行配置,这时候SpringBoot的主配置文件application.properties配置文件中的会生效,约定大于配置==

==properties文件和yaml文件的优先级:properties > yaml文件==

二、web开发

web开发

1、SpringMVC自动配置概览

Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)

The auto-configuration adds the following features on top of Spring’s defaults:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.

    • 内容协商视图解析器和BeanName视图解析器
  • Support for serving static resources, including support for WebJars (covered later in this document)).

    • 静态资源(包括webjars)
  • Automatic registration of Converter, GenericConverter, and Formatter beans.

    • 自动注册 Converter,GenericConverter,Formatter
  • Support for HttpMessageConverters (covered later in this document).

    • 支持 HttpMessageConverters (后来我们配合内容协商理解原理)
  • Automatic registration of MessageCodesResolver (covered later in this document).

    • 自动注册 MessageCodesResolver (国际化用)
  • Static index.html support.

    • 静态index.html 页支持
  • Custom Favicon support (covered later in this document).

    • 自定义 Favicon
  • Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

    • 自动使用 ConfigurableWebBindingInitializer ,(DataBinder负责将请求数据绑定到JavaBean上)

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

不用@EnableWebMvc注解。使用 @Configuration + WebMvcConfigurer 自定义规则

If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.

声明 WebMvcRegistrations 改变默认底层组件

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.

使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC

2、简单功能分析

2.1、静态资源访问

2.1、静态资源默认目录

当静态资源的目录为默认目录的时候,此时静态只有放在默认路径的默认的几个文件夹下的静态资源才能访问的到

静态资源内容文档:

https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-static-content

只要静态资源放在类路径下: ( /static or /public or /resources or /META-INF/resources)

访问 : 当前项目根路径/ + 静态资源名 ,因为SpringBoot默认情况下,资源映射到/** ,但是您可以使用spring.mvc.static-path-pattern属性对其进行调整

例如,将所有资源重新定位到以下位置/resources/**可以实现:

1
2
3
spring:
mvc:
static-path-pattern: /resources/**

==此时如果静态资源的名字和请求的Conteroller路径名一样此时,SpringBoot会先去找Controller看能不能处理,不能处理的所有请求又都交给静态资源处理器。静态资源会去指定的目录下去寻找,如果静态资源也没有的话报404==

2.2、静态资源访问前缀

SpringBoot默认访问静态资源是没有前缀的,默认为:当前项目根路径/ + 静态资源名

设置访问静态资源需要加上前缀

1
2
3
4
spring:
mvc:
static-path-pattern: /static/**
#此时访问静态资源的路径:当前项目根路径/ + static + 静态资源名

==此时在配置文件中配置了访问静态资源需要加上前缀了,此时访问路径为:当前项目根路径/ + 前缀名 + 静态资源名==

2.3、修改静态资源的默认目录

SpringBoot中默认的资源目录为,放在Maven的resource资源文件夹下面: ( /static or /public or /resources or /META-INF/resources)

==当四个目录下都有同样的资源时,优先级:resources > static > public > /META-INF/resources==

如果,在开发的时候不想文件放在SpringBoot指定的文件夹下面,此时可以使用spring.web.resources.static-locations来配置静态资源存放的目录,如果使用了spring.web.resources.static-locations来修改默认的目录,那么,此时默认的目录中存放的静态资源将不会被访问到,只有自己指定的目录下的资源文件才能被访问到

  • 使用spring.web.resources.static-locations

    1
    2
    3
    4
    5
    spring:
    #设置静态资源存放的目录,可以配置多个目录
    web:
    resources:
    static-locations: [ classpath:/myfile/,classpath:/app/ ]
2.4、WebJars

导入打包好的前端文件:https://www.webjars.org/

WebJars是将一些js文件打包成为了jar包,这些文件是存放在webjars文件夹下面的,此时访问路径为:==当前项目根路径/ + webjars + 静态资源名==

  • 在Maven中引入jQuery文件

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.5.1</version>
    </dependency>
  • 导入打包的jQuery之后访问:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径

2.2、欢迎页支持

Spring Boot支持静态和模板欢迎页面。它首先index.html在配置的静态内容位置中查找文件。如果找不到,它将寻找一个index模板。如果找到任何一个,它将自动用作应用程序的欢迎页面。

欢迎页面就是在不加任何后缀就能访问到的页面。(例如:http://localhost:8080/就会直接访问到index.html页面)

  • 静态资源路径下 index.html

    • 可以配置静态资源路径
    • 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
  • 注意:如果配置了静态资源访问前缀则会失效

    1
    2
    3
    spring:
    # mvc:
    # static-path-pattern: /rel/** 这个会导致welcome page功能失效
  • controller能处理/index

2.3、自定义 Favicon

favicon.ico 放在静态资源目录下即可。

2,4、静态资源配置原理

  • SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)
  • SpringMVC功能的自动配置类 WebMvcAutoConfiguration,生效

==具体的配置需要去研究源码==

3、请求参数处理

3.1请求映射

3.1.1、rest使用与原理
  • @xxxMapping;
    • @GetMapping、@DeleteMapping、@UpdateMapping、PostMapping、等
  • Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)

    • 以前:每个请求都要像一个映射名字,如:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户

    • 现在:一个请求路径就可以处理四种请求方式 /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户

    • 核心Filter;HiddenHttpMethodFilter

      • 用法: 表单method=post,写一个隐藏域(设置 name= _method,value=Get / Update / Delete / 其他)

        1
        2
        3
        4
        5
        6
        @Bean
        @ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
        @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
        public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
        return new OrderedHiddenHttpMethodFilter();
        }
      • 扩展:如何把_method 这个名字换成我们自己喜欢的。写一个配置类,注册 HiddenHttpMethodFilter

        1
        2
        3
        4
        5
        6
        7
        8
        //自定义filter,想要将表单隐藏域中的_method改成其他的变量名,如_m、_myMethod、等
        //此时可以使用自定义的HiddenHttpMethodFilter过滤器
        @Bean
        public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        methodFilter.setMethodParam("_m");
        return methodFilter;
        }
      • SpringBoot中手动开启Rest功能

        1
        2
        3
        4
        5
        spring:
        mvc:
        hiddenmethod:
        filter:
        enabled: true #开启页面表单的Rest功能

Rest原理(表单提交要使用REST的时候)

  • 使用表单请求一个Rest请求,表单method=post,写一个隐藏域(设置 name= _method,value=Get / Update / Delete / 其他)

    1
    2
    3
    4
    5
    <form action="/user" method = "post">
    <!-- 使用隐藏域来保存什么方式请求 -->
    <input type="hidden" name="_method" value="delete / get / update"/>
    <input type="submit" value="点击提交一个delete的请求"/>
    </form>
  • 表单提交会带上_method=PUT

  • 请求过来会被HiddenHttpMethodFilter拦截
    • 请求是否正常,并且是POST
      • 获取到_method的值。
      • 兼容以下请求;PUT.DELETE.PATCH
      • 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
      • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
      • HiddenHttpMethodFilter的具体作用:https://blog.csdn.net/zhizhengguan/article/details/102975945

Rest使用客户端工具,

  • 如PostMan、ApiPost是直接发送Put、delete等方式请求,无需Filter。

    1
    2
    3
    4
    5
    spring:
    mvc:
    hiddenmethod:
    filter:
    enabled: true #开启页面表单的Rest功能
3.1.2、自定义Rest拦截器

当我们想把_method 这个名字换成我们自己喜欢的。此时可以写一个配置类,注册 HiddenHttpMethodFilter

  • 注册一个HiddenHttpMethodFilter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Configuration
    public class MyConfig {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
    HiddenHttpMethodFilter filter = new HiddenHttpMethodFilter();
    //配置自定义的隐藏域名字,不再使用_method作为参数来提交
    filter.setMethodParam("_myMethod");
    return filter;
    }
    }
  • 表单请求

    1
    2
    3
    4
    <form action="/user" method="post">
    <input type="hidden" name="_myMethod" value="delete">
    <input type="submit" value="点击发送一个delete请求">
    </form>
  • controller

    1
    2
    3
    4
    5
    6
    7
    //delete请求写法一:
    //@RequestMapping(value = "/user", method = RequestMethod.DELETE)
    //delete请求写法二:
    @DeleteMapping("/user")
    public String deleteUser() {
    return "DELETE-张三";
    }
3.1.3、请求映射原理

去研究源码

3.2、普通参数与基本注解

3.2.1、注解

@PathVariable、@RequestHeader、@RequestParam、@CookieValue、@RequestBody、@RequestAttribute、@、@ModelAttribute、@MatrixVariable

==@requestBody这个标签在post 、put 方法中用于接收json格式的数据==

>
>

@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);

GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。在后端的同一个接收方法里,

@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。

  • @PathVariable:获取Rest请求中的参数

    • 用法:【请求地址:/test1/1/张三 】

      • 获取指定的@PathVariable(“变量名”),如果@PathVariable中没有指定属性名字,会根据绑定的参数的名字来寻找值,如果@PathVariable注解中传入了值,会根据值传入的名字来寻找对应的值

        1
        2
        @RequestMapping("/test1/{id}/{name}")
        public Map<String, Object> test1(@PathVariable String id, @PathVariable("name") String name1) { }
      • @PathVariable注解获取全部的值,不传入值,再绑定一个Map集合,此时会将全部的值封装为一个Map集合

        1
        2
        @RequestMapping("/test1/{id}/{name}")
        public Map<String, Object> test1(@PathVariable Map<String , Object> map) { }
  • @RequestHeader:获取请求头

    • 用法:

      • 获取指定的请求头:

        1
        public String test1(@RequestHeader("Host") String host) {    }
      • 获取全部请求头:

        1
        public Map<String, Object> test1(@RequestHeader Map<String, Object> map) {    }
  • @RequestAttribute 和 @SessionAttribute

    • 用法:在使用这两个参数的时候最好传入一个属性:required = false,如果不将该属性的值设置为False,当取值的时候没有值会报错,而将该属性设置为false之后,就算没有存值取出来会是null,而不是报错

      • 存值

        1
        2
        3
        4
        5
        @RequestMapping("/test1")
        public String test1(HttpServletRequest request) {
        request.getSession().setAttribute("password", "12345");
        return "forward:/req/test2";
        }
      • 取值:

        1
        2
        3
        4
        5
        6
        7
        @RequestMapping("/test2")
        @ResponseBody
        public String test2(@RequestAttribute(required = false) String name,
        @SessionAttribute(value = "password", required = false) Object password) {

        return "name = " + name + " ===== password = " + password;
        }
  • 其他几个注解使用差不多,上网查找加参照使用
3.2.2、Servlet API

WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId

ServletRequestMethodArgumentResolver 以上的部分参数

        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
(Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}
3.2.3、复杂参数

MapModel(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

1
2
Map<String,Object> map,  Model model, HttpServletRequest request 都是可以给request域中放数据,
request.getAttribute();
3.2.4、自定义对象参数

可以自动类型转换与格式化,可以级联封装。

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
/**
* 姓名: <input name="userName"/> <br/>
* 年龄: <input name="age"/> <br/>
* 生日: <input name="birth"/> <br/>
* 宠物姓名:<input name="pet.name"/><br/>
* 宠物年龄:<input name="pet.age"/>
*/
@Data
public class Person {

private String userName;
private Integer age;
private Date birth;
private Pet pet;

}

@Data
public class Pet {

private String name;
private String age;

}

result

3.3、POJO封装过程

  • 研究源码:ServletModelAttributeMethodProcessor

3.4、参数处理原理

具体笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#6SNoJ

4、数据响应与内容协商

具体笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#ltqcb

5、视图解析与模板引擎

视图解析:SpringBoot在处理完请求以后想要跳转到某个页面的过程,SpringBoot默认不支持JSP,需要引入第三方模板引擎技术实现页面渲染。

5.1、视图解析

视图解析与原理流程

5.1.1、视图解析原理流程

具体笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#ITRrl

5.2、模板引擎-Thymeleaf

5.2.1、Thymeleaf简介

Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text.

Thymeleaf是一个现代化、服务端Java模板引擎

5.2.2、基本语法
5.2.1.1、表达式
表达式名字 语法 用途
变量取值 ${…} 获取请求域、session域、对象等值
选择变量 *{…} 获取上下文对象值
消息 #{…} 获取国际化等值
链接 @{…} 生成链接
片段表达式 ~{…} jsp:include 作用,引入公共页面片段
5.2.1.2、字面量

文本值: ‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false

空值: null

变量: one,two,…. 变量不能有空格

5.2.1.3、文本操作

字符串拼接: +

变量替换: |The name is ${name}|

5.2.1.4、数学运算

运算符: + , - , * , / , %

5.2.1.5、布尔运算

运算符: and , or

一元运算: ! , not

5.2.1.6、比较运算

比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )

5.2.1.7、条件运算

If-then: (if) ? (then)

If-then-else: (if) ? (then) : (else)

Default: (value) ?: (defaultvalue)

5.2.1.8、特殊操作

无操作: _

5.2.3、设置属性值-th:attr

设置单个值

1
2
3
4
5
6
<form action="subscribe.html" th:attr="action=@{/subscribe}">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
</fieldset>
</form>

设置多个值

1
<img src="../../images/gtvglogo.webp"  th:attr="src=@{/images/gtvglogo.webp},title=#{logo},alt=#{logo}" />

以上两个的代替写法 th:xxxx

1
2
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
<form action="subscribe.html" th:action="@{/subscribe}">

所有h5兼容的标签写法:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes

5.2.4、迭代
1
2
3
4
5
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
1
2
3
4
5
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
5.2.5、条件运算
1
2
3
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
1
2
3
4
5
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>
5.2.6、属性优先级
Order Feature Attributes
1 Fragment inclusion th:include th:replace
2 Fragment iteration th:each
3 Conditional evaluation th:if th:unless th:switch th:case
4 Local variable definition th:object th:with
5 General attribute modification th:attr th:attrprepend th:attrappend
6 Specific attribute modification th:value, th:href, th:src, ....
7 Text (tag body modification) th:text th:utext
8 Fragment specification th:fragment
9 Fragment removal th:remove

5.3、Thymeleaf使用

5.3.1、引入Thymeleaf
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
5.3.2、自动配置thymeleaf

在SpringBoot自动配置好了Thymeleaf,不需要自己再配置,直接使用

1
2
3
4
5
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class })
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class })
public class ThymeleafAutoConfiguration { }

自动配好的策略

  • 1、所有thymeleaf的配置值都在 ThymeleafProperties
  • 2、配置好了 SpringTemplateEngine
  • 3、配好了 ThymeleafViewResolver
  • 4、我们只需要直接开发页面
1
2
3
4
//默认前缀,页面放到templates文件夹中
public static final String DEFAULT_PREFIX = "classpath:/templates/";
//所有的thymeleaf默认都都会跳转到一个 xxx.html
public static final String DEFAULT_SUFFIX = ".html"; //xxx.html
5.3.3、页面开发

templates中的页面只能通过控制器跳转进入,不能直接访问到,static文件夹下的可以直接访问到

  • 控制器代码

    1
    2
    3
    4
    5
    6
    7
    8
    @GetMapping("/toPage")
    public String toPage(Model model) {
    //Model中的数据会放到请求与中,相当于request.setAttribute("aa",aa)
    model.addAttribute("msg","存入的数据");
    model.addAttribute("url","https://home.code-nav.cn/");
    //由于Thymeleaf自动配置 了前缀和后缀,所以这里只需要输入文件的名称就行了
    return "success";
    }
  • 页面:需要再html标签中导入名称空间:xmlns:th=”http://www.thymeleaf.org

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    </head>
    <body>
    <!--th:text:用来修改标签中的值-->
    <h1 th:text="${msg}">哈哈</h1>
    <!-- 这里可以测试一下 ${} 和 @{} 取值的效果 -->
    <a th:href="${url}">去编程导航页面</a>
    <a th:href="@{url}">去编程导航页面2</a>
    </body>
    </html>

5.4、构建后台管理系统

5.4.1、项目创建

引入:thymeleaf、web-starter、devtools、lombok

5.4.2、静态资源处理

对于静态资源SpringBoot已经自动配置好了,只需要把所有静态资源放到 static 文件夹下

5.4.3、路径构建

th:action=”@{/login}”

5.4.4、模板抽取
  • th:insert 如同插入的字面意思,将指定的代码片段插入主标签内
  • th:replace如同替换的字面意思,将主标签替换为指定的代码片段
  • th:include (3.0版本后已不推荐使用) 类似于th:insert, 不同的是在插入的时候不带代码片段的标签,只插入代码
  • 公共部分:

    1
    2
    3
    <footer th:fragment="copy">
    这是公共部分的值
    </footer>
  • 三种引入方式:

    1
    2
    3
    4
    5
    <body>
    <div th:insert="footer :: copy"></div>
    <div th:replace="footer :: copy"></div>
    <div th:include="footer :: copy"></div>
    </body>
  • 三种引入方式的效果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <body>

    <div>
    <footer>
    这是公共部分的值
    </footer>
    </div>

    <footer>
    这是公共部分的值
    </footer>

    <div>
    这是公共部分的值
    </div>

    </body>
5.4.5、页面跳转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@PostMapping("/login")
public String main(User user, HttpSession session, Model model){

if(StringUtils.hasLength(user.getUserName()) && "123456".equals(user.getPassword())){
//把登陆成功的用户保存起来
session.setAttribute("loginUser",user);
//登录成功重定向到main.html; 重定向防止表单重复提交
return "redirect:/main.html";
}else {
model.addAttribute("msg","账号密码错误");
//回到登录页面
return "login";
}

}
5.4.6、数据渲染
  • 后台传入数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @GetMapping("/basic_table")
    public String basic_table(Model model) {
    model.addAttribute("user", Arrays.asList(
    new User("张三", "1"),
    new User("马尔扎哈", "2"),
    new User("李白", "3"),
    new User("韩信", "4"),
    new User("澜", "5"),
    new User("蔡文姬", "6")
    ));
    return "table/basic_table";
    }
  • 前端遍历数据 :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <table class="display table table-bordered" id="hidden-table-info">
    <thead>
    <tr>
    <th>#</th>
    <th>用户名</th>
    <th>密码</th>
    </tr>
    </thead>
    <tbody>
    <tr class="gradeX" th:each="user,stats:${users}">
    <td th:text="${stats.count}">Trident</td>
    <td th:text="${user.userName}">Internet</td>
    <td >[[${user.password}]]</td>
    </tr>
    </tbody>
    </table>

6、拦截器

==配置了拦截器的执行顺序:preHandle(拦截器中的方法) ==>> 控制器中的方法 ==>> postHandle(拦截器中的方法) ==>> afterCompletion(拦截器中的方法) ==>> 可能会由于浏览器的原因会继续执行以前前面四步流程 ==>> 进入浏览器==

6.1、配置自定义拦截器

实现HandlerInterceptor 接口

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
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("1");
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}", requestURI);

Object loginUser = request.getSession().getAttribute("loginUser");
if (loginUser != null) {
return true;
}
request.setAttribute("msg", "请先登录");
request.getRequestDispatcher("/").forward(request, response);
return false;
}

/**
* 目标方法执行完成以后
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("2");
log.info("postHandle执行{}", modelAndView);
}

/**
* 页面渲染以后
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("3");
log.info("afterCompletion执行异常{}", ex);
}
}

6.2、注册自定义拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 1、编写一个拦截器实现HandlerInterceptor接口
* 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有请求都被拦截包括静态资源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求
}
}

6.3、拦截器原理

1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】

2、先来顺序执行 所有拦截器的 preHandle方法

  • 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
  • 2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;

3、如果任何一个拦截器返回false。直接跳出不执行目标方法

4、所有拦截器都返回True。执行目标方法

5、倒序执行所有拦截器的postHandle方法。

6、前面的步骤有任何异常都会直接倒序触发 afterCompletion

7、页面成功渲染完成以后,也会倒序触发 afterCompletion

拦截器执行流程

7、文件上传

1、表单页面

1
<form method="post" action="/upload" enctype="multipart/form-data"> ....... </form>

2、文件上传代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* MultipartFile自动封装上传过来的文件,如果是多文件上传,可以定义一个MultipartFile数组,SpringBoot会将文件封装成为一个数组
*/
@PostMapping("/upload")
public String upload(@RequestPart("photo") MultipartFile photo,
@RequestPart("photos") MultipartFile[] photos) throws IOException {

//将文件写入磁盘路径
if (!photo.isEmpty()) {
photo.transferTo(new File(realPath + photo.getOriginalFilename()));
}

//将文件写入磁盘路径
if (photos.length > 0) {
//多文件上传需要将上传的文件依次遍历存入磁盘中
for (MultipartFile item : photos) {
item.transferTo(new File(realPath + item.getOriginalFilename()));
}
}

return "main";
}

3、修改文件上传配置文件

再application.properties中修改文件上传的相关数据:

1
2
3
4
5
6
#单个文件的大小设置
spring.servlet.multipart.max-file-size=10MB

#总共请求的文件的大小的设置,
#例如:当一次文件上传有单文件上传和多文件上传的时候,单文件上传中的文件的大小和多文件上传的文件的大小的总和 = 请求的大小
spring.servlet.multipart.max-request-size=100MB

4、文件上传自动配置原理

文件上传自动配置原理笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#h9I1a

8、异常处理

笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#6ezTG

想要配置错误页面,需要再template文件夹下设置一个error文件夹,专门用来放错误页面,页面中放的文件的 名字就会是错误信息的状态码,例如:想要配置一个404错误的页面,只需要在template文件夹下面的error文件夹中写一个404.html文件就行了,当发生404错误的时候就会自动跳转到404页面,例如:写一个5xx.html的文件,只要出现以5开头的错误的状态码都会跳转到5xx.html页面中

8.1、错误处理

8.1.1、默认规则
  • 默认情况下,Spring Boot提供/error处理所有错误的映射
  • 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
8.1.2、定制错误处理逻辑
8.1.3、异常处理自动配置原理
8.1.4、异常处理步骤流程

9、Web原生组件注入(Servlet、Filter、Listener)

==如果想要将Servlet、Filter、Listener都能在SpringBoot中使用,需要在SpringBoot的启动类中使用@ServletComponentScan注解来扫描进去==

9.1、使用Servlet API

  • 注册Servlet三大组件
    • @ServletComponentScan(basePackages = “com.atguigu.admin”) :指定原生Servlet组件都放在那里
  • 注册Serlvet三大组件
    • @WebServlet(urlPatterns = “/my”):效果:直接响应,没有经过Spring的拦截器?
    • @WebFilter(urlPatterns={“/css/*“,“/images/*“})
    • @WebListener

9.2、使用@ServletComponentScan注册Servlet组件

使用@ServletComponentScan扫描Servlet组件

  • 假如三大组件都放在:con.lf.servlet这个包下,使用@ServletComponentScan扫描Servlet三大组件

    1
    2
    3
    4
    5
    6
    7
    8
    //想要将自己写的Servlet能被访问到,需要在SpringBoot的启动类中加上:@ServletComponentScan,传入Servlet组件的路径
    @ServletComponentScan(basePackages = "com.lf.servlet")
    @SpringBootApplication
    public class Springboot03WebAdminApplication {
    public static void main(String[] args) {
    SpringApplication.run(Springboot03WebAdminApplication.class, args);
    }
    }
  • @WebServlet:

    1
    2
    3
    4
    5
    6
    7
    8
    //编写一个类继承HttpServlet类
    @WebServlet(urlPatterns = "/my")
    public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("这是原生继承HttpServlet的方法");
    }
    }
  • @WebFilter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Slf4j
    @WebFilter(filterName = "MyFilter",urlPatterns = {"/css/*,"})
    public class MyFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {
    log.info("Filter初始化");
    }

    public void destroy() {
    log.info("Filter销毁");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
    log.info("Filter工作");
    chain.doFilter(request, response);
    }
    }
  • WebListener

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Slf4j
    //@WebListener
    public class MySwervletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
    log.info("MySwervletContextListener监听到项目初始化完成");
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    log.info("MySwervletContextListener监听到项目销毁");
    }
    }

9.3、使用RegistrationBean注册Servlet组件

ServletRegistrationBean:注册Servlet

FilterRegistrationBean:注册Filter

ServletListenerRegistrationBean:注册Listener

  • 写好三大组件:不需要在组件上写@WebServlet、@WebFilter、@WebListener,只需要写一个类实现或者继承对应的接口或者类就行了

    1
    2
    3
    4
    5
    public class MyServlet extends HttpServlet {  }

    public class MyFilter implements Filter { }

    public class MySwervletContextListener implements ServletContextListener { }
  • 写一个配置类,将组件注册:

    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
    @Configuration
    public class MyRegisterConfig {
    //注册Servlet
    @Bean
    public ServletRegistrationBean<MyServlet> servletRegister() {
    return new ServletRegistrationBean<>(new MyServlet(), "/my", "/my2");
    }

    //注册Filter
    @Bean
    public FilterRegistrationBean<MyFilter> filterRegister() {
    //可以在创建FilterRegistrationBean的时候往构造函数中传入一个ServletRegistrationBean,这时候就会只拦截该servlet
    FilterRegistrationBean<MyFilter> registrationBean2 =
    new FilterRegistrationBean<>(new MyFilter(),servletRegister());

    FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>(new MyFilter());
    //拦截规则1:传入一个集合,集合中存的是拦截规则
    registrationBean.setUrlPatterns(Arrays.asList("/css/*", "/js/*"));
    return registrationBean;
    }

    //注册Listener
    @Bean
    public ServletListenerRegistrationBean<MyListener> listenerRegister() {
    return new ServletListenerRegistrationBean<MyListener>(new MyListener());
    }
    }

10、嵌入式Servlet容器

笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#ED2gi

11、定制化原理

笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#cQant

本章可以全面接管SpringMVC,比如,静态资源过滤,视图解析器,等等都需要自己写,没有把握别使用。还可增加心功能,修改旧功能,替代原有的自动化配置好的组件等

>
>

一般想要定制一些SpringBoot配置好的,只需要写一个配置类实现WebMvcConfigurer即可定义web功能,然后使用@Bean注解往SpringBoot添加扩展一些新的功能,没有必要全面接管SpringMVC来全部自定义配置

11.1、定制化的常见方式

笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#eSDgT

11.2、原理分析

笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#GH9jB

三、数据访问(操作数据库)

1、SQL

1.1、数据源的自动配置-HikariDataSource

1.1.1、导入JDBC场景
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

JDBC场景

通过上图可以看到没有数据库驱动,因为官方不知道我们使用的是什么数据库,MySQL、Oracle

还有就是数据库驱动的版本和我们装的数据库版本是否一致等问题,这时需要我们自己导入数据库连接驱动

  • MySQL的数据库连接驱动

    1
    2
    3
    4
    5
    6
    <!--当SpringBoot提供的版本和装的版本一致的时候可以不需要写版本。-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
    </dependency>
1.1.2、分析自动配置
1、自动配置的类
  • DataSourceAutoConfiguration : 数据源的自动配置

    • 修改数据源相关的配置:spring.datasource
    • 数据库连接池的配置,是自己容器中没有DataSource才自动配置的
    • 底层配置好的连接池是:HikariDataSource

      1
      2
      3
      4
      5
      6
      7
      @Configuration(proxyBeanMethods = false)
      @Conditional(PooledDataSourceCondition.class)
      @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
      @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
      DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
      DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
      protected static class PooledDataSourceConfiguration
  • DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置

  • JdbcTemplateAutoConfiguration: JdbcTemplate的自动配置,可以来对数据库进行crud

    • 可以修改这个配置项@ConfigurationProperties(prefix = “spring.jdbc”) 来修改JdbcTemplate
    • @Bean@Primary JdbcTemplate;容器中有这个组件
  • JndiDataSourceAutoConfiguration: jndi的自动配置

  • XADataSourceAutoConfiguration: 分布式事务相关的
3.1.3、修改配置项

修改连接地址,驱动地址,用户名和密码等信息

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/db_account
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
3.1.4、测试
1
2
3
4
5
6
7
8
9
@SpringBootTest
class SpringbootDataApplicationTests {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
void contextLoads() {
jdbcTemplate.queryForList("select *from employee").forEach(System.out::println);
}
}

1.2、使用Druid数据源

1.2.1、Druid的GitHub地址

https://github.com/alibaba/druid

1.2.2、SpringBoot整合Druid

SpringBoot整合第三方技术的两种方式

  • 自定义
  • 找status
1.2.3、自定义方式
1.2.3.1、创建数据源
1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
1.2.3.2、StatViewServlet

StatViewServlet的用途包括:

  • 提供监控信息展示的html页面
  • 提供监控信息的JSON API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 配置 druid的监控页功能
* @return
*/
@Bean
public ServletRegistrationBean statViewServlet(){
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");

registrationBean.addInitParameter("loginUsername","admin");
registrationBean.addInitParameter("loginPassword","123456");

return registrationBean;
}
1.2.3.3、StatFilter

用于统计监控信息;如SQL监控、URI监控

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* WebStatFilter 用于采集web-jdbc关联监控的数据。
*/
@Bean
public FilterRegistrationBean webStatFilter(){
WebStatFilter webStatFilter = new WebStatFilter();

FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.webp,*.webp,*.css,*.ico,/druid/*");

return filterRegistrationBean;
}
1.2.4、使用官方starter方式

官网地址:https://hub.fastgit.org/alibaba/druid/tree/master/druid-spring-boot-starter

1.2.4.1、引入druid-stater
1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
1.2.4.2、分析自动配置
  • 扩展配置项 spring.datasource.druid

  • DruidSpringAopConfiguration.class, 监控SpringBean的;配置项:spring.datasource.druid.aop-patterns

  • DruidStatViewServletConfiguration.class, 监控页的配置:spring.datasource.druid.stat-view-servlet;默认开启

  • DruidWebStatFilterConfiguration.class, web监控配置;spring.datasource.druid.web-stat-filter;默认开启

  • DruidFilterConfiguration.class}) 所有Druid自己filter的配置

    1
    2
    3
    4
    5
    6
    7
    8
    private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat";
    private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config";
    private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding";
    private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j";
    private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j";
    private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2";
    private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log";
    private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall";
1.2.4.3、配置示例

只需要将下面代码设置后,就可以直接使用Druid的监控页面了,访问路径为:http://localhost:8080/druid/

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
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver

druid:
aop-patterns: com.lf.* #springbean监控
filters: stat,wall,slf4j #所有开启的功能

stat-view-servlet: #监控页配置
enabled: true #设置开启监控页,监控页默认关闭
login-username: admin #进入监控页账号
login-password: admin #进入监控页密码
resetEnable: false #设置重置按钮是否有效

web-stat-filter: #web监控
enabled: true #设置开启web监控,web监控默认关闭
urlPattern: /* #监控路径配置
exclusions: '*.js,*.gif,*.webp,*.webp,*.css,*.ico,/druid/*' #设置排除哪些,该选项有默认值,可以不填

filter:
stat: #sql监控
enabled: true #设置开启sql监控,sql监控默认关闭
slow-sql-millis: 1000 #配置超过1000毫秒的都是慢SQL
logSlowSql: true #是否记录慢SQL
wall: #防火墙
enabled: true #设置开启防火墙,防火墙默认关闭
config:
drop@-table-allow: false #设置是否允许删除表

1.3、整合MyBatis

镜像官网:https://hub.fastgit.org/mybatis

官网:https://github.com/mybatis

  • 引入MyBatis的starter

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
    </dependency>
1.3.1、配置模式
  • 全局配置文件

  • SqlSessionFactory: 自动配置好了

  • SqlSession:自动配置了 SqlSessionTemplate 组合了SqlSession

  • @Import(AutoConfiguredMapperScannerRegistrar.class);

  • Mapper: 只要我们写的操作MyBatis的接口标注了 @Mapper 就会被自动扫描进来

    1
    2
    3
    @EnableConfigurationProperties(MybatisProperties.class)
    @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
    public class MybatisAutoConfiguration implements InitializingBean { }

可以修改配置文件中 以mybatis开始修改mybatis的配置:

1
2
3
4
# 配置mybatis规则
mybatis:
config-location: classpath:mybatis/mybatis-config.xml #全局配置文件位置
mapper-locations: classpath:mybatis/mapper/*.xml #sql映射文件位置

mybatis-config:

1
2
3
4
5
6
7
8
9
10
11
Mapper接口--->绑定Xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.admin.mapper.AccountMapper">
<!-- public Account getAcct(Long id); -->
<select id="getAcct" resultType="com.atguigu.admin.bean.Account">
select * from account_tbl where id=#{id}
</select>
</mapper>

配置 private Configuration configuration; mybatis.configuration下面的所有,就是相当于改mybatis全局配置文件中的值

1
2
3
4
5
6
7
8
# 配置mybatis规则
mybatis:
#config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
#注意:如果配置了mybatis.configuration就不能再引入MyBatis的核心配置文件,因为两者都是同一种东西,都引入会报错
# 可以不写全局;配置文件,所有全局配置文件的配置都放在configuration配置项中即可

SpringBoot整合MyBatis步骤:

  • 导入mybatis官方starter
  • 编写mapper接口,标注@Mapper注解。就是写一个接口,在接口上使用@Mapper注解
  • 编写sql映射文件并绑定mapper接口
  • 在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议;配置在mybatis.configuration,mybatis.configuration中配置的信息就相当于在MyBatis的全局配置文件中配置的信息
1.3.2、注解模式
1
2
3
4
5
6
7
8
9
10
11
12
@Mapper
public interface CityMapper {

@Insert("INSERT INTO city (name, state, country) VALUES(#{name}, #{state}, #{country})")
//开启主键自增长,且主键回填,设置主键为 id
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(City city);

@Select("SELECT id, name, state, country FROM city WHERE id = #{id}")
City findById(long id);

}
1.3.3、混合模式

注解版 + 配置版 = 混合版

1.3.4、总结
  • 引入mybatis-starter
  • 配置application.yaml中,指定mapper-location位置即可,不指定mapper-location就使用spring.configuration来替代mybatis的配置文件
  • 编写Mapper接口并标注@Mapper注解,也可以使用@MapperScan("Mapper接口位置")注解来扫描mapper接口的位置,接口就不需要@Mapper注解了
  • 简单方法直接注解方式,@Select,@Update ......
  • 复杂方法编写mapper.xml进行绑定映射,简单SQL可以用注解,复杂SQL还是写配置文件
  • 使用使用@MapperScan("Map接口位置") 简化开发后,其他的接口就可以不用标注@Mapper注解,但是最好还是给接口标上@Mapper注解

1.4、整合MyBatisPlus

1.4.1、整合MyBatisPlus
1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>版本号</version>
</dependency>
1.4.2、MP的自动配置

自动配置

  • MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对**mybatis-plus的定制**
  • SqlSessionFactory 自动配置好。底层是容器中默认的数据源
  • mapperLocations 自动配置好的。有默认值。**classpath*:/mapper/*/\.xml;任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下**
  • 容器中也自动配置好了 SqlSessionTemplate
  • @Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan(“com.atguigu.admin.mapper”) 批量扫描就行
1.4.3、优点:
  • 只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力

1.4.4、简单CRUD

Service的实现类可以实现一个超级Service类,ServiceImpl, T>,可以不需要调用xxxMapper接口,就可以实现一些简单的CRUD

xxxMapper接口可以实现一个BaseMapper< T >接口,里面封装了一些简单的CRUD

  • 使用

    1
    2
    3
    4
    5
    //Service的实现类实现ServiceImpl实现类,里面封装了一些简单的CRUD
    public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService { }

    //XxxMapper接口继承BaseMapper<T> 可以进行一些简单的CRUD
    public interface EmployeeMapper extends BaseMapper<Employee> { }

1.5、整合PageHelper插件

GitHub地址:

1.5.1、引入Starter
1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
1.5.2、简单使用:
  • 使用:(这里列举了两种使用方式)

    • PageHelper.offsetPage(参数1,参数2)==(参数一:从第几条数据开始查询,参数二:每页查询多少条数据)==
    • PageHelper.startPage(参数一, 参数二);==(参数一:从第几页开始查询。参数二:每页显示多少条数据)==
> **这里只介绍了两种方法的使用,其他方法的使用可以去PageHelper官网查询使用**

四、单元测试

引入SpringBoot-Test的Starter

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

1、Junit5变化

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。

JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行。

JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。

注意:

SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容junit4需要自行引入(不能使用junit4的功能 @Test)

JUnit 5’s Vintage Engine Removed from spring-boot-starter-test,如果需要继续兼容junit4需要自行引入vintage

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>

以前:

@SpringBootTest + @RunWith(SpringTest.class)

SpringBoot整合Junit以后。

  • 编写测试方法:@Test标注(注意需要使用junit5版本的注解)
  • Junit类具有Spring的功能,@Autowired、比如 @Transactional 标注测试方法,测试完成后自动回滚

2、JUnit5常用注解

Junit5官网:https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations

JUnit5的注解与JUnit4的注解有所变化

  • @Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试

  • @ParameterizedTest :表示方法是参数化测试,下方会有详细介绍

  • @RepeatedTest :表示方法可重复执行,下方会有详细介绍

  • @DisplayName :为测试类或者测试方法设置展示名称

  • @BeforeEach :表示在每个单元测试之前执行

  • @AfterEach :表示在每个单元测试之后执行

  • @BeforeAll :表示在所有单元测试之前执行

  • @AfterAll :表示在所有单元测试之后执行

  • @Tag :表示单元测试类别,类似于JUnit4中的@Categories

  • @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore

  • @Timeout :表示测试方法运行如果超过了指定时间将会返回错误

  • @ExtendWith :为测试类或测试方法提供扩展类引用

  • @RepeatedTest(5):传入一个数字,标注在测试方法上,表示这个方法执行几次

  • @Transactional:标注在ceshilei 或测试方法上,那么标注上该注解之后的增删改操作完成之后会数据回滚

3、断言(assertions)

断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。JUnit 5 内置的断言可以分成如下几个类别:

检查业务逻辑返回的数据是否合理。

所有的测试运行结束以后,会有一个详细的测试报告;

3.1、简单断言

方法 说明
assertEquals 判断两个对象或两个原始类型是否相等
assertNotEquals 判断两个对象或两个原始类型是否不相等
assertSame 判断两个对象引用是否指向同一个对象
assertNotSame 判断两个对象引用是否指向不同的对象
assertTrue 判断给定的布尔值是否为 true
assertFalse 判断给定的布尔值是否为 false
assertNull 判断给定的对象引用是否为 null
assertNotNull 判断给定的对象引用是否不为 null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
@DisplayName("simple assertion")
public void simple() {
assertEquals(3, 1 + 2, "simple math");
assertNotEquals(3, 1 + 1);

assertNotSame(new Object(), new Object());
Object obj = new Object();
assertSame(obj, obj);

assertFalse(1 > 2);
assertTrue(1 < 2);

assertNull(null);
assertNotNull(new Object());
}

3.2、数组断言

通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等

1
2
3
4
5
@Test
@DisplayName("array assertion")
public void array() {
assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}

3.3、组合断言

assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言

1
2
3
4
5
6
7
8
@Test
@DisplayName("assert all")
public void all() {
assertAll("Math",
() -> assertEquals(2, 1 + 1),
() -> assertTrue(1 > 0)
);
}

3.4、异常断言

在JUnit4时期,想要测试方法的异常情况时,需要用@Rule注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows() ,配合函数式编程就可以进行使用。

1
2
3
4
5
6
7
8
@Test
@DisplayName("异常测试")
public void exceptionTest() {
ArithmeticException exception = Assertions.assertThrows(
//扔出断言异常
ArithmeticException.class, () -> System.out.println(1 % 0));

}

3.5、超时断言

Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间

1
2
3
4
5
6
@Test
@DisplayName("超时测试")
public void timeoutTest() {
//如果测试方法时间超过1s将会异常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}

3.6、快速失败

通过 fail 方法直接使得测试失败

1
2
3
4
5
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}

4、前置条件

JUnit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@DisplayName("前置条件")
public class AssumptionsTest {
private final String environment = "DEV";

@Test
@DisplayName("simple")
public void simpleAssume() {
assumeTrue(Objects.equals(this.environment, "DEV"));
assumeFalse(() -> Objects.equals(this.environment, "PROD"));
}

@Test
@DisplayName("assume then do")
public void assumeThenDo() {
assumingThat(
Objects.equals(this.environment, "DEV"),
() -> System.out.println("In DEV")
);
}
}

assumeTrue 和 assumFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行终止。assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时,Executable 对象才会被执行;当条件不满足时,测试执行并不会终止。

5、嵌套测试

JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
@DisplayName("A stack")
class TestingAStackDemo {

Stack<Object> stack;

@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}

@Nested
@DisplayName("when new")
class WhenNew {

@BeforeEach
void createNewStack() {
stack = new Stack<>();
}

@Test
@DisplayName("is empty")
void isEmpty() {
assertTrue(stack.isEmpty());
}

@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
assertThrows(EmptyStackException.class, stack::pop);
}

@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
assertThrows(EmptyStackException.class, stack::peek);
}

@Nested
@DisplayName("after pushing an element")
class AfterPushing {

String anElement = "an element";

@BeforeEach
void pushAnElement() {
stack.push(anElement);
}

@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
assertFalse(stack.isEmpty());
}

@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}

@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}

6、参数化测试

参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。

利用@ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。

@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型

@NullSource: 表示为参数化测试提供一个null的入参

@EnumSource: 表示为参数化测试提供一个枚举入参

@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参

@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)

当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。让我真正感到他的强大之处的地方在于他可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现ArgumentsProvider接口,任何外部文件都可以作为它的入参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("参数化测试1")
public void parameterizedTest1(String string) {
System.out.println(string);
Assertions.assertTrue(StringUtils.isNotBlank(string));
}


@ParameterizedTest
@MethodSource("method") //指定方法名
@DisplayName("方法来源参数")
public void testWithExplicitLocalMethodSource(String name) {
System.out.println(name);
Assertions.assertNotNull(name);
}

static Stream<String> method() {
return Stream.of("apple", "banana");
}

7、Junit4迁移到Junit5指南

在进行迁移的时候需要注意如下的变化:

  • 注解在 org.junit.jupiter.api 包中,断言在 org.junit.jupiter.api.Assertions 类中,前置条件在 org.junit.jupiter.api.Assumptions 类中。
  • 把@Before 和@After 替换成@BeforeEach 和@AfterEach。
  • 把@BeforeClass 和@AfterClass 替换成@BeforeAll 和@AfterAll。
  • 把@Ignore 替换成@Disabled。
  • 把@Category 替换成@Tag。
  • 把@RunWith、@Rule 和@ClassRule 替换成@ExtendWith。

五、指标监控

1、SpringBoot Actuator

1.1、简介

未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。

  • 引入SpringBoot Actuator

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

1.2、1.x与2.x的不同

SpringBootActuator-1.x与2.x的区别

1.3、如何使用

1.4、可视化

GitHub地址:https://github.com/codecentric/spring-boot-admin

  • 第一步:专门写一个服务器用来监控其他客户端服务器信息:

    • 引入Starter

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

      <!--SpringBoot指标监控可视化-->
      <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-starter-server</artifactId>
      <version>2.3.1</version>
      </dependency>
    • 修改端口号避免重复:

      1
      server.port=8888
  • 第二步:将客户端给管理员端监控

    • 引入starter

      1
      2
      3
      4
      5
      6
      <!--客户端的client-stater-->
      <dependency>
      <groupId>de.codecentric</groupId>
      <artifactId>spring-boot-admin-starter-client</artifactId>
      <version>2.3.1</version>
      </dependency>
    • 修改一些配置信息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      management:
      endpoints:
      enabled-by-default: true #暴露所有端点信息
      web:
      exposure:
      include: '*' #以web方式暴露

      spring:
      boot:
      admin:
      client:
      url: http://localhost:8888 #admin端的地址
      instance:
      prefer-ip: true #使用IP注册
      application:
      name: springboot_04_data #一般将名字改为项目名称

其他具体使用查看官方文档

2、Actuator Endpoint

其他指标监控笔记:https://www.yuque.com/atguigu/springboot/sgpvgn#KBTP3

2.1、最常使用的端点

ID 描述
auditevents 暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件
beans 显示应用程序中所有Spring Bean的完整列表。
caches 暴露可用的缓存。
conditions 显示自动配置的所有条件信息,包括匹配或不匹配的原因。
configprops 显示所有@ConfigurationProperties
env 暴露Spring的属性ConfigurableEnvironment
flyway 显示已应用的所有Flyway数据库迁移。 需要一个或多个Flyway组件。
health 显示应用程序运行状况信息。
httptrace 显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository组件。
info 显示应用程序信息。
integrationgraph 显示Spring integrationgraph 。需要依赖spring-integration-core
loggers 显示和修改应用程序中日志的配置。
liquibase 显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase组件。
metrics 显示当前应用程序的“指标”信息。
mappings 显示所有@RequestMapping路径列表。
scheduledtasks 显示应用程序中的计划任务。
sessions 允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。
shutdown 使应用程序正常关闭。默认禁用。
startup 显示由ApplicationStartup收集的启动步骤数据。需要使用
startup 显示由ApplicationStartup收集的启动步骤数据。需要使用SpringApplication进行配置BufferingApplicationStartup
threaddump 执行线程转储。

还想了解其他信息可以去

官网:https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready

笔记:https://www.yuque.com/atguigu/springboot/sgpvgn

六、原理解析

跨域问题

一、在SpringBoot中解决跨域问题

写一个配置类实现WebMvcConfigurer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.southwind.configuration;

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

@Configuration
public class CrossConfiguration implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}

在控制器上写一个@CrossOrigin用来解决跨域问题

二、跨域的时候绑定Cookie出现的问题

由于前后端分离项目中,请求后端是默认不带Cookie的信息的,所以,每次请求后端的session的值是不一样的。后端对于是否重新创建Session是取决与Cookie中是否有 JSESSIONID,所以为了保证session不是每次请求都是重新创建的,下面分别有前端和后端的代码,两端代码都要写

  • 前端(在Vue的main.js文件中添加):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //直接在Vue的原型中引入,再设置Axios的基路径,到后面使用axios发送请求路径前面都会加上该路径
    axios.defaults.baseURL = "http://localhost:8100"

    //请求超时时间
    axios.defaults.timeout = 10000;

    //post请求头
    axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
    //
    //设置cross跨域 并设置访问权限 允许跨域携带cookie信息,
    // 如果不携带cookie信息,那么跨域每次请求服务器都会重新创建一个session,因为session的JSESSIONID是保存在浏览器的cookie中的
    axios.defaults.withCredentials=true;

    Vue.prototype.$http = axios //全局注册axios,使用方法为:this.$http.GET/POST/DELETE/PUT
  • 后端(写一个解决跨域问题的配置类):

    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
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

    /**
    * 解决前后端分离引发的跨域问题
    */
    @Configuration
    public class CrossConfiguration implements WebMvcConfigurer {

    /**
    * 注意:如果想要在前端请求的时候携带cookie信息,allowedOriginPatterns的值不能设置为 * ,
    * 需要将值设置为前端的工程路径,可以设置多个请求的路径,每个用 ’,‘隔开
    */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
    .allowedOriginPatterns(
    "http://localhost:8080",
    "http://localhost:8081",
    "http://localhost:8082",
    "http://localhost:8083")
    .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
    .allowCredentials(true)
    .maxAge(3600)
    .allowedHeaders("*");
    }
    }

SpringBoot整合Vue中的问题

1、Cookie的问题

前后端分离开发Cookie的跨域问题,请求中有Cookie信息,但浏览器本地却没有Cookie信息

解决方法:不再使用Servlet中原生的设置Cookie的方式设置Cookie,而是使用 ResponseCookie来设置Cookie

  • 设置Cookie(写成一个工具类,用的时候直接调用就行了):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 用于保存cookie的值
    * @param name cookie的键
    * @param value cookie的值
    * @return 返回cookie的信息
    */
    public static ResponseCookie setCookie(String name, String value) {
    return ResponseCookie.from(name, value) // key & value
    .httpOnly(false) //是否禁止js读取设置为true,允许js读取为false
    .secure(true) //是否在http下也传输,设置true在http下也传输,设置为false只能在https下传输
    .domain("localhost")// 设置域名
    .path("/") // path
    .maxAge(Duration.ofHours(1)) // 1个小时候过期
    .sameSite("Lax") // 大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外
    .build();
    }
  • 将Cookie的信息通知浏览器保存:

    1
    2
    3
    //调用封装好的工具类来设置Cookie的值
    // 设置Cookie Header
    response.setHeader(HttpHeaders.SET_COOKIE, String.valueOf(WebUtils.setCookie("myCookie","myCookieValue")));

具体原因:https://www.jianshu.com/p/6707ad6a793f

2、数据库问题

mybatisplus数据库没有的字段实体类如何处理:【比如:JavaBean中的属性值为另外一个JavaBean的时候可以使用@TableField( exist = false )】

1
2
3
4
5
/**
* 使用 @TableField(exist = false) ,表示该字段在数据库中不存在 ,所以不会插入数据库中
* 使用 transient 、 static 修饰属性也不会插入数据库中
* 在属性或get方法上声明注解@TableField(exist = false)
*/

3、日期格式化问题

后台解决

1
2
3
4
5
//在JavaBean日期类的字段上加上如下注解
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@JSONField(format = "yyyy-MM-dd")

前端解决:使用momunt过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//进行全局定义过滤器
//npm install moment -S

//第一步、在main.js中
import moment from ‘moment/moment’

//定义全局时间过滤器
// 定义过滤器
Vue.filter('dateString', function (value, format = 'YYYY-MM-DD HH:mm:ss') {
return moment(value).format(format);
})

//在页面中使用
{{ book.addTime | dateString }}

4、SpringBoot拦截器获取不到Session的值

在拦截器开始前加上如下代码:

1
2
3
if (request.getMethod().equals("OPTIONS")) {
return true;
}

为什么SpringBoot拦截器取不到Session值的原因:https://blog.csdn.net/weixin_45059962/article/details/111104137

Vue使用遇到的问题

一、点击事件

在点击事件加上 【 .native 】表示使用原生的事件,而不是Vue提供的,例如 @click.native

在Vue中使用@click点击事件没有用:下面连接具体原因

https://blog.csdn.net/weixin_41646716/article/details/90069562

二、拦截事件

当被后台的拦截器拦截成功后返回的的结果为 ‘’ ,一个空的字符串

三、使用Vue进行文件上传

使用ElementUI进行文件上传时遇到的问题

1
2
3
4
5
6
7
8
9
10
11
12
<!-- action属性必填。提交的方式默认为POST,默认是不携带Cookie信息的,需要将with-credentials设置为True,才会携带Cooie信息 -->
<el-form>
<el-form-item>
<el-upload class="upload-demo" drag accept="image/jpg"
with-credentials
name="userImg" :action="requestURL+'user/admin/updateImg'">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传jpg文件,且不超过10MB</div>
</el-upload>
</el-form-item>
</el-form>

三、axios的使用

1、在axios中,param和data属性的区别

2、使用axios传参的问题

问题:在使用axios的POST方法传参数的时候,前端传了值,后端接收不到值

对于上述问题:可以先使用 URLSearchParams将参数保存,再传入给后端

1
2
3
4
5
6
7
8
9
10
11
//使用axios的post方法传入参数,直接加在data属性中,前端可能传入了参数,但是后台却就收不到
//这时可以使用URLSearchParams来将值保存,用于传入后台
let param = new URLSearchParams();
param.append("address", address);
param.append("carts", JSON.stringify(this.multipleSelection));

this.$http({
method: "POST",
url: "/user/admin/order",
data: param,
})