实习------SpringBoot 框架

news2024/11/24 11:35:45

Spring Boot 是什么 (了解)

Spring Boot 是 Spring 开源组织下的子项目,其设计目的是专注于Spring应用的开发,开发人员可以把更多的精力放在业务代码上,而无需过多关注XML的配置,从而简化Spring应用开发,提高开发效率

Spring Boot 提供了大量开箱即用(out-of-the-box)的依赖模块,例如 spring-boot-starter-redis、spring-boot-starter-data-mongodb 和 spring-boot-starter-data-elasticsearch 等。这些依赖模块为 Spring Boot 应用提供了大量的自动配置,使得 Spring Boot 应用只需要非常少量的配置甚至零配置,便可以运行起来,让开发人员从 Spring 的“配置地狱”中解放出来,有更多的精力专注于业务逻辑的开发。

Spring Boot starter (了解)

Spring Boot 将日常企业应用研发中的各种场景都抽取出来,做成一个个的 starter(启动器),starter 中整合了该场景下各种可能用到的依赖,用户只需要在 Maven 中引入 starter 依赖,SpringBoot 就能自动扫描到要加载的信息并启动相应的默认配置。starter 提供了大量的自动配置,让用户摆脱了处理各种依赖和配置的困扰。所有这些 starter 都遵循着约定成俗的默认配置,并允许用户调整这些配置,即遵循“约定大于配置”的原则。

并不是所有的 starter 都是由 Spring Boot 官方提供的,也有部分 starter 是第三方技术厂商提供的,例如 druid-spring-boot-starter 和 mybatis-spring-boot-starter 等等。当然也存在个别第三方技术,Spring Boot 官方没提供 starter,第三方技术厂商也没有提供 starter。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!--SpringBoot父项目依赖管理-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/>
    </parent>
    ....
    <dependencies>
        <!--导入 spring-boot-starter-web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        ...
    </dependencies>
    ...
</project>

spring-boot-starter-parent 是所有 Spring Boot 项目的父级依赖,它被称为 Spring Boot 的版本仲裁中心,可以对项目内的部分常用依赖进行统一管理。

Spring Boot 项目可以通过继承 spring-boot-starter-parent 来获得一些合理的默认配置,它主要提供了以下特性:

  • 默认 JDK 版本(Java 8)

  • 默认字符集(UTF-8)

  • 依赖管理功能

  • 资源过滤

  • 默认插件配置

  • 识别 application.properties 和 application.yml 类型的配置文件

查看 spring-boot-starter- parent 的底层代码,可以发现其有一个父级依赖 spring-boot-dependencies。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.5</version>
</parent>

spring-boot-dependencies 的底层代码如下。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.4.5</version>
    <packaging>pom</packaging>
    ....
    <properties>
        <activemq.version>5.16.1</activemq.version>
        <antlr2.version>2.7.7</antlr2.version>
        <appengine-sdk.version>1.9.88</appengine-sdk.version>
        <artemis.version>2.15.0</artemis.version>
        <aspectj.version>1.9.6</aspectj.version>
        <assertj.version>3.18.1</assertj.version>
        <atomikos.version>4.0.6</atomikos.version>
        ....
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.apache.activemq</groupId>
                <artifactId>activemq-amqp</artifactId>
                <version>${activemq.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.activemq</groupId>
                <artifactId>activemq-blueprint</artifactId>
                <version>${activemq.version}</version>
            </dependency>
            ...
        </dependencies>
    </dependencyManagement>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>build-helper-maven-plugin</artifactId>
                    <version>${build-helper-maven-plugin.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.flywaydb</groupId>
                    <artifactId>flyway-maven-plugin</artifactId>
                    <version>${flyway.version}</version>
                </plugin>
                ...
            </plugins>
        </pluginManagement>
    </build>
</project>

以上配置中,部分元素说明如下:

  • dependencyManagement :负责管理依赖;

  • pluginManagement:负责管理插件;

  • properties:负责定义依赖或插件的版本号。

spring-boot-dependencies 通过 dependencyManagement 、pluginManagement 和 properties 等元素对一些常用技术框架的依赖或插件进行了统一版本管理,例如 Activemq、Spring、Tomcat 等。

YAML (熟悉)

什么是yaml?

YAML 是一种易读的数据序列化语言。它通常用于配置文件。与properties文件相比,YAML 文件具有结构化、有序、不容易混淆、分层、简洁的优点。

SpringBoot 默认使用以下 2 种全局的配置文件,其文件名是固定的。

  • application.properties

  • application.yml

YAML 语法

YAML 的语法如下:

  • 使用缩进表示层级关系。

  • 缩进时不允许使用 Tab 键,只允许使用空格。

  • 缩进的空格数不重要,但同级元素必须左侧对齐。

  • 大小写敏感。

spring:
  profiles: dev

  datasource:
    url: jdbc:mysql://127.0.01/banchengbang_springboot
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver

YAML 常用写法

YAML 支持以下三种数据结构:

  • 对象:键值对的集合

  • 数组:一组按次序排列的值

  • 字面量:单个的、不可拆分的值

YAML 字面量写法

字面量是指单个的,不可拆分的值,例如:数字、字符串、布尔值、以及日期等。

在 YAML 中,使用“key:[空格]value的形式表示一对键值对(空格不能省略),如 url: www.biancheng.net。

字面量直接写在键值对的“value中即可,且默认情况下字符串是不需要使用单引号或双引号的。

name: bianchengbang

YAML 对象写法

在 YAML 中,对象可能包含多个属性,每一个属性都是一对键值对。

YAML 为对象提供了 2 种写法:

普通写法,使用缩进表示对象与属性的层级关系。

website: 
  name: bianchengbang
  url: www.biancheng.net

行内写法:

website: {name: bianchengbang,url: www.biancheng.net}

YAML 数组写法

YAML 使用“-”表示数组中的元素,普通写法如下:

pets:
  -dog
  -cat
  -pig

行内写法

pets: [dog,cat,pig]

复合结构

以上三种数据结构可以任意组合使用,以实现不同的用户需求,例如:

person:
  name: zhangsan
  age: 30
  pets:
    -dog
    -cat
    -pig
  car:
    name: QQ
  child:
    name: zhangxiaosan
    age: 2

YAML 组织结构

一个 YAML 文件可以由一个或多个文档组成,文档之间使用“---作为分隔符,且个文档相互独立,互不干扰。如果 YAML 文件只包含一个文档,则“---分隔符可以省略。

---
website:
  name: bianchengbang
  url: www.biancheng.net
---
website: {name: bianchengbang,url: www.biancheng.net}

pets:
  -dog
  -cat
  -pig

---
pets: [dog,cat,pig]

name: "zhangsan \n lisi"

---
name: 'zhangsan \n lisi'

*Spring Boot 配置绑定 (熟悉)

https://blog.csdn.net/qq_41946216/article/details/124771388

Spring Boot 导入Spring 配置(了解)

默认情况下,Spring Boot 中是不包含任何的 Spring 配置文件的,即使我们手动添加 Spring 配置文件到项目中,也不会被识别。

Spring Boot提倡基于Java的配置。尽管你可以使用XML源,不过还是建议你使用@Configuration类作为主要配置源。通常定义了main方法的类也是使用@Configuration注解的一个很好的替补。

使用@ImportResource注解加载Spring配置文件

在主启动类上使用 @ImportResource 注解可以导入一个或多个 Spring 配置文件,并使其中的内容生效。

在resources下创建beans.xml文件,如下

<?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="orderService" class="com.moming.service.impl.OrderServiceImpl"></bean>
</beans>
@SpringBootApplication
@ImportResource(value = "classpath:beans.xml")
public class App{
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

Spring Boot 推荐我们使用全注解的方式加载 Spring 配置,其实现方式如下:

  • 使用 @Configuration 注解定义配置类,替换 Spring 的配置文件;

  • 配置类内部可以包含有一个或多个被 @Bean 注解的方法,这些方法会被 AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext 类扫描,构建 bean 定义(相当于 Spring 配置文件中的<bean></bean>标签),方法的返回值会以组件的形式添加到容器中,组件的 id/name 就是方法名。

Spring Boot Profile (熟悉)

实际开发中会同时存在dev、uat、prod等多套环境,这些环境共用一套代码逻辑,但却需要不同的配置。例如dev环境要连接测试数据库,而prod则需要连接生产数据库等等。profile最主要的目的就是可以区分不同的环境,进而对不同环境进行配置

当profile为default时,只使用application.yaml,SpringBoot默认Server端口为8080。将当前profile激活为dev时, SpringBoot就会额外加载application-dev.yaml 后合并到application.yaml中,若其中有相同的配置则覆盖掉。

Spring Boot配置 profile

配置profile作用:我们在开发Spring Boot应用时,通常同一套程序会被安装到不同环境,比如:开发、测试、生产等。其中数据库地址、服务器端口等等配置都不同,如果每次打包时,都要修改配置文件,那么非常麻烦。profile功能就是来进行动态配置切换的。

profile配置方法

  • 多profile文件方式

  • yml多文档方式

profile激活方法

  • 配置文件

  • 虚拟机参数 在VM options指定: -Dspring.profiles.active=dev

  • 命令行参数 java-jar xxx.jar --spring.profiles.active=dev

演示

一、创建不同的application.properties配置文件(多profile文件方式)

下面创建application-dev(开发配置文件,端口8081)、application-test(测试配置文件,端口8082)、application-pro(生产配置文件,端口8083),并设置不同的配置文件下的端口。

application-dev.properties
server.port = 8081
application-test.properties
server.port = 8082
application-pro.properties
server.port = 8083

如果需要调用对应的端口,则需要在application配置文件中对相应的端口进行激活开放

application
spring.profiles.active = dev #开放application-dev.properties的端口
spring.profiles.active = test#开放application-test.properties的端口
spring.profiles.active = pro#开放application-pro.properties的端口
#其中spring.profiles.active的值为不同配置文件的后缀名

二、创建一个application.yml文件(yml多文档方式)

在application.yml配置文件中,通过 - - - 对不同profile配置进行区分,并通过Spring Boot中的profiles对不同profile进行命名

---
server:
  port: 8081
  
spring:
  profiles: dev
---
server:
  port: 8082
  
spring:
  profiles: test
---
server:
  port: 8083
  
spring:
  profiles: pro
---
#不同profile的激活,通过修改active的值,从而达到对不同的profile的激活
spring:
  profiles:
    active: dev

Spring Boot 默认配置文件(熟悉)

通常情况下,Spring Boot 在启动时会将 resources 目录下的 application.properties 或 apllication.yml 作为其默认配置文件,我们可以在该配置文件中对项目进行配置,但这并不意味着 Spring Boot 项目中只能存在一个 application.properties 或 application.yml。

默认配置文件顺序

Spring Boot 项目中可以存在多个 application.properties 或 apllication.yml。

SpringBoot项目启动会扫描以下位置的application.properties或者application.yml文件作为SpringBoot的默认配置文件,具体的目录如下所示。

  1. file:./config/ ( 项目根路径下的config文件夹)

  1. file:./ (项目根路径)

  1. classpath:/config/ (类路径下的config文件夹)

  1. classpath:/ (类路径)

注:file: 指当前项目根目录;classpath: 指当前项目的类路径,即 resources 目录。

以上所有位置的配置文件都会被加载,且它们优先级依次降低,序号越小优先级越高。其次,位于相同位置的 application.properties 的优先级高于 application.yml

所有位置的文件都会被加载,高优先级配置会覆盖低优先级配置,形成互补配置,即:

  • 存在相同的配置内容时,高优先级的内容会覆盖低优先级的内容;

  • 存在不同的配置内容时,高优先级和低优先级的配置内容取并集。

优先级:8081>8082>8083>8084

优先级由高到底,高优先级的配置会覆盖低优先级的配置,没有的配置进行互补配置。

注意:不是类路径下的配置文件在打包时,如果不做配置是不会打包进jar中的,也会是说前两个配置在项目打包后,包中是不存在的,所以尽量不要用前两个位置。

Spring Boot 外部配置文件(熟悉)

除了默认配置文件,Spring Boot 还可以加载一些位于项目外部的配置文件。我们可以通过如下 2 个参数,指定外部配置文件的路径:

  • spring.config.location

  • spring.config.additional-location

spring.config.location

我们可以先将 Spring Boot 项目打包成 JAR 文件,然后在命令行启动命令中,使用命令行参数 --spring.config.location,指定外部配置文件的路径。

java -jar {JAR}  --spring.config.location={外部配置文件全路径}
java -jar springbootdemo-0.0.1-SNAPSHOT.jar  --spring.config.location=D:\myConfig\my-application.yml

需要注意的是,使用该参数指定配置文件后,会使项目默认配置文件(application.properties 或 application.yml )失效,Spring Boot 将只加载指定的外部配置文件。我们可以通过以下 3 种方式解决这个问题。

  • 在 IDEA 的运行配置(Run/Debug Configuration)中,添加虚拟机参数 -Dspring.config.additional-location=D:\myConfig\my-application.yml,指定外部配置文件;

  • 在 IDEA 的运行配置(Run/Debug Configuration)中,添加程序运行参数 --spring.config.additional-location=D:\myConfig\my-application.yml,指定外部配置文件;

  • 在主启动类中调用 System.setProperty()方法添加系统属性 spring.config.additional-location,指定外部配置文件。

spring.config.additional-location

我们还可以在 Spring Boot 启动时,使用命令行参数 --spring.config.additional-location 来加载外部配置文件。

java -jar {JAR}  --spring.config.additional-location={外部配置文件全路径}
java -jar springbootdemo-0.0.1-SNAPSHOT.jar  --spring.config.additional-location=D:\myConfig\my-application.yml

但与 --spring.config.location 不同,--spring.config.additional-location 不会使项目默认的配置文件失效,使用该命令行参数添加的外部配置文件会与项目默认的配置文件共同生效,形成互补配置,且其优先级是最高的,比所有默认配置文件的优先级都高。

注意:Maven 对项目进行打包时,位于项目根目录下的配置文件是无法被打包进项目的 JAR 包的,因此位于根目录下的默认配置文件无法在 JAR 中生效,即该项目将只加载指定的外部配置文件和项目类路径(classpath)下的默认配置文件,它们的加载优先级顺序为:

  1. spring.config.additional-location 指定的外部配置文件 my-application.yml

  1. classpath:/config/application.yml

  1. classpath:/application.yml

实战演示:http://c.biancheng.net/spring_boot/external-config.html

Spring Boot 配置加载顺序(熟悉)

Spring Boot配置加载顺序优先级

properties文件--->YAML文件--->系统环境变量--->命令行参数

配置加载顺序:优先级由高到底,高优先级的配置会覆盖低优先级的配置,没有的配置进行互补配置。

默认配置文件顺序

Spring Boot 项目中可以存在多个 application.properties 或 apllication.yml。

SpringBoot项目启动会扫描以下位置的application.properties或者application.yml文件作为SpringBoot的默认配置文件,具体的目录如下所示。

  1. file:./config/ ( 项目根路径下的config文件夹)

  1. file:./ (项目根路径)

  1. classpath:/config/ (类路径下的config文件夹)

  1. classpath:/ (类路径)

注:file: 指当前项目根目录;classpath: 指当前项目的类路径,即 resources 目录。

以上所有位置的配置文件都会被加载,且它们优先级依次降低,序号越小优先级越高。其次,位于相同位置的 application.properties 的优先级高于 application.yml

所有位置的文件都会被加载,高优先级配置会覆盖低优先级配置,形成互补配置,即:

  • 存在相同的配置内容时,高优先级的内容会覆盖低优先级的内容;

  • 存在不同的配置内容时,高优先级和低优先级的配置内容取并集。

优先级:8081>8082>8083>8084

优先级由高到底,高优先级的配置会覆盖低优先级的配置,没有的配置进行互补配置。

注意:不是类路径下的配置文件在打包时,如果不做配置是不会打包进jar中的,也会是说前两个配置在项目打包后,包中是不存在的,所以尽量不要用前两个位置。

Spring Boot 自动配置原理(熟悉)

我们知道SpringBoot可以帮我们减少很多的配置,也肯定听过“约定大于配置”这么一句话,那SpringBoot是怎么做的呢?其实靠的就是@EnableAutoConfiguration注解。

简单来说,这个注解可以帮助我们自动载入应用程序所需要的所有默认配置。

发现有两个比较重要的注解

  • @AutoConfigurationPackage:自动配置包

  • @Import(AutoConfigurationImportSelector.class):给IOC容器导入组件

@AutoConfigurationPackage

我们可以发现,依靠的还是@Import注解

@AutoConfigurationPackage注解就是在默认的情况下就是将主配置类(@SpringBootApplication这个注解所在包及其子包里边的组件)扫描到Spring容器中

看起来跟@ComponentScan注解一样的作用,但是扫描的对象不一样,比如说,你用了Spring Data JPA,可能会在实体类上写@Entity注解。这个@Entity注解由 @AutoConfigurationPackage扫描并加载,而我们平时开发用的@Controller/@Service/@Component/@Repository这些注解是由@ComponentScan来扫描并加载的。

简单理解:这二者扫描的对象是不一样的。

@Import(AutoConfigurationImportSelector.class)

AutoConfigurationImportSelector这个类中有个selectImports方法,其中getCandidateConfigurations方法可以获取很多的配置信息

那这些配置信息哪里来的呢?

这里包装了一层,我们看到的只是通过SpringFactoriesLoader来加载获取到的配置信息,还没看到关键信息,继续进去:

解析

FACTORIES_RESOURCE_LOCATION(工厂资源加载路径)的值是META-INF/spring.factories

Spring启动的时候会扫描所有jar路径下的META-INF/spring.factories,将其文件包装成Properties对象

从Properties对象获取到key值为EnableAutoConfiguration的数据,然后添加到容器里边。

最后我们会默认加载113个默认的配置类:

Spring Boot 自动配置原理是什么?

其中@EnableAutoConfiguration是关键(启用自动配置),这个注解里的

@Import(AutoConfigurationImportSelector.class)注解起核心作用,Spring启动的时候会扫描所有jar路径下的META-INF/spring.factories,将其文件包装成Properties对象,从Properties对象获取到key值为EnableAutoConfiguration的数据(这些数据都是SpringBoot默认配置好了的配置文件数据,例如Redis常用配置的数据,这样就不需要我们配置了,我们只要简单配置几个核心的数据就可以用Redis了,从而简化了配置),加载到IOC容器中,实现自动配置功能!

Spring Boot 统一日志框架(熟悉)

在项目开发中,日志十分的重要,不管是记录运行情况还是定位线上问题,都离不开对日志的分析。在 Java 领域里存在着多种日志框架,如 JCL、SLF4J、Jboss-logging、jUL、log4j、log4j2、logback 等等。

日志框架的选择

市面上常见的日志框架有很多,它们可以被分为两类:日志门面(日志抽象层)和日志实现,如下表。

日志分类

描述

举例

日志门面(日志抽象层)

为 Java 日志访问提供一套标准和规范的 API 框架,其主要意义在于提供接口。

JCL(Jakarta Commons Logging)、SLF4j(Simple Logging Facade for Java)、jboss-logging

日志实现

日志门面的具体的实现

Log4j、JUL(java.util.logging)、Log4j2、Logback

通常情况下,日志由一个日志门面与一个日志实现组合搭建而成,Spring Boot 选用 SLF4J + Logback 的组合来搭建日志系统。

SLF4J 是目前市面上最流行的日志门面,使用 Slf4j 可以很灵活的使用占位符进行参数占位,简化代码,拥有更好的可读性。

Logback 是 Slf4j 的原生实现框架,它与 Log4j 出自一个人之手,但拥有比 log4j 更多的优点、特性和更做强的性能,现在基本都用来代替 log4j 成为主流。

统一日志框架(通用)

通常一个完整的应用下会依赖于多种不同的框架,而且它们记录日志使用的日志框架也不尽相同,例如,Spring Boot(slf4j+logback),Spring(commons-logging)、Hibernate(jboss-logging)等等。那么如何统一日志框架的使用呢?

统一日志框架一共需要以下 3 步 :

  1. 排除应用中的原来的日志框架;

  1. 引入替换包替换被排除的日志框架;

  1. 导入 SLF4J 实现。

SLF4J 官方给出的统一日志框架的方案是“狸猫换太子”,即使用一个替换包来替换原来的日志框架,例如 log4j-over-slf4j 替换 Log4j(Commons Logging API)、jul-to-slf4j.jar 替换 JUL(java.util.logging API)等等。

替换包内包含被替换的日志框架中的所有类,这样就可以保证应用不会报错,但替换包内部实际使用的是 SLF4J API,以达到统一日主框架的目的。

统一日志框架(Spring Boot)

我们在使用 Spring Boot 时,同样可能用到其他的框架,例如 Mybatis、Spring MVC、 Hibernate 等等,这些框架的底层都有自己的日志框架,此时我们也需要对日志框架进行统一。

我们知道,统一日志框架的使用一共分为 3 步,Soring Boot 作为一款优秀的开箱即用的框架,已经为用户完成了其中 2 步:引入替换包和导入 SLF4J 实现。

Spring Boot 的核心启动器 spring-boot-starter 引入了 spring-boot-starter-logging,使用 IDEA 查看其依赖关系,如下图。

从图 可知,spring-boot-starter-logging 的 Maven 依赖不但引入了 logback-classic (包含了日志框架 SLF4J 的实现),还引入了 log4j-to-slf4j(log4j 的替换包),jul-to-slf4j (JUL 的替换包),即 Spring Boot 已经为我们完成了统一日志框架的 3 个步骤中的 2 步。

SpringBoot 底层使用 slf4j+logback 的方式记录日志,当我们引入了依赖了其他日志框架的第三方框架(例如 Hibernate)时,只需要把这个框架所依赖的日志框架排除,即可实现日志框架的统一,示例代码如下。

<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-console</artifactId>
    <version>${activemq.version}</version>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

logback 使用和相关配置(了解)

添加logback的配置文件

此配置文件是对日志进行按级别分类存储 例如 debug一个文件,info 一个文件,error 一个文件等

另外 当天的日志信息在根目录下显示,隔日的归并到按日期的目录中并设置了有效期限,到期会自动删除

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!--设置存储路径变量-->
    <!-- windows -->
    <property name="log.path" value="D:/projectName/log"/>
    <!-- linux -->
    <!--<property name="log.path" value="/home/log"/>-->

    <!--控制台输出appender-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--设置输出格式-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <!--设置编码-->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--输出到文件-->

    <!-- 时间滚动输出 level为 DEBUG 日志 -->
    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_debug.log</file>
        <!--日志文件输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志归档 -->
            <fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- 此日志文件只记录debug级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>debug</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 时间滚动输出 level为 INFO 日志 -->
    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_info.log</file>
        <!--日志文件输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志文件只记录info级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 时间滚动输出 level为 WARN 日志 -->
    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_warn.log</file>
        <!--日志文件输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志文件只记录warn级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>warn</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <!-- 时间滚动输出 level为 ERROR 日志 -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${log.path}/log_error.log</file>
        <!--日志文件输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文件保留天数-->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- 此日志文件只记录ERROR级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <!--指定基础的日志输出级别-->
    <root level="INFO">
        <!--appender将会添加到这个loger-->
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="DEBUG_FILE" />
        <appender-ref ref="INFO_FILE" />
        <appender-ref ref="WARN_FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>
</configuration>

应用

在需要做日志记录的程序中定义Logger 然后使用方式与Log4j相同

private final Logger logger = LoggerFactory.getLogger(SysInterceptor.class);

Spring Boot 日志配置及输出(熟悉)

默认配置

Spring Boot 默认使用 SLF4J+Logback 记录日志,并提供了默认配置,即使我们不进行任何额外配,也可以使用 SLF4J+Logback 进行日志输出。常见的日志配置包括日志级别、日志的输入出格式等内容。

通过控制台输出结果可知,Spring Boot 日志默认级别为 info,日志输出内容默认包含以下元素:

  • 时间日期

  • 日志级别

  • 进程 ID

  • 分隔符:---

  • 线程名:方括号括起来(可能会截断控制台输出)

  • Logger 名称

  • 日志内容

日志级别

日志的输出都是分级别的,当一条日志信息的级别大于或等于配置文件的级别时,就对这条日志进行记录。

常见的日志级别如下(优先级依次升高)。

序号

日志级别

说明

1

trace

追踪,指明程序运行轨迹。

2

debug

调试,实际应用中一般将其作为最低级别,而 trace 则很少使用。

3

info

输出重要的信息,使用较多。

4

warn

警告,使用较多。

5

error

错误信息,使用较多。

输出格式

我们可以通过以下常用日志参数对日志的输出格式进行修改,如下表。

序号

输出格式

说明

1

%d{yyyy-MM-dd HH:mm:ss, SSS}

日志生产时间,输出到毫秒的时间

2

%-5level

输出日志级别,-5 表示左对齐并且固定输出 5 个字符,如果不足在右边补 0

3

%logger 或 %c

logger 的名称

4

%thread 或 %t

输出当前线程名称

5

%p

日志输出格式

6

%message 或 %msg 或 %m

日志内容,即 logger.info("message")

7

%n

换行符

8

%class 或 %C

输出 Java 类名

9

%file 或 %F

输出文件名

10

%L

输出错误行号

11

%method 或 %M

输出方法名

12

%l

输出语句所在的行数, 包括类名、方法名、文件名、行数

13

hostName

本地机器名

14

hostAddress

本地 ip 地址

修改默认日志配置

我们可以根据自身的需求,通过全局配置文件(application.properties/yml)修改 Spring Boot 日志级别和显示格式等默认配置。在 application.properties 中,修改 Spring Boot 日志的默认配置,代码如下。

#日志级别
logging.level.net.biancheng.www=trace
#使用相对路径的方式设置日志输出的位置(项目根目录目录\my-log\mylog\spring.log)
#logging.file.path=my-log/myLog
#绝对路径方式将日志文件输出到 【项目所在磁盘根目录\springboot\logging\my\spring.log】
logging.file.path=/spring-boot/logging
#控制台日志输出格式
logging.pattern.console=%d{yyyy-MM-dd hh:mm:ss} [%thread] %-5level %logger{50} - %msg%n
#日志文件输出格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} === - %msg%n

自定义日志配置

在 Spring Boot 的配置文件 application.porperties/yml 中,可以对日志的一些默认配置进行修改,但这种方式只能修改个别的日志配置,想要修改更多的配置或者使用更高级的功能,则需要通过日志实现框架自己的配置文件进行配置。

Spring 官方提供了各个日志实现框架所需的配置文件,用户只要将指定的配置文件放置到项目的类路径下即可。

日志框架

配置文件

Logback

logback-spring.xml、logback-spring.groovy、logback.xml、logback.groovy

Log4j2

log4j2-spring.xml、log4j2.xml

JUL (Java Util Logging)

logging.properties

从上表可以看出,日志框架的配置文件基本上被分为 2 类:

  • 普通日志配置文件,即不带 srping 标识的配置文件,例如 logback.xml;

  • 带有 spring 表示的日志配置文件,例如 logback-spring.xml。

这两种日志配置文件在使用时大不相同,下面我们就对它们分别进行介绍。

普通日志配置文件

我们将 logback.xml、log4j2.xml 等不带 spring 标识的普通日志配置文件,放在项目的类路径下后,这些配置文件会跳过 Spring Boot,直接被日志框架加载。通过这些配置文件,我们就可以达到自定义日志配置的目的。

示例

1. 将 logback.xml 加入到 Spring Boot 项目的类路径下(resources 目录下),该配置文件配置内容如下。

<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志的根目录 -->
    <property name="LOG_HOME" value="/app/log"/>
    <!-- 定义日志文件名称 -->
    <property name="appName" value="bianchengbang-spring-boot-logging"></property>
    <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!--
        日志输出格式:
   %d表示日期时间,
   %thread表示线程名,
   %-5level:级别从左显示5个字符宽度
   %logger{50} 表示logger名字最长50个字符,否则按照句点分割。
   %msg:日志消息,
   %n是换行符
        -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread]**************** %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>

    <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
    <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 指定日志文件的名称 -->
        <file>${LOG_HOME}/${appName}.log</file>
        <!--
        当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
        TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
        -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
            滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
            %i:当文件大小超过maxFileSize时,按照i进行文件滚动
            -->
            <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
            <!--
            可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
            且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
            那些为了归档而创建的目录也会被删除。
            -->
            <MaxHistory>365</MaxHistory>
            <!--
            当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
            -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志输出格式: -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %thread ] ------------------ [ %-5level ] [ %logger{50} : %line ] -
                %msg%n
            </pattern>
        </layout>
    </appender>

    <!--
  logger主要用于存放日志对象,也可以定义日志类型、级别
  name:表示匹配的logger类型前缀,也就是包的前半部分
  level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
  additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
  false:表示只用当前logger的appender-ref,true:
  表示当前logger的appender-ref和rootLogger的appender-ref都有效
    -->
    <!-- hibernate logger -->
    <logger name="net.biancheng.www" level="debug"/>
    <!-- Spring framework logger -->
    <logger name="org.springframework" level="debug" additivity="false"></logger>

    <!--
    root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
    要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
    -->
    <root level="info">
        <appender-ref ref="stdout"/>
        <appender-ref ref="appLogAppender"/>
    </root>
</configuration> 

带有 spring 标识的日志配置文件

Spring Boot 推荐用户使用 logback-spring.xml、log4j2-spring.xml 等这种带有 spring 标识的配置文件。这种配置文件被放在项目类路径后,不会直接被日志框架加载,而是由 Spring Boot 对它们进行解析,这样就可以使用 Spring Boot 的高级功能 Profile,实现在不同的环境中使用不同的日志配置。

示例

1. 将 logback.xml 文件名修改为 logback-spring.xml,并将配置文件中日志输出格式的配置修改为使用 Profile 功能的配置。

2. 配置内容修改前,日志输出格式配置如下。

<configuration scan="false" scanPeriod="60 seconds" debug="false">
    ......
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread]**************** %-5level %logger{50} - %msg%n</pattern>
        </layout>
    </appender>
    ......
</configuration> 

3. 修改 logback-spring.xml 的配置内容,通过 Profile 功能实现在不同的环境中使用不同的日志输出格式,配置如下。

<configuration scan="false" scanPeriod="60 seconds" debug="false">
    ......
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
       <layout class="ch.qos.logback.classic.PatternLayout">
            <!--开发环境 日志输出格式-->
            <springProfile name="dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
            <!--非开发环境 日志输出格式-->
            <springProfile name="!dev">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
            </springProfile>
        </layout>
    </appender>
    ......
</configuration> 

4. 在 Spring Boot 项目的 application.yml 中,激活开发环境(dev)的 Profile,配置内容如下。

#默认配置
server:
  port: 8080

#切换配置
spring:
  profiles:
    active: dev

---
#开发环境
server:
  port: 8081

spring:
  config:
    activate:
      on-profile: dev
---
#测试环境
server:
  port: 8082

spring:
  config:
    activate:
      on-profile: test
---
#生产环境
server:
  port: 8083

spring:
  config:
    activate:
      on-profile: prod

5. 启动 Spring Boot 并执行测试代码,控制台输出如下。

6. 修改 appplication.yml 中的配置,激活测试环境(test)的 Profile,配置如下。

#默认配置
server:
  port: 8080

#切换配置
spring:
  profiles:
    active: test

---
#开发环境
server:
  port: 8081

spring:
  config:
    activate:
      on-profile: dev
---
#测试环境
server:
  port: 8082

spring:
  config:
    activate:
      on-profile: test
---
#生产环境
server:
  port: 8083

spring:
  config:
    activate:
      on-profile: prod

7. 重启 Spring Boot 并执行测试代码,控制台输出如下。

Spring-boot-starter-web(了解)

Spring Boot 是在 Spring 的基础上创建一款开源框架,它提供了 spring-boot-starter-web(Web 场景启动器) 来为 Web 开发予以支持。spring-boot-starter-web 为我们提供了嵌入的 Servlet 容器以及 SpringMVC 的依赖,并为 Spring MVC 提供了大量自动配置,可以适用于大多数 Web 开发场景。

Spring Boot Web 快速开发

Spring Boot 为 Spring MVC 提供了自动配置,并在 Spring MVC 默认功能的基础上添加了以下特性:

  • 引入了 ContentNegotiatingViewResolver 和 BeanNameViewResolver(视图解析器)

  • 对包括 WebJars 在内的静态资源的支持

  • 自动注册 Converter、GenericConverter 和 Formatter (转换器和格式化器)

  • 对 HttpMessageConverters 的支持(Spring MVC 中用于转换 HTTP 请求和响应的消息转换器)

  • 自动注册 MessageCodesResolver(用于定义错误代码生成规则)

  • 支持对静态首页(index.html)的访问

  • 自动使用 ConfigurableWebBindingInitializer

只要我们在 Spring Boot 项目中的 pom.xml 中引入了 spring-boot-starter-web ,即使不进行任何配置,也可以直接使用 Spring MVC 进行 Web 开发。

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

注意:由于 spring-boot-starter-web 默认替我们引入了核心启动器 spring-boot-starter,因此,当 Spring Boot 项目中的 pom.xml 引入了 spring-boot-starter-web 的依赖后,就无须在引入 spring-boot-starter 核心启动器的依赖了。

Spring Boot 静态资源映射(了解)

Spring Boot 默认为我们提供了 3 种静态资源映射规则:

  • WebJars 映射

  • 默认资源映射

  • 静态首页(欢迎页)映射

WebJars 映射

为了让页面更加美观,让用户有更多更好的体验,Web 应用中通常会使用大量的 JS 和 CSS,例如 jQuery,Backbone.js 和 Bootstrap 等等。通常我们会将这些 Web 前端资源拷贝到 Java Web 项目的 webapp 相应目录下进行管理。但是 Spring Boot 项目是以 JAR 包的形式进行部署的,不存在 webapp 目录,那么 Web 前端资源该如何引入到 Spring Boot 项目中呢?

WebJars 可以完美的解决上面的问题,它可以 Jar 形式为 Web 项目提供资源文件。

WebJars 可以将 Web 前端资源(JS,CSS 等)打成一个个的 Jar 包,然后将这些 Jar 包部署到 Maven 中央仓库中进行统一管理,当 Spring Boot 项目中需要引入 Web 前端资源时,只需要访问 WebJars 官网,找到所需资源的 pom 依赖,将其导入到项目中即可。

所有通过 WebJars 引入的前端资源都存放在当前项目类路径(classpath)下的“/META-INF/resources/webjars/” 目录中。WebJars 的映射路径为“/webjars/**”,即所有访问“/webjars/**”的请求,都会去“classpath:/META-INF/resources/webjars/”查找 WebJars 前端资源。

默认静态资源映射

当访问项目中的任意资源(即“/**”)时,Spring Boot 会默认从以下路径中查找资源文件(优先级依次降低):

  1. classpath:/META-INF/resources/

  1. classpath:/resources/

  1. classpath:/static/

  1. classpath:/public/

这些路径又被称为静态资源文件夹,它们的优先级顺序为:classpath:/META-INF/resources/ > classpath:/resources/ > classpath:/static/ > classpath:/public/ 。

当我们请求某个静态资源(即以“.html”结尾的请求)时,Spring Boot 会先查找优先级高的文件夹,再查找优先级低的文件夹,直到找到指定的静态资源为止。

静态首页(欢迎页)映射

静态资源文件夹下的所有 index.html 被称为静态首页或者欢迎页,它们会被被 /** 映射,换句话说就是,当我们访问“/”或者“/index.html”时,都会跳转到静态首页(欢迎页)。

注意,访问静态首页或欢迎页时,其查找顺序也遵循默认静态资源的查找顺序,即先查找优先级高的目录,在查找优先级低的目录,直到找到 index.html 为止。

Spring Boot 定制Spring MVC(了解)

Spring Boot 抛弃了传统 xml 配置文件,通过配置类(标注 @Configuration 的类,相当于一个 xml 配置文件)以 JavaBean 形式进行相关配置。

Spring Boot 对 Spring MVC 的自动配置可以满足我们的大部分需求,但是我们也可以通过自定义配置类(标注 @Configuration 的类)并实现 WebMvcConfigurer 接口来定制 Spring MVC 配置,例如拦截器、格式化程序、视图控制器等等。

SpringBoot 1.5 及以前是通过继承 WebMvcConfigurerAdapter 抽象类来定制 Spring MVC 配置的,但在 SpringBoot 2.0 后,WebMvcConfigurerAdapter 抽象类就被弃用了,改为实现 WebMvcConfigurer 接口来定制 Spring MVC 配置。

WebMvcConfigurer 是一个基于 Java 8 的接口,该接口定义了许多与 Spring MVC 相关的方法,其中大部分方法都是 default 类型的,且都是空实现。因此我们只需要定义一个配置类实现 WebMvcConfigurer 接口,并重写相应的方法便可以定制 Spring MVC 的配置。

方法

说明

default void configurePathMatch(PathMatchConfigurer configurer) {}

HandlerMappings 路径的匹配规则。

default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}

内容协商策略(一个请求路径返回多种数据格式)。

default void configureAsyncSupport(AsyncSupportConfigurer configurer) {}

处理异步请求。

default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}

这个接口可以实现静态文件可以像 Servlet 一样被访问。

default void addFormatters(FormatterRegistry registry) {}

添加格式化器或者转化器。

default void addInterceptors(InterceptorRegistry registry) {}

添加 Spring MVC 生命周期拦截器,对请求进行拦截处理。

default void addResourceHandlers(ResourceHandlerRegistry registry) {}

添加或修改静态资源(例如图片,js,css 等)映射;

Spring Boot 默认设置的静态资源文件夹就是通过重写该方法设置的。

default void addCorsMappings(CorsRegistry registry) {}

处理跨域请求。

default void addViewControllers(ViewControllerRegistry registry) {}

主要用于实现无业务逻辑跳转,例如主页跳转,简单的请求重定向,错误页跳转等

default void configureViewResolvers(ViewResolverRegistry registry) {}

配置视图解析器,将 Controller 返回的字符串(视图名称),转换为具体的视图进行渲染。

default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}

添加解析器以支持自定义控制器方法参数类型,实现该方法不会覆盖用于解析处理程序方法参数的内置支持;

要自定义内置的参数解析支持, 同样可以通过 RequestMappingHandlerAdapter 直接配置 RequestMappingHandlerAdapter 。

default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}

添加处理程序来支持自定义控制器方法返回值类型。使用此选项不会覆盖处理返回值的内置支持;

要自定义处理返回值的内置支持,请直接配置 RequestMappingHandlerAdapter。

default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}

用于配置默认的消息转换器(转换 HTTP 请求和响应)。

default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}

直接添加消息转换器,会关闭默认的消息转换器列表;

实现该方法即可在不关闭默认转换器的起提下,新增一个自定义转换器。

default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}

配置异常解析器。

default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}

扩展或修改默认的异常解析器列表。

在 Spring Boot 项目中,我们可以通过以下 形式定制 Spring MVC:

  • 扩展 Spring MVC

  • 全面接管 Spring MVC

扩展 Spring MVC

如果 Spring Boot 对 Spring MVC 的自动配置不能满足我们的需要,我们还可以通过自定义一个 WebMvcConfigurer 类型(实现 WebMvcConfigurer 接口)的配置类(标注 @Configuration,但不标注 @EnableWebMvc 注解的类),来扩展 Spring MVC。这样不但能够保留 Spring Boot 对 Spring MVC 的自动配置,享受 Spring Boot 自动配置带来的便利,还能额外增加自定义的 Spring MVC 配置。

package net.biancheng.www.config;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
 
//实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能
//@EnableWebMvc   不要接管SpringMVC
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
 
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //当访问 “/” 或 “/index.html” 时,都直接跳转到登陆页面
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/index.html").setViewName("login");
    }
}

全面接管 Spring MVC

在一些特殊情况下,我们可能需要抛弃 Spring Boot 对 Spring MVC 的全部自动配置,完全接管 Spring MVC。此时我们可以自定义一个 WebMvcConfigurer 类型(实现 WebMvcConfigurer 接口)的配置类,并在该类上标注 @EnableWebMvc 注解,来实现完全接管 Spring MVC。

注意:完全接管 Spring MVC 后,Spring Boot 对 Spring MVC 的自动配置将全部失效。

package net.biancheng.www.config;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
 
//实现 WebMvcConfigurer 接口可以来扩展 SpringMVC 的功能
@EnableWebMvc  // 完全接管SpringMVC
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
 
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //当访问 “/” 或 “/index.html” 时,都直接跳转到登陆页面
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/index.html").setViewName("login");
    }
}

Spring Boot 能够访问位于静态资源文件夹中的静态文件,这是在 Spring Boot 对 Spring MVC 的默认自动配置中定义的,当我们全面接管 Spring MVC 后,Spring Boot 对 Spring MVC 的默认配置都会失效,此时再访问静态资源文件夹中的静态资源就会报 404 错误。

Spring Boot 国际化 (了解)

国际化(Internationalization 简称 I18n,其中“I”和“n”分别为首末字符,18 则为中间的字符数)是指软件开发时应该具备支持多种语言和地区的功能。换句话说就是,开发的软件需要能同时应对不同国家和地区的用户访问,并根据用户地区和语言习惯,提供相应的、符合用具阅读习惯的页面和数据,例如,为中国用户提供汉语界面显示,为美国用户提供提供英语界面显示。

在 Spring 项目中实现国际化,通常需要以下 3 步:

  1. 编写国际化资源(配置)文件;

  1. 使用 ResourceBundleMessageSource 管理国际化资源文件;

  1. 在页面获取国际化内容。

代码演示:http://c.biancheng.net/spring_boot/global.html

Spring Boot 拦截器 (熟悉)

我们对拦截器并不陌生,无论是 Struts 2 还是 Spring MVC 中都提供了拦截器功能,它可以根据 URL 对请求进行拦截,主要应用于登陆校验、权限验证、乱码解决、性能监控和异常处理等功能上。Spring Boot 同样提供了拦截器功能。

在 Spring Boot 项目中,使用拦截器功能通常需要以下 3 步:

  1. 定义拦截器;

  1. 注册拦截器;

  1. 指定拦截规则(如果是拦截所有,静态资源也会被拦截)。

定义拦截器

在 Spring Boot 中定义拦截器十分的简单,只需要创建一个拦截器类,并实现 HandlerInterceptor 接口即可。

HandlerInterceptor 接口中定义以下 3 个方法,如下表。

返回值类型

方法声明

描述

boolean

preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

该方法在控制器处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。

void

postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)

该方法在控制器处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步修改。

void

afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

该方法在视图渲染结束后执行,可以通过此方法实现资源清理、记录日志信息等工作。

注册拦截器

创建一个实现了 WebMvcConfigurer 接口的配置类(使用了 @Configuration 注解的类),重写 addInterceptors() 方法,并在该方法中调用 registry.addInterceptor() 方法将自定义的拦截器注册到容器中。

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    ......
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor());
    }
}

指定拦截规则

修改 MyMvcConfig 配置类中 addInterceptors() 方法的代码,继续指定拦截器的拦截规则,代码如下。

@Slf4j
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    ......
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info("注册拦截器");
        registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //拦截所有请求,包括静态资源文件
                .excludePathPatterns("/", "/login", "/index.html", "/user/login", "/css/**", "/images/**", "/js/**", "/fonts/**"); //放行登录页,登陆操作,静态资源
    }
}

在指定拦截器拦截规则时,调用了两个方法,这两个方法的说明如下:

  • addPathPatterns:该方法用于指定拦截路径,例如拦截路径为“/**”,表示拦截所有请求,包括对静态资源的请求。

  • excludePathPatterns:该方法用于排除拦截路径,即指定不需要被拦截器拦截的请求。

代码演示:http://c.biancheng.net/spring_boot/interceptor.html

拦截器与过滤器的区别(了解)

  • 拦截器是基于java的反射机制的,而过滤器是基于函数回调。

  • 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。

  • 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。

  • 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。

  • 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次

Spring Boot 默认异常处理(熟悉)

Spring Boot 默认异常处理机制

Spring Boot 提供了一套默认的异常处理机制,一旦程序中出现了异常,Spring Boot 会自动识别客户端的类型(浏览器客户端或机器客户端),并根据客户端的不同,以不同的形式展示异常信息。

1. 对于浏览器客户端而言,Spring Boot 会响应一个“ whitelabel”错误视图,以 HTML 格式呈现错误信息

2. 对于机器客户端而言,Spring Boot 将生成 JSON 响应,来展示异常消息。

{
    "timestamp": "2021-07-12T07:05:29.885+00:00",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/m1ain.html"
}

Spring Boot 异常处理自动配置原理

Spring Boot 通过配置类 ErrorMvcAutoConfiguration 对异常处理提供了自动配置,该配置类向容器中注入了以下 4 个组件。

  • ErrorPageCustomizer:该组件会在在系统发生异常后,默认将请求转发到“/error”上。

  • BasicErrorController:处理默认的“/error”请求。

  • DefaultErrorViewResolver:默认的错误视图解析器,将异常信息解析到相应的错误视图上。

  • DefaultErrorAttributes:用于页面上共享异常信息。

ErrorPageCustomizer

ErrorMvcAutoConfiguration 向容器中注入了一个名为 ErrorPageCustomizer 的组件,它主要用于定制错误页面的响应规则。

@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
    //将请求转发到 /errror(this.properties.getError().getPath())上
    ErrorPage errorPage = new ErrorPage(this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
    // 注册错误页面
    errorPageRegistry.addErrorPages(errorPage);
}

ErrorPageCustomizer 通过 registerErrorPages() 方法来注册错误页面的响应规则。当系统中发生异常后,ErrorPageCustomizer 组件会自动生效,并将请求转发到 “/error”上,交给 BasicErrorController 进行处理

BasicErrorController

ErrorMvcAutoConfiguration 还向容器中注入了一个错误控制器组件 BasicErrorController

BasicErrorController 的定义如下

//BasicErrorController 用于处理 “/error” 请求
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
    ......
    /**
     * 该方法用于处理浏览器客户端的请求发生的异常
     * 生成 html 页面来展示异常信息
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        //获取错误状态码
        HttpStatus status = getStatus(request);
        //getErrorAttributes 根据错误信息来封装一些 model 数据,用于页面显示
        Map<String, Object> model = Collections
                .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
        //为响应对象设置错误状态码
        response.setStatus(status.value());
        //调用 resolveErrorView() 方法,使用错误视图解析器生成 ModelAndView 对象(包含错误页面地址和页面内容)
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
    }

    /**
     * 该方法用于处理机器客户端的请求发生的错误
     * 产生 JSON 格式的数据展示错误信息
     * @param request
     * @return
     */
    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity<>(status);
        }
        Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
        return new ResponseEntity<>(body, status);
    }
    ......
}

Spring Boot 通过 BasicErrorController 进行统一的错误处理(例如默认的“/error”请求)。Spring Boot 会自动识别发出请求的客户端的类型(浏览器客户端或机器客户端),并根据客户端类型,将请求分别交给 errorHtml() 和 error() 方法进行处理。

返回值类型

方法声明

客户端类型

错误信息返类型

ModelAndView

errorHtml(HttpServletRequest request, HttpServletResponse response)

浏览器客户端

text/html(错误页面)

ResponseEntity<Map<String, Object>>

error(HttpServletRequest request)

机器客户端(例如安卓、IOS、Postman 等等)

JSON

换句话说,当使用浏览器访问出现异常时,会进入 BasicErrorController 控制器中的 errorHtml() 方法进行处理,当使用安卓、IOS、Postman 等机器客户端访问出现异常时,就进入error() 方法处理。

在 errorHtml() 方法中会调用父类(AbstractErrorController)的 resolveErrorView() 方法,代码如下。

protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,
                                        Map<String, Object> model) {
    //获取容器中的所有的错误视图解析器来处理该异常信息
    for (ErrorViewResolver resolver : this.errorViewResolvers) {
        //调用错误视图解析器的 resolveErrorView 解析到错误视图页面
        ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
        if (modelAndView != null) {
            return modelAndView;
        }
    }
    return null;
}

从上述源码可以看出,在响应页面的时候,会在父类的 resolveErrorView 方法中获取容器中所有的 ErrorViewResolver 对象(错误视图解析器,包括 DefaultErrorViewResolver 在内),一起来解析异常信息。

DefaultErrorViewResolver

ErrorMvcAutoConfiguration 还向容器中注入了一个默认的错误视图解析器组件 DefaultErrorViewResolver

当发出请求的客户端为浏览器时,Spring Boot 会获取容器中所有的 ErrorViewResolver 对象(错误视图解析器),并分别调用它们的 resolveErrorView() 方法对异常信息进行解析,其中自然也包括 DefaultErrorViewResolver(默认错误信息解析器)。

public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {

    private static final Map<HttpStatus.Series, String> SERIES_VIEWS;

    static {
        Map<HttpStatus.Series, String> views = new EnumMap<>(HttpStatus.Series.class);
        views.put(Series.CLIENT_ERROR, "4xx");
        views.put(Series.SERVER_ERROR, "5xx");
        SERIES_VIEWS = Collections.unmodifiableMap(views);
    }

    ......

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        //尝试以错误状态码作为错误页面名进行解析
        ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            //尝试以 4xx 或 5xx 作为错误页面页面进行解析
            modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
        }
        return modelAndView;
    }

    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        //错误模板页面,例如 error/404、error/4xx、error/500、error/5xx
        String errorViewName = "error/" + viewName;
        //当模板引擎可以解析这些模板页面时,就用模板引擎解析
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
                this.applicationContext);
        if (provider != null) {
            //在模板能够解析到模板页面的情况下,返回 errorViewName 指定的视图
            return new ModelAndView(errorViewName, model);
        }
        //若模板引擎不能解析,则去静态资源文件夹下查找 errorViewName 对应的页面
        return resolveResource(errorViewName, model);
    }

    private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
        //遍历所有静态资源文件夹
        for (String location : this.resources.getStaticLocations()) {
            try {
                Resource resource = this.applicationContext.getResource(location);
                //静态资源文件夹下的错误页面,例如error/404.html、error/4xx.html、error/500.html、error/5xx.html
                resource = resource.createRelative(viewName + ".html");
                //若静态资源文件夹下存在以上错误页面,则直接返回
                if (resource.exists()) {
                    return new ModelAndView(new DefaultErrorViewResolver.HtmlResourceView(resource), model);
                }
            } catch (Exception ex) {
            }
        }
        return null;
    }
    ......
}

DefaultErrorViewResolver 解析异常信息的步骤如下:

  1. 根据错误状态码(例如 404、500、400 等),生成一个错误视图 error/status,例如 error/404、error/500、error/400。

  1. 尝试使用模板引擎解析 error/status 视图,即尝试从 classpath 类路径下的 templates 目录下,查找 error/status.html,例如 error/404.html、error/500.html、error/400.html。

  1. 若模板引擎能够解析到 error/status 视图,则将视图和数据封装成 ModelAndView 返回并结束整个解析流程,否则跳转到第 4 步。

  1. 依次从各个静态资源文件夹中查找 error/status.html,若在静态文件夹中找到了该错误页面,则返回并结束整个解析流程,否则跳转到第 5 步。

  1. 将错误状态码(例如 404、500、400 等)转换为 4xx 或 5xx,然后重复前 4 个步骤,若解析成功则返回并结束整个解析流程,否则跳转第 6 步。

  1. 处理默认的 “/error ”请求,使用 Spring Boot 默认的错误页面(Whitelabel Error Page)。

DefaultErrorAttributes

ErrorMvcAutoConfiguration 还向容器中注入了一个组件默认错误属性处理工具 DefaultErrorAttributes,DefaultErrorAttributes 是 Spring Boot 的默认错误属性处理工具,它可以从请求中获取异常或错误信息,并将其封装为一个 Map 对象返回

在 Spring Boot 默认的 Error 控制器(BasicErrorController)处理错误时,会调用 DefaultErrorAttributes 的 getErrorAttributes() 方法获取错误或异常信息,并封装成 model 数据(Map 对象),返回到页面或 JSON 数据中。该 model 数据主要包含以下属性:

  • timestamp:时间戳;

  • status:错误状态码

  • error:错误的提示

  • exception:导致请求处理失败的异常对象

  • message:错误/异常消息

  • trace: 错误/异常栈信息

  • path:错误/异常抛出时所请求的URL路径

所有通过 DefaultErrorAttributes 封装到 model 数据中的属性,都可以直接在页面或 JSON 中获取。

Spring Boot 全局异常处理(熟悉)

我们知道 Spring Boot 已经提供了一套默认的异常处理机制,但是 Spring Boot 提供的默认异常处理机制却并不一定适合我们实际的业务场景,因此,我们通常会根据自身的需要对 Spring Boot 全局异常进行统一定制,例如定制错误页面,定制错误数据等。

定制错误页面

我们可以通过以下 3 种方式定制 Spring Boot 错误页面:

  • 自定义 error.html

  • 自定义动态错误页面

  • 自定义静态错误页面

自定义 error.html

我们可以直接在模板引擎文件夹(/resources/templates)下创建 error.html ,覆盖 Spring Boot 默认的错误视图页面(Whitelabel Error Page)。

自定义动态错误页面

如果 Sprng Boot 项目使用了模板引擎,当程序发生异常时,Spring Boot 的默认错误视图解析器(DefaultErrorViewResolver)就会解析模板引擎文件夹(resources/templates/)下 error 目录中的错误视图页面。

精确匹配

我们可以根据错误状态码(例如 404、500、400 等等)的不同,分别创建不同的动态错误页面(例如 404.html、500.html、400.html 等等),并将它们存放在模板引擎文件夹下的 error 目录中。当发生异常时,Spring Boot 会根据其错误状态码精确匹配到对应的错误页面上。

在 spring-boot-adminex 的模板引擎文件夹下 error 目录中,创建一个名为 404.html 的错误页面

模糊匹配

我们还可以使用 4xx.html 和 5xx.html 作为动态错误页面的文件名,并将它们存放在模板引擎文件夹下的 error 目录中,来模糊匹配对应类型的所有错误,例如 404、400 等错误状态码以“4”开头的所有异常,都会解析到动态错误页面 4xx.html 上。

在 spring-boot-adminex 的模板引擎文件夹下 error 目录中,创建一个名为 4xx.html 的错误页面

自定义静态错误页面

静态错误页面和动态错误页面的区别在于动态错误页面的数据是可以从后端动态获取的,静态只能把那个页面在前端写死(错误页为静态页面,无法识别 Thymeleaf 表达式)

错误页面优先级

以上 5 种方式均可以定制 Spring Boot 错误页面,且它们的优先级顺序为:自定义动态错误页面(精确匹配)>自定义静态错误页面(精确匹配)>自定义动态错误页面(模糊匹配)>自定义静态错误页面(模糊匹配)>自定义 error.html。

当遇到错误时,Spring Boot 会按照优先级由高到低,依次查找解析错误页,一旦找到可用的错误页面,则直接返回客户端展示。

定制错误数据

我们知道,Spring Boot 提供了一套默认的异常处理机制,其主要流程如下:

  1. 发生异常时,将请求转发到“/error”,交由 BasicErrorController(Spring Boot 默认的 Error 控制器) 进行处理;

  1. BasicErrorController 根据客户端的不同,自动适配返回的响应形式,浏览器客户端返回错误页面,机器客户端返回 JSON 数据。

  1. BasicErrorController 处理异常时,会调用 DefaultErrorAttributes(默认的错误属性处理工具) 的 getErrorAttributes() 方法获取错误数据。

我们还可以定制 Spring Boot 的错误数据,具体步骤如下。

  1. 自定义异常处理类,将请求转发到 “/error”,交由 Spring Boot 底层(BasicErrorController)进行处理,自动适配浏览器客户端和机器客户端。

  1. 通过继承 DefaultErrorAttributes 来定义一个错误属性处理工具,并在原来的基础上添加自定义的错误数据。

1. 自定义异常处理类

被 @ControllerAdvice 注解的类可以用来实现全局异常处理,这是 Spring MVC 中提供的功能,在 Spring Boot 中可以直接使用。

1)在 net.biancheng.net.exception 包内,创建一个名为 UserNotExistException 的异常类,代码如下。

package net.biancheng.www.exception;
 
/**
* 自定义异常
*/
public class UserNotExistException extends RuntimeException {
    public UserNotExistException() {
        super("用户不存在!");
    }
}

2)在 IndexController 添加以下方法,触发 UserNotExistException 异常,代码如下。

@Controller
public class IndexController {
    ......
    @GetMapping(value = {"/testException"})
    public String testException(String user) {
        if ("user".equals(user)) {
            throw new UserNotExistException();
        }
        //跳转到登录页 login.html
        return "login";
    }
}

3)在 net.biancheng.www.controller 中,创建一个名为 MyExceptionHandler 异常处理类,代码如下。

package net.biancheng.www.controller;
 
import net.biancheng.www.exception.UserNotExistException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
 
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
 
@ControllerAdvice
public class MyExceptionHandler {
    @ExceptionHandler(UserNotExistException.class)
    public String handleException(Exception e, HttpServletRequest request) {
        Map<String, Object> map = new HashMap<>();
        //向 request 对象传入错误状态码
        request.setAttribute("javax.servlet.error.status_code",500);
        //根据当前处理的异常,自定义的错误数据
        map.put("code", "user.notexist");
        map.put("message", e.getMessage());
        //将自定的错误数据传入 request 域中
        request.setAttribute("ext",map);
        return "forward:/error";
    }
}

2. 自定义错误属性处理工具

1)在 net.biancheng.www.componet 包内,创建一个错误属性处理工具类 MyErrorAttributes(继承 DefaultErrorAttributes ),通过该类我们便可以添加自定义的错误数据,代码如下。

 package net.biancheng.www.componet;
 
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
 
import java.util.Map;
//向容器中添加自定义的储物属性处理工具
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
        Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
        //添加自定义的错误数据
        errorAttributes.put("company", "www.biancheng.net");
        //获取 MyExceptionHandler 传入 request 域中的错误数据
        Map ext = (Map) webRequest.getAttribute("ext", 0);
        errorAttributes.put("ext", ext);
        return errorAttributes;
    }
}

2)在 templates/error 目录下,创建动态错误页面 5xx.html,代码如下。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>自定义 error.html</title>
</head>
<body>
<p>status:<span th:text="${status}"></span></p>
<p>error:<span th:text="${error}"></span></p>
<p>timestamp:<span th:text="${timestamp}"></span></p>
<p>message:<span th:text="${message}"></span></p>
<p>path:<span th:text="${path}"></span></p>
<!--取出定制的错误信息-->
<h3>以下为定制错误数据:</h3>
<p>company:<span th:text="${company}"></span></p>
<p>code:<span th:text="${ext.code}"></span></p>
<p>path:<span th:text="${ext.message}"></span></p>
</body>
</html>

3)启动 Spring Boot,访问“http://localhost:8080/testException?user=user”,结果如下图。

注意:为了避免拦截器干扰,建议先将拦截器屏蔽掉。

Spring Boot 注册 Web原生组件(了解)

由于 Spring Boot 默认以 Jar 包方式部署的,默认没有 web.xml,因此无法再像以前一样通过 web.xml 配置来使用 Servlet 、Filter、Listener,但 Spring Boot 提供了 2 种方式来注册这些 Web 原生组件。

  • 通过组件扫描注册

  • 使用 RegistrationBean 注册

通过组件扫描注册

Servlet 3.0 提供了以下 3 个注解:

  • @WebServlet:用于声明一个 Servlet;

  • @WebFilter:用于声明一个 Filter;

  • @WebListener:用于声明一个 Listener。

这些注解可直接标注在对应组件上,它们与在 web.xml 中的配置意义相同。每个注解都具有与 web.xml 对应的属性,可直接配置,省去了配置 web.xml 的繁琐。

想要在 SpringBoot 中注册这些原生 Web 组件,可以使用 @ServletComponentScan 注解实现,该注解可以扫描标记 @WebServlet、@WebFilter 和 @WebListener 三个注解的组件类,并将它们注册到容器中。

注意:@ServletComponentScan 注解只能标记在启动类或配置类上。

使用 RegistrationBean 注册

我们还可以在配置类中使用 RegistrationBean 来注册原生 Web 组件,不过这种方式相较于注解方式要繁琐一些。使用这种方式注册的原生 Web 组件,不再需要使用 @WebServlet 、@WebListener 和 @WebListener 等注解。

RegistrationBean 是个抽象类,负责将组件注册到 Servlet 容器中,Spring 提供了三个它的实现类,分别用来注册 Servlet、Filter 和 Listener。

  • ServletRegistrationBean:Servlet 的注册类

  • FilterRegistrationBean:Filter 的注册类

  • ServletListenerRegistrationBean:Listener 的注册类

我们可以在配置类中,使用 @Bean 注解将 ServletRegistrationBean、FilterRegistrationBean 和 ServletListenerRegistrationBean 添加 Spring 容器中,并通过它们将我们自定义的 Servlet、Filter 和 Listener 组件注册到容器中使用。

代码演示:http://c.biancheng.net/spring_boot/servlet-filter-listener.html

Spring Boot JDBC 访问数据库(了解)

对于数据访问层,无论是 SQL(关系型数据库) 还是 NOSQL(非关系型数据库),Spring Boot 都默认采用整合 Spring Data 的方式进行统一处理,通过大量自动配置,来简化我们对数据访问层的操作

导入 JDBC 场景启动器

Spring Boot 将日常企业应用研发中的各种场景都抽取出来,做成一个个的场景启动器(Starter),场景启动器中整合了该场景下各种可能用到的依赖,让用户摆脱了处理各种依赖和配置的困扰。

想要在 Spring Boot 中使用 JDBC 进行数据访问,第一步就是要在 pom.xml 中导入 JDBC 场景启动器:spring-boot-starter-data-jdbc,代码如下。

<!--导入JDBC的场景启动器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

查看 spring-boot-starter-data-jdbc 的依赖树,可以看到,该场景启动器默认引入了一个数据源:HikariCP,如下图所示。

导入数据库驱动

JDBC 的场景启动器中并没有导入数据库驱动,我们需要根据自身的需求引入所需的数据库驱动。例如,访问 MySQL 数据库时,需要导入 MySQL 的数据库驱动:mysql-connector-java,示例代码如下。

<!--导入数据库驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

配置数据源

在导入了 JDBC 场景启动器和数据库驱动后,接下来我们就可以在配置文件(application.properties/yml)中配置数据源了,示例代码(application.yml)如下。

#数据源连接信息
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/bianchengbang_jdbc
    driver-class-name: com.mysql.cj.jdbc.Driver

测试

Spring Boot 提供了一个名为 JdbcTemplate 的轻量级数据访问工具,它是对 JDBC 的封装。Spring Boot 对 JdbcTemplate 提供了默认自动配置,我们可以直接使用 @Autowired 或构造函数将它注入到 bean 中使用。

package net.biancheng.www;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.sql.SQLException;

@SpringBootTest
class SpringBootJdbcApplicationTests {
    //数据源组件
    @Autowired
    DataSource dataSource;
    //用于访问数据库的组件
    @Autowired
    JdbcTemplate jdbcTemplate;

    @Test
    void contextLoads() throws SQLException {
        System.out.println("默认数据源为:" + dataSource.getClass());
        System.out.println("数据库连接实例:" + dataSource.getConnection());
        //访问数据库
        Integer i = jdbcTemplate.queryForObject("SELECT count(*) from `user`", Integer.class);
        System.out.println("user 表中共有" + i + "条数据。");
    }
}

*Spring Boot 数据源配置原理(了解)

https://blog.csdn.net/youandme520/article/details/122893581

Spring Boot 整合Druid 数据源(了解)

Spring Boot 2.x 默认使用 HikariCP 作为数据源,我们只要在项目中导入了 Spring Boot 的 JDBC 场景启动器,便可以使用 HikariCP 数据源获取数据库连接,对数据库进行增删改查等操作。Spring Boot 使用 HikariCP 作为其默认数据源,但其中有一个十分重要的条件

@ConditionalOnMissingBean(DataSource.class) 的含义是:当容器中没有 DataSource(数据源类)时,Spring Boot 才会使用 HikariCP 作为其默认数据源。 也就是说,若我们向容器中添加 Druid 数据源类(DruidDataSource,继承自 DataSource)的对象时,Spring Boot 就会使用 Druid 作为其数据源,而不再使用 HikariCP。

HikariCP 是目前市面上性能最好的数据源产品,但在实际的开发过程中,企业往往更青睐于另一款数据源产品:Druid,它是目前国内使用范围最广的数据源产品。

Druid 是阿里巴巴推出的一款开源的高性能数据源产品,Druid 支持所有 JDBC 兼容的数据库,包括 Oracle、MySQL、SQL Server 和 H2 等等。Druid 不仅结合了 C3P0、DBCP 和 PROXOOL 等数据源产品的优点,同时还加入了强大的监控功能。通过 Druid 的监控功能,可以实时观察数据库连接池和 SQL 的运行情况,帮助用户及时排查出系统中存在的问题。

Druid 不是 Spring Boot 内部提供的技术,它属于第三方技术,我们可以通过以下两种方式进行整合:

  • 自定义整合 Druid

  • 通过 starter 整合 Druid

深入了解:http://c.biancheng.net/spring_boot/druid.html

Spring Boot 整合MyBatis (了解)

MyBatis 是一个半自动化的 ORM 框架,所谓半自动化是指 MyBatis 只支持将数据库查出的数据映射到 POJO 实体类上,而实体到数据库的映射则需要我们自己编写 SQL 语句实现,相较于Hibernate 这种完全自动化的框架,Mybatis 更加灵活,我们可以根据自身的需求编写 sql 语句来实现复杂的数据库操作。

随着 Spring Boot 越来越流行,越来越多的被厂商及开发者所认可,MyBatis 也开发了一套基于 Spring Boot 模式的 starter:mybatis-spring-boot-starter。本节我们就介绍下如何在 Spring Boot 项目中整合 MyBatis。

引入依赖

Spring Boot 整合 MyBatis 的第一步,就是在项目的 pom.xml 中引入 mybatis-spring-boot-starter 的依赖,示例代码如下。

<!--引入 mybatis-spring-boot-starter 的依赖-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

配置 MyBatis

在 Spring Boot 的配置文件(application.properties/yml)中对 MyBatis 进行配置,例如指定 mapper.xml 的位置、实体类的位置、是否开启驼峰命名法等等,示例代码如下。

###################################### MyBatis 配置######################################
mybatis:
  # 指定 mapper.xml 的位置
  mapper-locations: classpath:mybatis/mapper/*.xml
  #扫描实体类的位置,在此处指明扫描实体类的包,在 mapper.xml 中就可以不写实体类的全路径名
  type-aliases-package: net.biancheng.www.bean
  configuration:
    #默认开启驼峰命名法,可以不用设置该属性
    map-underscore-to-camel-case: true  

注意:使用 MyBatis 时,必须配置数据源信息,例如数据库 URL、数据库用户型、数据库密码和数据库驱动等。

创建 Mapper 接口

在 net.biancheng.www.mapper 中创建一个 UserMapper 接口,并在该类上使用 @Mapper 注解,代码如下。

package net.biancheng.www.mapper;

import net.biancheng.www.bean.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    //通过用户名密码查询用户数据
    User getByUserNameAndPassword(User user);
}

当 mapper 接口较多时,我们可以在 Spring Boot 主启动类上使用 @MapperScan 注解扫描指定包下的 mapper 接口,而不再需要在每个 mapper 接口上都标注 @Mapper 注解。

创建 Mapper 映射文件

在配置文件 application.properties/yml 通过 mybatis.mapper-locations 指定的位置中创建 UserMapper.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="net.biancheng.www.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="User">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="user_id" jdbcType="VARCHAR" property="userId"/>
        <result column="user_name" jdbcType="VARCHAR" property="userName"/>
        <result column="password" jdbcType="VARCHAR" property="password"/>
        <result column="email" jdbcType="VARCHAR" property="email"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, user_id, user_name, password, email
    </sql>
    <!--根据用户名密码查询用户信息-->
    <!--application.yml 中通过 type-aliases-package 指定了实体类的为了,因此-->
    <select id="getByUserNameAndPassword" resultType="User">
        select *
        from user
        where user_name = #{userName,jdbcType=VARCHAR}
          and password = #{password,jdbcType=VARCHAR}
    </select>
</mapper>

使用 Mapper 进行开发时,需要遵循以下规则:

  • mapper 映射文件中 namespace 必须与对应的 mapper 接口的完全限定名一致。

  • mapper 映射文件中 statement 的 id 必须与 mapper 接口中的方法的方法名一致

  • mapper 映射文件中 statement 的 parameterType 指定的类型必须与 mapper 接口中方法的参数类型一致。

  • mapper 映射文件中 statement 的 resultType 指定的类型必须与 mapper 接口中方法的返回值类型一致。

注解方式

通过上面的学习,我们知道 mapper 映射文件其实就是一个 XML 配置文件,它存在 XML 配置文件的通病,即编写繁琐,容易出错。即使是一个十分简单项目,涉及的 SQL 语句也都十分简单,我们仍然需要花费一定的时间在mapper 映射文件的配置上。

为了解决这个问题,MyBatis 针对实际实际业务中使用最多的“增伤改查”操作,分别提供了以下注解来替换 mapper 映射文件,简化配置:

  • @Select

  • @Insert

  • @Update

  • @Delete

通过以上注解,基本可以满足我们对数据库的增删改查操作,示例代码如下。

package net.biancheng.www.mapper;

import net.biancheng.www.bean.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper
public interface UserMapper {
    @Select("select * from user where user_name = #{userName,jdbcType=VARCHAR} and password = #{password,jdbcType=VARCHAR}")
    List<User> getByUserNameAndPassword(User user);

    @Delete("delete from user where id = #{id,jdbcType=INTEGER}")
    int deleteByPrimaryKey(Integer id);

    @Insert("insert into user ( user_id, user_name, password, email)" +
            "values ( #{userId,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR})")
    int insert(User record);


    @Update(" update user" +
            "    set user_id = #{userId,jdbcType=VARCHAR},\n" +
            "      user_name = #{userName,jdbcType=VARCHAR},\n" +
            "      password = #{password,jdbcType=VARCHAR},\n" +
            "      email = #{email,jdbcType=VARCHAR}\n" +
            "    where id = #{id,jdbcType=INTEGER}")
    int updateByPrimaryKey(User record);
}

注意事项

mapper 接口中的任何一个方法,都只能使用一种配置方式,即注解和 mapper 映射文件二选一,但不同方法之间,这两种方式则可以混合使用,例如方法 1 使用注解方式,方法 2 使用 mapper 映射文件方式。

我们可以根据 SQL 的复杂程度,选择不同的方式来提高开发效率。

  • 如果没有复杂的连接查询,我们可以使用注解的方式来简化配置;

  • 如果涉及的 sql 较为复杂时,则使用 XML (mapper 映射文件)的方式更好一些。

Spring Boot 自定义starter (了解)

starter 是 SpringBoot 中一种非常重要的机制,它可以繁杂的配置统一集成到 starter 中,我们只需要通过 maven 将 starter 依赖引入到项目中,SpringBoot 就能自动扫描并加载相应的默认配置。starter 的出现让开发人员从繁琐的框架配置中解放出来,将更多的精力专注于业务逻辑的开发,极大的提高了开发效率。

在一些特殊情况下,我们也可以将一些通用功能封装成自定义的 starter 进行使用,本节我们将为您详细介绍如何自定义 starter。

命名规范

SpringBoot 提供的 starter 以 spring-boot-starter-xxx 的形式命名。为了与 SpringBoot 生态提供的 starter 进行区分,官方建议第三方开发者或技术(例如 Druid、Mybatis 等等)厂商自定义的 starter 使用 xxx-spring-boot-starter 的形式命名,例如 mybatis-spring-boot-starter、druid-spring-boot-starter 等等。

模块规范

Spring Boot 官方建议我们在自定义 starter 时,创建两个 Module :autoConfigure Module 和 starter Module,其中 starter Module 依赖于 autoConfigure Module。当然,这只是 Spring Boot 官方的建议,并不是硬性规定,若不需要自动配置代码和依赖项目分离,我们也可以将它们组合到同一个 Module 里。

自定义 starter

自定义 starter 可以分为以下 7 步:

  1. 创建工程

  1. 添加 POM 依赖

  1. 定义 propertie 类

  1. 定义 Service 类

  1. 定义配置类

  1. 创建 spring.factories文件

  1. 构建 starter

代码演示:https://blog.csdn.net/youandme520/article/details/122897671

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/141958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux网络管理】之ip、nmcli

文章目录一、验证网络配置1. ip link命令将列出系统上可用的所有网络接口2. ip命令查看设备和地址信息3. ip命令显示性能统计信息4. ip命令以及route选项来显示路由信息5. 添加-6选项可显示IPv6路由器二、查看联网信息1.nmcli dev status命令可显示所有网络设备的状态2.nmcli c…

JDBC基础内容

JDBC的概念&#xff1a; JDBC就是使用java语言操作关系数据库的一套API&#xff0c;全称为(java DataBase Connectivity)-----java数据库连接. JDBC的本质&#xff1a; 是由sun公司定义的一套操作所有关系型数据库的规则&#xff0c;即接口&#xff0c;各个数据库厂商去实现…

更智能行车记录仪,4K画质超清晰,凌度行车记录仪 4K版上手

行车记录仪是很多车友的必需品&#xff0c;这两年行车记录仪的提升非常明显&#xff0c;如今市面上已经能够找到很多4K分辨率的产品了&#xff0c;相比于早期只要有480P分辨率的记录仪&#xff0c;清晰度和可靠性都更加出色&#xff0c;确实有升级的必要。 这两天我尝试了一款华…

P2141 [NOIP2014 普及组] 珠心算测验————C++

题目 [NOIP2014 普及组] 珠心算测验 题目描述 珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术。珠心算训练&#xff0c;既能够开发智力&#xff0c;又能够为日常生活带来很多便利&#xff0c;因而在很多学校得到普及。 某学校的珠心算老师采用一种快速考…

基于python机器学习及深度学习在空间模拟与时间预测领域中的实践技术应用

机器学习 理论知识 1.1.1 课程目标 了解机器学习的发展历史、计算原理、基本定义&#xff0c;熟悉机器学习方法的分类&#xff0c;常用机器学习方法&#xff0c; 以及模型的评估与选择&#xff1b;熟悉数据预处理的流程&#xff0c;掌握 python 程序包的使用&#xff1b;理解机…

美国培生教育集团下PYP Readers系列的音乐是怎样制造的,读了吗?

很多小朋友都对音乐很感兴趣。 TA可能会喜欢Pop Music , 可能会喜欢Rap Music&#xff0c; 也可能会喜欢Rock&Roll...... 那么TA可能会想&#xff0c;这些音乐到底是怎样产生的呢&#xff1f; 没错&#xff0c;乐器&#xff08;Instrument&#xff09;&#xff01; 好书推荐…

【YOLO系列】YOLO V1论文总结

目标检测论文总结 【RCNN系列】 RCNN Fast RCNN Faster RCNN 【YOLO系列】 YOLO V1 文章目录目标检测论文总结前言一、Pipeline二、模型设计1.训练阶段2.损失函数2.1.框回归损失2.2.置信度损失2.3.分类损失总结前言 一些经典论文的总结。 一、Pipeline YOLO的pipeline很简单…

排查 Hive 任务导致磁盘写满的过程

计算的中间结果会写到 HDFS&#xff0c;如果用户写的SQL 有问题。如用户的 SQL 如下&#xff1a; select * from A join B where a.dt20230101 and b.dt20230101&#xff0c;如 A 表 dt20230101 分区 1000万行&#xff0c;B 表 dt20230101 有 1万行记录。由于 SQL 没有关联条件…

Druid 连接池技术的使用

文章目录官网链接连接性能消耗问题分析数据库连接池的作用市面常见连接池产品和对比国货之光druid连接池使用导入druid依赖硬编码方式&#xff08;了解&#xff09;软编码方式druid配置(了解)官网链接 http://www.apache-druid.cn/GettingStarted/chapter-1.html 连接性能消耗…

[Vue组件及组件之间的通信]一.Vue脚手架的使用;二.Vue的组件和组件之间的通信

目录 一.Vue脚手架的使用 1.通过命令行使用vue-cli的指令创建&#xff1a;vue init webpack 项目名称 2使用webStorm软件&#xff1a;本质仍然使用vue脚手架 3.使用vue ui创建&#xff1a;vue提供的图形化的操作界面 二.Vue的组件和组件之间的通信 1.组件&#xff1a;是vu…

数字签名与签名验证过程

1.1 生成数字签名 1 利用RSA算法生成公钥、私钥。私钥由密钥持有者自主保存&#xff0c;公钥可对外发布。 2 准备好待签名的文档。 3 利用哈希算法&#xff08;HASH&#xff09;&#xff0c;生成待签名文档的摘要。&#xff08;文档摘要&#xff09; 4 利用签名者的私钥&am…

谷粒学院——第十八章、统计分析

准备工作 需求分析 1、统计在线教育项目中&#xff0c;每一天有多少注册人数 2、把统计出来的注册人数&#xff0c;使用图表显示出来 创建数据库表 创建工程 service_statistics 配置文件 # 服务端口 server.port8008# 服务名 spring.application.nameservice-statistic…

Python基础(二十一):面向对象深入了解

文章目录 面向对象深入了解 一、魔法方法 1、__init__() 2、__str__() 3、__del__()

数据结构入门——二叉树(C语言实现)

数据结构入门——二叉树一. 树概念及结构1.1 树的概念1.2 树的相关概念1.3 树的表示1.4 树的应用二. 二叉树概念及结构2.1 二叉树的概念2.2 特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储结构三. 二叉树的顺序结构及其实现(堆的实现)3.1 二叉树的顺序结构3.2 堆的实现(以大堆为…

要想宝宝吃得好,粮仓就要保护好,做好3点保护粮仓,防止皲裂

众所周知&#xff0c;母乳喂养的妈妈并不容易&#xff0c;因为母乳喂养也有很多“难题”&#xff0c;也会很痛&#xff0c;比如开奶痛、挂奶、堵奶等&#xff0c;疼痛的程度不亚于子宫收缩。还有一个&#xff0c;牛奶&#xff0c;牛奶。.头皲裂的时候&#xff0c;真的是含泪喂奶…

Python实现可视化大屏数据

参考网址如下&#xff1a; 【Python】全网最新最全Pyecharts可视化教程(三)&#xff1a;制作多个子图_51CTO博客_python数据可视化pyecharts使用pyecharts拖拉&#xff0c;拽&#xff0c;创建大屏展示 - 简书 (jianshu.com) 智慧大屏是如何实现数据可视化的&#xff1f; - 知…

调查问卷考试问卷创建生成工具助手小程序开发

调查问卷考试问卷创建生成工具助手小程序开发 问卷调查考试软件&#xff0c;可以自由让每一个用户自由发起调查问卷、考试问卷。发布的问卷允许控制问卷的搜集、回答等各个环节的设置&#xff0c;同时支持系统模板问卷&#xff0c;选用模板问卷后可以一键创建属于自己的问卷&a…

JVM基础知识总结

日常工作中接触到的jvm相关的知识&#xff0c;和jvm相关书籍中汇总总结一下jvm相关基础知识&#xff0c;作为对jvm的了解。 文章目录jvm运行时数据区域程序计数器java虚拟机栈堆heap非堆内存 nonheap方法区直接内存 Direct Memory类加载机制类的加载过程类加载器加载过程的详细…

阿里云网络解决方案架构师任江波:全球一张网,支撑游戏业务高效互联

2022 年 8 月 30 日&#xff0c;阿里云用户组&#xff08;AUG&#xff09;第 9 期活动在北京举办。活动现场&#xff0c;阿里云网络解决方案架构师任江波&#xff0c;向参会企业代表分享了全球一张网&#xff0c;支撑游戏业务高效互联。本文根据演讲内容整理而成。 在座的很多我…

Web3中文|年度回顾:2022年Web3的发展情况

老生常谈的话题都有一个共同点&#xff0c;那就是它总是包含着一些无趣但至关重要的真理。例如&#xff0c;众所皆知天空是蓝色的&#xff0c;所以大家并不会把它纳入日常讨论&#xff0c;但这并不代表它对物理学、生物学和其他学科而言没有价值。 当我们回望2022年的Web3领域…