SpringBoot

01-SpringBoot概述

SpringBoot 概念

SpringBoot提供了一种快速使用Spring的方式,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。2014 年 4 月,Spring Boot 1.0.0 发布。Spring的顶级项目之一。

spring官方的网站:https://spring.io/

image-20211118124534634

SpringBoot是由Pivotal团队在2013年开始研发、2014年4月发布第一个版本的全新开源的轻量级框架。它基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决

Spring 缺点

1)配置繁琐

1
2
3
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多 XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。 	

Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。 所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但它要求的回报也不少。

2)依赖繁琐

1
项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导 入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。

image-20220611180907558

SpringBoot主要特性

1、 SpringBoot Starter:他将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven或Gradle构建中;

1
起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖 ,这些东西加在一起即支持某项功能。 简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能

2、 使编码变得简单,SpringBoot采用 JavaConfig的方式对Spring进行配置,并且提供了大量的注解,极大的提高了工作效率。

1
提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标,健康检测、外部配置等。 

3、 自动配置:SpringBoot的自动配置特性利用了Spring对条件化配置的支持,合理地推测应用所需的bean并自动化配置他们;

1
Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定 Spring配置应该用哪个,不该用哪个。该过程是SpringBoot自动完成的。

4、 使部署变得简单,SpringBoot内置了三种Servlet容器,Tomcat,Jetty,undertow.我们只需要一个Java的运行环境就可以跑SpringBoot的项目了,SpringBoot的项目可以打成一个jar包。

Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速使用 Spring 的方式

小结

SpringBoot提供了一种快速开发Spring项目的方式,而不是对Spring功能上的增强。

Spring的缺点:

  • 配置繁琐
  • 依赖繁琐

SpringBoot功能:

  • 自动配置
  • 起步依赖:依赖传递
  • 辅助功能

02-SpringBoot快速入门

需求案例

**需求:**搭建SpringBoot工程,定义HelloController.hello()方法,返回”Hello SpringBoot!”。

实现步骤:

maven方式

① 创建Maven项目

image-20211118130004312

② 导入SpringBoot起步依赖

文档中可以查看到初始化方法

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--SpringBoot 工程需要继承的父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<!--web开发的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

③ 定义Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.pointink.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author pointink
* Created on 2021/11/18.
*/
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello Spring Boot!";
}
}

④ 编写引导类

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

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* 引导类,SpringBoot项目的入口
* @author pointink
* Created on 2021/11/18.
*/
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class,args);
}
}

⑤ 启动测试

image-20211118131144872

Spring Initializr方式

快速构建SpringBoot工程的方式

image-20211118174936157

Spring initializr会创建基础的启动类和添加依赖等。可以直接启动。

需要网络才行

官网线上方式

勾勾选选,可以创建项目,然后导入工程

image-20220905083424530

小结

  • SpringBoot在创建项目时,使用jar的打包方式。
  • SpringBoot的引导类,是项目入口,运行main方法就可以启动项目。
  • 使用SpringBoot和Spring构建的项目,业务代码编写方式完全一样。

03-SpringBoot起步依赖原理分析

image-20211118183154151

原理:我们的工程依赖spring-boot-starter-parent,spring-boot-starter-parent依赖spring-boot-dependencies。我们在自己的工程引用时,在spring-boot-dependenciesdependencyManagement中定义好了很多jar包的版本,通过maven的依赖传递特性从而实现了版本的统一管理,就不用写版本了。

小结

  • 在spring-boot-starter-parent中定义了各种技术的版本信息,组合了一套最优搭配的技术版本。
  • 在各种starter中,定义了完成该功能需要的坐标合集,其中大部分版本信息来自于父工程
  • 我们的工程继承parent,引入starter后,通过依赖传递,就可以简单方便获得需要的jar包,并且不会存在版本冲突等问题。

04-SpringBoot配置

配置文件分类

SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置。

1
2
3
4
5
6
# 比如Tomcat端口模式是8080,要修改的话就显示声明即可。
# properties:
server.port=8080
# yml:
server:
port: 8080

yml是以数据为核心的,简介。

image-20211118185300338

小结:

  • Spring Boot 有2中配置文件类型:properties和yml/yaml
  • 默认的配置文件名叫application.xx
  • 同一级目录下优先级为: properties>yml>yaml。高优先级的同名配置会覆盖低优先级的。不同名配置的会结合。

yaml基本语法

1
YAML全称是 YAML Ain't Markup Language 。YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅 读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP 等。YML文件是以数据为核心的,比传统的xml方式更加简洁。 YAML文件的扩展名可以使用.yml或者.yaml。

基础语法:

  • 大小写敏感
  • 数据值前边必须有空格,作为分隔符
  • 使用缩进表示层级关系
  • 缩进时不允许使用Tab键,只允许使用空格(各个系统 Tab对应的 空格数目可能不同,导致层次混乱)。
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
  • #表示注释,从这个字符一直到行尾,都会被解析器忽略。

yaml数据格式

  • 对象(map):键值对的集合

image-20211118185703314

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

image-20211118185905800

  • 集合写法
1
2
3
4
5
6
7
8
9
10
11
12
students:
- name: zhangsan
age: 18
score: 100
- name: lisi
age: 28
score: 88
- name: wangwu
age: 38
score: 90
#map集合形式
maps: {"name":"zhangsan", "age": "15"}
  • 纯量:单个的、不可再分的值

image-20211118190106147

识别的意思就是msg2会换行输出

  • 参数引用

image-20211118190410334

小结:

  • 配置文件类型
    • properties
    • yml/yaml:注意空格
  • yaml:简介,以数据为核心
    • 基本语法
      • 大小写敏感
      • 数据值前边必须有空格,作为分隔符
      • 使用空格缩进表示层级关系,相同缩进表示同一级
    • 数据格式
      • 对象
      • 数组:使用’-‘表示数据每个元素
      • 参数引用:${key}

获取数据

  • @Value

image-20211118192436889

  • Environment

image-20211118192816066

  • @ConfigurationProperties

image-20211118193608237

复杂版:

image-20211118194839979

如果在yml中写实体属性想要有提示,则可以添加依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

如果实体类有提示:Re-run Spring Boot Configuration Annotation Processor to update generated metadata。不影响代码执行。只是提醒用户,进行必要的重新编译。再当文件进行了有效的更新时,该提示也会消失。

profile

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

image-20220611180714987

1) profile配置方式

  • 多profile文件方式

image-20211118195756930

  • yml多文档方式

image-20211118210259503

一般不用,写一个里面太乱了。分文件写比较清晰。

2) profile激活方式

  • 配置文件

上面那种spring.profiles.active=dev的形式指定激活的配置文件

  • 虚拟机参数

上面配置文件的方式可能会让人有疑问:我部署过后还得改配置文件,比较麻烦。

所以还可以通过配置vm options= -Dspring.profiles.active=prod来指定,这样本地默认使用dev,生产默认使用prod

image-20211118211418876

  • 命令行参数

还可以通过设置命令行的那种,--spring.profiles.active=test

image-20211118211540768

当然,上面是在idea里面配置的,实际中我们会将工程打包为jar,然后通过命令行激活。

image-20211118212508271

image-20211118212728951

小结:

  • profiel是用来完成不同环境下,配置动态切换功能的
  • profile配置方式
    • 多profile文件方式:提供多个配置文件,每个代表一种环境
      • application-dev.properties/yml 开发环境
      • application-test.properties/yml 测试环境
      • application-prod.properties/yml 生产环境
    • yml多文档形式
      • 在yml中使用 — 分隔不同配置
    • profile激活方式
      • 配置文件:在配置文件中配置:spring.profiles.active=dev 【本地开发的时候用这种形式指定dev环境】
      • 虚拟机参数:在VM options指定: -Dspring.profies.active=dev
      • 命令行参数: java -jar xxx.jar –spring.profiles.active=prod【项目部署的时候用这种形式指定prod环境】

实践起来就是本地区分好不同的配置文件,然后使用dev环境,然后在远程服务器通过设置vm虚拟机参数/命令行参数,指向test测试/prod生产环境部署

项目内部配置文件加载顺序

Springboot程序启动时,会从以下位置加载配置文件:

  • 1、file:./config/:当前项目下的/config目录下==(即打成的jar包同级目录config下的配置文件)==
  • 2、file:./ :当前项目的根目录==(即打成的jar包同级目录)==
  • 3、classpath:/config/:classpath的/config目录==(resources下的config目录下的配置文件) ==
  • 4、classpath:/ :classpath的根目录==(resources下的配置文件)==

加载顺序为上文的排列顺序,高优先级配置的属性会生效。不同的属性不会覆盖,会整合在一起。

image-20220303210624473

项目外部配置加载顺序

详见官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

有17种,顺序对应着优先级。

文件上传限制配置

文件上传限制可以分为:前端请求拦截,后端请求拦截,nginx拦截

此处讲SpringBoot 上传文件报错,请求大小超过了配置的最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
the request was rejected because its size (79637511) exceeds the configured maximum (10485760)
解决:
SpringBoot 1.4之前:
spring.servlet.multipart.max-file-size=120MB # 最大文件大小。单位:“MB”或“KB”.
spring.servlet.multipart.max-request-size=120MB # 最大请求大小.单位:“MB”或“KB”.
SpringBoot 1.4之后:
spring.http.multipart.maxFileSize=100Mb
spring.http.multipart.maxRequestSize=100Mb
spring boot 2.x:
spring:
servlet:
multipart:
max-file-size: 64000MB
max-request-size: 6400MB

05-SpringBoot整合框架

整合Junit

SpringInitilazr快速构建的工程会帮我们引入依赖并创建测试类

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

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.pointink.springbootprofiles;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

// SpringBoot2.2及以前
//@RunWith(SpringRunner.class)
//@SpringBootTest(classes = 启动类.class)
// SpringBoot2.2以后
@SpringBootTest
class SpringbootProfilesApplicationTests {
@Test
void contextLoads() {
}
}

PS:注意

  • springboot2.2后依赖的是junit5,不需要加@RunWith(SpringRunner.class)
  • 如果测试包和工程包一致。则不需要写@SpringBootTest(classes = 启动类.class),直接用@SpringBootTest()就行了

整合redis

怎么用redis作缓存

image-20220611180823336

① 搭建SpringBoot工程

image-20211118222432732

② 引入redis起步依赖

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

③ 配置redis相关属性

本地的redis则不用配置,否则要配置远程redis信息。

1
2
3
4
spring:
redis:
host: 127.0.0.1 # redis的主机ip
port: 6379 # 端口

④ 注入RedisTemplate模板

1
2
@Autowired
private RedisTemplate redisTemplate;

⑤ 编写测试方法,测试

image-20211118223924813

对先查redis再查数据库的流程的代码实现

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
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserMapper userMapper;

@Autowired
private RedisTemplate redisTemplate;

@Override
public List<User> findAllUser() {

//1.获取redis中的数据
List<User> list = (List<User>) redisTemplate.boundValueOps("key_all").get();
//2.判断 是否有,如果有则返回,如果没有则从mysql中获取设置到redis中再返回
if (list != null && list.size() > 0) {
return list;
}
List<User> allUser = userMapper.findAllUser();

//3 从mysql中获取设置到redis中再返回
redisTemplate.boundValueOps("key_all").set(allUser);

return allUser;
}
}

(5)启动redis

如图双击exe启动

image-20230922151043835

(6)测试,如下图所示

image-20230922151050286

再次访问,则从redis中获取数据了。

redis的序列化机制

如上图所示,出现了乱码,这个是由于redis的默认的序列化机制导致的。这里需要注意下:并不是错误,由于序列化机制,导致我们数据无法正常显示。如果有代码的方式获取则是可以获取到数据的。

1
2
3
4
5
6
7
8
9
10
1,默认的情况下redisTemplate操作key vlaue的时候 必须要求 key一定实现序列化 value 也需要实现序列化
2,默认的情况下redisTemplate使用JDK自带的序列化机制:JdkSerializationRedisSerializer
3,JDK自带的序列化机制中要求需要key 和value 都需要实现Serializable接口
4. RedisTemplate支持默认以下几种序列化机制:机制都实现了RedisSerializer接口
+ OxmSerializer
+ GenericJackson2JsonRedisSerializer
+ GenericToStringSerializer
+ StringRedisSerializer
+ JdkSerializationRedisSerializer
+ Jackson2JsonRedisSerializer

image-20230922151055171

我们可以进行自定义序列化机制:例如:我们定义key 为字符串序列化机制,value:为JDK自带的方式则,应当处理如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@SpringBootApplication
@MapperScan(basePackages = "com.nbchen.dao")
//MapperScan 用于扫描指定包下的所有的接口,将接口产生代理对象交给spriing容器
public class MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class,args);
}

@Bean
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//设置key的值为字符串序列化方式 那么在使用过程中key 一定只能是字符串
template.setKeySerializer(new StringRedisSerializer());
//设置value的序列化机制为JDK自带的方式
template.setValueSerializer(new JdkSerializationRedisSerializer());
return template;
}

}

重启并重新测试如下效果:

image-20230922151101446

总结:

1
在工作中,根据我们业务的需要进行设置和选择,如果没有合适的还可以自己定义。只要实现RedisSerializer接口即可。

整合mybatis

① 搭建SpringBoot工程

image-20211118224258699

② 引入mybatis起步依赖,添加mysql驱动

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

③ 编写DataSource和MyBatis相关配置

  • mysql8以前
1
2
3
4
5
6
7
# datasource 数据源信息
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///pointink_test
username: root
password: root
  • mysql8以后
1
2
3
4
5
6
7
# datasource 数据源信息
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///pointink_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: root

④ 定义表和实体类

1
2
3
4
5
6
7
8
9
10
11
# 数据库pointink_test创建表
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`password` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

INSERT INTO `t_user` VALUES (3, 'zhangsan', '123');
INSERT INTO `t_user` VALUES (4, 'lsii', '123');

实体

1
2
3
4
5
6
@Data
public class User {
private int id;
private String username;
private String password;
}

⑤ 编写dao和mapper文件/纯注解开发

1
2
3
4
5
6
@Mapper
// @Repository 这里加注解是为了注入的时候某些版本的IDEA不会报红提示.不加也可以
public interface UserMapper {
@Select("select * from t_user")
public List<User> findAll();
}

创建启动类,加入mapper接口注解扫描

1
2
3
4
5
6
7
8
@SpringBootApplication
@MapperScan(basePackages = "com.nbchen.dao")
//MapperScan 用于扫描指定包下的所有的接口,将接口产生代理对象交给spriing容器
public class MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class,args);
}
}

⑥ 测试

image-20211118225549894

如果是mapper文件,则要如下配置:

  • yml配置文件增加配置
1
2
3
4
5
# mybatis
mybatis:
# config-location: # 指定mybatis核心配置文件
mapper-locations: classpath:mapper/*Mapper.xml # mapper映射文件路径
type-aliases-package: com.pointink.springbootmybatis.domain # 别名(实际开发中我还是写全名,可以直接跳转过去)
  • 编写mapper.xml
1
2
3
4
5
6
7
8
9
<?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.pointink.springbootmybatis.mapper.UserXmlMapper">
<select id="findXmlAll" resultType="User">
select * from t_user
</select>
</mapper>

image-20211118230622074

整合dubbo

06-SpringBoot自动配置

在我们使用springboot的时候,能带来的方便性和便利性,不需要配置便可以实现相关的使用,开发效率极大的提升。

实际上,springboot本身的基础依赖中封装了许许多多的配置帮我们自动完成了配置了。那么它是如何实现的呢?

Condition

Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作。

condition接口是spring4之后提供给了的接口,增加条件判断功能,用于选择性的创建Bean对象到spring容器中。

思考一个问题 ?

​ 我们之前用过springboot整合redis 实现的步骤:就是添加redis起步依赖之后,直接就可以使用从spring容器中获取注入RedisTemplate对象了,而不需要创建该对象放到spring容器中了.意味着Spring boot redis的起步依赖已经能自动的创建该redisTemplate对象加入到spring容器中了。这里应用的重要的一个点就是condition的应用。

思考:SpringBoot是如何知道要创建哪个Bean的?比如SpringBoot是如 何知道要创建RedisTemplate的?

现象:

  • 没有加入redis的时候

image-20211119082358827

  • 加入redis依赖后,就可以获取到bean了

image-20211119082535482

那么SpringBoot是如何做到这一步的?

需求:

1
2
3
在 Spring 的 IOC 容器中有一个 TestCondition 的 Bean,现要求: 
1. 导入Jedis坐标后,加载该Bean,没导入,则不加载。
2. 将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定。
  • 体验一下@Conditional

image-20211119084124097

  • 升级版,动态指定需要导入的坐标,如果有导入则动态创建Bean

image-20211119121847271

而SpringBoot正是封装了更多更复杂的注解,得到有导入包,则自动创建bean的效果。详见:spring-boot-autoconfigure

小结:

  • 自定义条件:

    ① 定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回 boolean值 。 matches 方法两个参数
    context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。
    metadata:元数据对象,用于获取注解属性
    ② 判断条件: 在初始化Bean时,使用 @Conditional(条件类.class)注解

  • SpringBoot 提供的常用条件注解

    • ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean

    • ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean

    • ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean

image-20230922151113993

切换内置web服务器

SpringBoot的web环境中默认使用tomcat作为内置服务器,其实SpringBoot提供了4中内置服务器供我们选择,我们可以很方便的进行切换。

image-20211119123405119

以上如图所示:

1
2
3
4
5
6
web容器有4种类型:
+ tomcat容器
+ jetty
+ netty
+ undertow
默认spring-boot-starter-web加入的是tomcat ,所以根据上图配置,会配置tomcat作为web容器

启动时如下:

image-20230922151123781

(4)可以尝试修改web容器:

​ 如上,我们可以通过修改web容器,根据业务需求使用性能更优越的等等其他的web容器。这里我们演示使用jetty作为web容器。

在pom.xml中排出tomcat依赖,添加jetty依赖即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>

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

再次启动如下图所示:

image-20230922151129568

Enable注解原理

SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用@Import注 解导入一些配置类,实现Bean的动态加载。

思考:SpringBoot 工程是否可以直接获取jar包(第三方)中定义的Bean? => 不行

image-20211119125042129

那为什么我们引入第三方的jar包的启动依赖就可以获取到对应的bean了呢?

  • 可以将第三方的包也扫描或者导入=>解决方式太low了

image-20211119125232371

image-20230922151235315

@Enable*初体验

image-20211119125809048

@Import详解

1
`@Enable*`底层依赖于`@Import`注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法: 

① 导入Bean
② 导入配置类
③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类

image-20211119130531399

④ 导入 ImportBeanDefinitionRegistrar 实现类。

image-20211119130942547

@EnableAutoConfiguration详解

  • @EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class)来加载配置类。
  • 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载 这些配置类,初始化Bean
  • 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean

原理图:

image-20211119194501664

自定义starter

需求:

自定义redis-starter。要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean

参考mybatis-spring-boot-starter的源码:

image-20211119200620007

步骤分析

① 创建 redis-spring-boot-autoconfigure 模块
② 创建 redis-spring-boot-starter 模块,依赖 redis-spring- boot-autoconfigure的模块
③ 在 redis-spring-boot-autoconfigure 模块中初始化 Jedis 的 Bean。并定义META-INF/spring.factories 文件
④ 在测试模块中引入自定义的 redis-starter 依赖,测试获取 Jedis 的Bean,操作 redis。

实现

image-20211119221248618

07-SpringBoot事件监听

java监听机制

SpringBoot的监听机制,其实是对java提供的事件监听机制的封装,随便找个包看下:底层是对java事件对象的封装

image-20211120095439770

java中的事件监听机制定义了以下几个角色:

  • ①事件:Event,继承了java.util.EventObject类的对象

  • ②事件源:Source,任意对象Object

  • ③监听器:Listener,实现java.util.EventListener接口的对象

SpringBoot监听机制

SpringBoot在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成一些操作。

ApplicationContextInitializer,SpringApplicationRunListener,CommandLineRunner,ApplicationRunner

CommandLineRunner&ApplicationRunner

image-20230922151157235

ApplicationContextInitializer

image-20211120093904555

SpringApplicationRunListener

image-20211120095009103

小结:

1
2
3
4
5
6
7
8
CommandLineRunner :在容器准备好了之后可以回调   @componet修饰即可
ApplicationRunner :在容器准备好了之后可以回调 @componet修饰即可

ApplicationContextInitializer :
在spring 在刷新之前 调用该接口的方法 用于:做些初始化工作 通常用于web环境,用于激化配置,web上下问的属性注册。
注意:需要配置META/spring.factories 配置之后才能加载调用
SpringApplicationRunListener :也需要配置META/spring.factories 配置之后才能加载调用
他是SpringApplication run方法的监听器,当我们使用SpringApplication调用Run方法的时候触发该监听器回调方法。注意:他需要有一个公共的构造函数,并且每一次RUN的时候都需要重新创建实例

08-SpringBoot流程分析

点击查看高清大图:SpringBoot启动流程.png

image-20230922151218736

初始化

DEBUG调试源码

image-20211120101826971

run

SpringApplication创建后执行run方法,启动应用

image-20211120104655544

09-SpringBoot监控

actuator基本使用

时常我们在使用的项目的时候,想知道相关项目的一些参数和调用状态,而SpringBoot自带监控功能Actuator,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、配置属性、日志信息等。

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

image-20230922151205607

访问http://localhost:808/actuator

image-20211120112336320

actuator开启所有endpoint

1
2
3
4
5
# 开启健康检查的完整信息
management.endpoint.health.show-details=always

# 将所有的监控endpoint暴露出来
management.endpoints.web.exposure.include=*
路径 描述
/beans 描述应用程序上下文里全部的Bean,以及它们的关系
/env 获取全部环境属性
/env/{name} 根据名称获取特定的环境属性值
/health 报告应用程序的健康指标,这些值由HealthIndicator的实现类提供
/info 获取应用程序的定制信息,这些信息由info打头的属性提供
/mappings 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系
/metrics 报告各种应用程序度量信息,比如内存用量和HTTP请求计数
/metrics/{name} 报告指定名称的应用程序度量值
/trace 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等)

springboot admin图形化界面使用

自带的actuator,都是文字,看的很乱,使用起来比较费劲,没有数据直观感受。我们可以通过插件来展示。

  • Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。
  • Spring Boot Admin 有两个角色,客户端(Client)和服务端(Server)。
  • 应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册
  • Spring Boot Admin Server 的UI界面将Spring Boot Admin Client的Actuator Endpoint上的一些监控信

spring boot admin的架构角色

  • admin server 用于收集统计所有相关client的注册过来的信息进行汇总展示
  • admin client 每一个springboot工程都是一个client 相关的功能展示需要汇总到注册汇总到server

image-20230922151144582

搭建工程:

  • admin-server:
    ① 创建 admin-server 模块
    ② 导入依赖坐标 admin-starter-server
1
2
3
4
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

​ ③ 在引导类上启用监控功能@EnableAdminServer

  • admin-client:
    ① 创建 admin-client 模块
    ② 导入依赖坐标 admin-starter-client
1
2
3
4
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>

​ ③ 配置相关信息:server地址等

1
2
3
4
5
6
7
8
9
10
11
# 指定admin.server地址
spring.boot.admin.client.url=http://localhost:9000

# 启用健康检查 默认就是true
management.endpoint.health.enabled=true

# 开启健康检查的完整信息
management.endpoint.health.show-details=always

# 将所有的监控endpoint暴露出来
management.endpoints.web.exposure.include=*

​ ④ 启动server和client服务,访问server

直接访问服务端http://localhost:9000即可看到客户端client的应用情况

image-20211120114143013

image-20211120123732946

idea启动控制台也可以查看

image-20211120123711723

10-SpringBoot部署

jar包部署(官方推荐)

直接打包,默认就是jar包

image-20211120124202879

打包后可以直接运行:java -jar springboot-admin-client-0.0.1-SNAPSHOT.jar (实际项目中结合nohup以及一堆更复杂的命令)

war包部署

image-20211120124608196

1
2
3
注意:
1.pom可以设置filename指定打包的名字
2.项目内的server.port配置就不再生效了,要改端口需要改Tomcat端口

11-SpringBoot日志

日志的故事

1
2
3
4
5
6
7
8
小张:开发一个大型系统;
1、没有框架时:System.out.println("");将关键数据打印在控制台;去掉?写在一个文件?
2、框架来记录系统的一些运行时信息;日志框架:zhanglogging.jar;
3、更优:高大上的几个功能?异步模式?自动归档?xxxx?zhanglogging-good.jar?
4、又有问题:将以前框架卸下来?换上新的框架,重新修改之前相关的API;zhanglogging-prefect.jar;
5、联想:JDBC---数据库驱动;
写了一个统一的接口层:日志门面(日志的一个抽象层):logging-abstract.jar;
给项目中导入具体的日志实现就行了;我们之前的日志框架都是实现的抽象层;

日志框架


1
2
市场上存在非常多的日志框架。
JUL(java.util.logging),JCL(ApacheCommons Logging),Log4j,Log4j2,Logback,SLF4j,jboss-logging等。

image-20230922150907763

image-20230922150912818

image-20230922150918936

1
2
3
4
5
左边选一个门面(抽象层)、右边来选一个实现;
    日志门面: SLF4J
    日志实现:Logback
Spring Boot,底层是Spring框架在框架内容部默认使用JCL.
spring-boot-starter-logging采用了slf4j+logback的形式,Spring Boot也能自动适配(jul,log4j2,logback)并简化配置

SLF4J使用


  • 1、如何在系统中使用SLF4j: 见官网:

 

1
2
以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;
给系统里面导入slf4j的jar和 logback的实现jar

官网示例

1
2
3
4
5
6
7
8
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger(HelloWorld.class);
        logger.info("Hello World");
    }
}

image-20230922150924871

每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件;

  • 2、遗留问题
1
2
加入我的a系统用(slf4j+logback),而依赖的用的日志框架不同: Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx统一日志记录
则如何做到: 即使是别的框架和我一起统一使用slf4j进行输出?

image-20230922150929834

如何让系统中所有的日志都统一到slf4j

  1. 将系统中其他日志框架先排除出去;
  2. 用中间包来替换原有的日志框架;
  3. 我们导入slf4j其他的实现

Spring Boot日志关系


1
2
3
4
5
6
7
8
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring‐boot‐starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring‐boot‐starter‐logging</artifactId>
</dependency>

image-20230922150935743

总结

1)、SpringBoot底层也是使用slf4j+logback的方式进行日志记录

2)、SpringBoot也把其他的日志都替换成了slf4j;

3)、中间替换包?

1
2
3
4
@SuppressWarnings("rawtypes")
public abstract class LogFactory {
    static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J=http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j";
    static LogFactory logFactory = new SLF4JLogFactory();

image-20230922150940073

4)、如果我们要引入其他框架?一定要把这个框架的默认日志依赖移除掉?

Spring框架用的是commons-logging;

1
2
3
4
5
6
7
8
9
10
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring‐core</artifactId>
    <exclusions>
        <exclusion>
            <groupId>commons‐logging</groupId>
            <artifactId>commons‐logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可;

日志使用


SpringBoot默认帮我们配置好了日志;

image-20230922150944933

  • 1、全局常规设置(格式、路径、级别)
1
2
3
4
5
6
7
8
9
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%‐5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
‐‐>
%d{yyyy‐MM‐dd HH:mm:ss.SSS} [%thread] %‐5level %logger{50} ‐ %msg%n

SpringBoot修改日志的默认配置

image-20230922150950360

1
2
3
4
5
6
7
8
9
10
11
logging.level.com.atguigu=trace
#logging.path=
# 不指定路径在当前项目下生成springboot.log日志
# 可以指定完整的路径;
#logging.file=G:/springboot.log
# 在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用 spring.log 作为默认文件
logging.path=/spring/log
# 在控制台输出的日志的格式
logging.pattern.console=%d{yyyy‐MM‐dd} [%thread] %‐5level %logger{50} ‐ %msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy‐MM‐dd} === [%thread] === %‐5level === %logger{50} ==== %msg%n

image-20230922150954722

image-20230922150959377

  • 2、指定配置

给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不使用他默认配置的了

image-20230922151006275

logback.xml:直接就被日志框架识别了.

logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profifile功能

image-20230922151011098

image-20220717151811362

切换日志框架


可以按照slf4j的日志适配图,进行相关的切换;

slf4j+log4j的方式

image-20220717151729344

实际上这种切换没有意义,因为lockback比log4j好,这里只是做测试用。

二选一

image-20220717151743516

切换为log4j2

image-20220717151652369

logback.xml

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?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="atguigu-springboot"></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.SSS} [%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.SSS} [ %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="com.atguigu" 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>

log4j.properties

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
### set log levels ###
log4j.rootLogger = debug , stdout , D , E


### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n


#### 输出到日志文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/log.log
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = DEBUG ## 输出DEBUG级别以上的日志
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#
#### 保存异常信息到单独文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/error.log ## 异常日志文件名
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = ERROR ## 只输出ERROR级别以上的日志!!!
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

12-SpringBoot接口文档

Swagger3 注解使用(Open API 3)

背景

Swagger2(基于openApi3)已经在17年停止维护了,取而代之的是 sagger3(基于openApi3),而国内几乎没有 sagger3使用的文档,百度搜出来的都是swagger2的使用,这篇文章将介绍如何在 java 中使用 openApi3(swagger3)

Open API

OpenApi是业界真正的 api 文档标准,其是由 Swagger 来维护的,并被linux列为api标准,从而成为行业标准。

swagger

swagger 是一个 api 文档维护组织,后来成为了 Open API 标准的主要定义者,现在最新的版本为17年发布的Swagger3(Open Api3)。
国内绝大部分人还在用过时的swagger2(17年停止维护并更名为swagger3)。
swagger2的包名为 io.swagger,而swagger3的包名为 io.swagger.core.v3。

SpringFox

SpringFox是 spring 社区维护的一个项目(非官方),帮助使用者将 swagger2 集成到 Spring 中。
常常用于 Spring 中帮助开发者生成文档,并可以轻松的在spring boot中使用。
截至2020年4月,都未支持 OpenAPI3 标准。

SpringDoc

SpringDoc也是 spring 社区维护的一个项目(非官方),帮助使用者将 swagger3 集成到 Spring 中。也是用来在 Spring 中帮助开发者生成文档,并可以轻松的在spring boot中使用。

该组织下的项目支持swagger页面Oauth2登录(Open API3的内容),相较 SpringFox来说,它的支撑时间更长,无疑是更好的选择。但由于国内发展较慢,在国内不容易看到太多有用的文档,不过可以访问它的官网。它的使用了swagger3(OpenAPI3),但 swagger3 并未对 swagger2 的注解做兼容,不易迁移,也因此,名气并不如 spring fox。

从SpringFox->SpringDoc

依赖替换
1
2
3
4
5
<dependency> 
    <groupId>org.springdoc</groupId> 
    <artifactId>springdoc-openapi-ui</artifactId> 
    <version>1.3.1</version> 
</dependency>
注解替换( io.swagger.v3.oas.annotations.下的注解)
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
@ApiParam -> @Parameter 
@ApiOperation -> @Operation 
@Api -> @Tag 
@ApiImplicitParams -> @Parameters 
@ApiImplicitParam -> @Parameter 
@ApiIgnore -> @Parameter(hidden = true) or @Operation(hidden = true) or @Hidden 
@ApiModel -> @Schema 
@ApiModelProperty -> @Schema
=======================================================================
@Api:用在请求的类上,表示对类的说明
    tags="说明该类的作用,可以在UI界面上看到的注解"
    value="该参数没什么意义,在UI界面上也看到,所以不需要配置"

@ApiOperation:用在请求的方法上,说明方法的用途、作用
    value="说明方法的用途、作用"
    notes="方法的备注说明"

@ApiImplicitParams:用在请求的方法上,表示一组参数说明
    @ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
        name:参数名
        value:参数的汉字说明、解释
        required:参数是否必须传
        paramType:参数放在哪个地方
            · header --> 请求参数的获取:@RequestHeader
            · query --> 请求参数的获取:@RequestParam
            · path(用于restful接口)--> 请求参数的获取:@PathVariable
            · div(不常用)
            · form(不常用)    
        dataType:参数类型,默认String,其它值dataType="Integer"       
        defaultValue:参数的默认值

@ApiResponses:用在请求的方法上,表示一组响应
    @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
        code:数字,例如400
        message:信息,例如"请求参数没填好"
        response:抛出异常的类

@ApiModel:用于响应类上,表示一个返回响应数据的信息
            (这种一般用在post创建的时候,使用@RequestBody这样的场景,
            请求参数无法使用@ApiImplicitParam注解进行描述的时候)
    @ApiModelProperty:用在属性上,描述响应类的属性
修改API分组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Bean
public Docket publicApi() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.nbchen.blog.web.public"))
            .paths(PathSelectors.regex("/public.*"))
            .build()
            .groupName("springshop-public")
            .apiInfo(apiInfo());
}

@Bean
public Docket adminApi() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.nbchen.blog.web.admin"))
            .paths(PathSelectors.regex("/admin.*"))
            .build()
            .groupName("blog-admin")
            .apiInfo(apiInfo());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Bean
public GroupedOpenApi publicApi() {
    return GroupedOpenApi.builder()
            .setGroup("blog-public")
            .pathsToMatch("/public/**")
            .build();
}

@Bean
public GroupedOpenApi adminApi() {
    return GroupedOpenApi.builder()
            .setGroup("blog-admin")
            .pathsToMatch("/admin/**")
            .build();
}
  • 如果之前只有一个 Docket,则把他删了,用配置文件替代它
1
2
springdoc.packagesToScan=package1, package2 
springdoc.pathsToMatch=/v1, /api/balance/**

swagger ui在代理的后面,如 nginx

附录

Swagger3.0.0 +springfox3.0.0

依赖
1
2
3
4
5
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>${swagger.version}</version>
</dependency>
主启动类增加注解
  • 使用例子
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
@Api(tags="用户管理")
@RestController
public class UserController {
    @ApiOperation("创建用户")
    @PostMapping("/users")
    public User create(@RequestBody @Valid User user) {
        return user;
    }
    @ApiOperation("用户详情")
    @GetMapping("/users/{id}")
    public User findById(@PathVariable Long id) {
        return new User("bbb", 21, "上海", "aaa@bbb.com");
    }
    @ApiOperation("用户列表")
    @GetMapping("/users")
    public List<User> list(@ApiParam("查看第几页") @RequestParam int pageIndex,
                           @ApiParam("每页多少条") @RequestParam int pageSize) {
        List<User> result = new ArrayList<>();
        result.add(new User("aaa", 50, "北京", "aaa@ccc.com"));
        result.add(new User("bbb", 21, "广州", "aaa@ddd.com"));
        return result;
    }
    @ApiIgnore
    @DeleteMapping("/users/{id}")
    public String deleteById(@PathVariable Long id) {
        return "delete user : " + id;
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("用户基本信息")
public class User {
    @ApiModelProperty("姓名")
    @Size(max = 20)
    private String name;
    @ApiModelProperty("年龄")
    @Max(150)
    @Min(1)
    private Integer age;
    @NotNull
    private String address;
    @Pattern(regexp = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$")
    private String email;
}
访问 http://localhost:8080/swagger-ui/index.html

访问swagger-ui.html页面404

springboot2.3.2 整合 swagger2.9.2访问swagger-ui.html页面404

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
package com.shengxi.wangyang.common.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 重新指定静态资源
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/");
        // 解决 SWAGGER2 404报错
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    /**
     * 配置servlet处理
     */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

SpringBoot源码

1 SpringBoot源码环境构建

1.1 下载源码

https://github.com/spring-projects/spring-boot/releases
下载对应版本的源码(课程中采用spring-boot-2.2.9.RELEASE)

image-20220310195702813

1.2 环境准备

1、JDK1.8+
2、Maven3.5+

image-20220310195714408

1.2 编译源码

进⼊spring-boot源码根⽬录
执⾏mvn命令: mvn clean install -DskipTests -Pfast // 跳过测试⽤例,会下载⼤量 jar 包(时间会长一些)

image-20220310195728145

1.3 导入IDEA

将编译后的项目导入IDEA中

image-20220310195755741

打开pom.xml关闭maven代码检查

1
2
3
4
5
<properties>
<revision>2.2.9.RELEASE</revision>
<main.basedir>${basedir}</main.basedir>
<disable.checks>true</disable.checks><!--设置为true即为关闭代码检查-->
</properties>

1.4 新建一个module

image-20220310195850924

1.5 新建一个Controller

1
2
3
4
5
6
7
8
@RestController
public class TestController {
@RequestMapping("/test")
public String test(){
System.out.println("源码环境搭建完成");
return "源码环境搭建完成";
}
}

启动测试

image-20220310195927659

2 源码剖析-依赖管理

问题:(1)为什么导入dependency时不需要指定版本?

在Spring Boot入门程序中,项目pom.xml文件有两个核心依赖,分别是spring-boot-starter-parent和spring-boot-starter-web,关于这两个依赖的相关介绍具体如下

spring-boot-starter-parent

在chapter01项目中的pom.xml文件中找到spring-boot-starter-parent依赖,示例代码如下:

1
2
3
4
5
6
7
<!-- Spring Boot父项目依赖管理 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

​ 上述代码中,将spring-boot-starter-parent依赖作为Spring Boot项目的统一父项目依赖管理,并将项目版本号统一为2.2.9.RELEASE,该版本号根据实际开发需求是可以修改的

​ 使用“Ctrl+鼠标左键”进入并查看spring-boot-starter-parent底层源文件,先看spring-bootstarter-parent做了哪些事首先看 spring-boot-starter-parent 的 properties 节点

1
2
3
4
5
6
7
8
9
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
<java.version>1.8</java.version>
<resource.delimiter>@</resource.delimiter> <!-- delimiter that doesn't clash with Spring ${} placeholders -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF- 8</project.reporting.outputEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>

在这里 spring-boot-starter-parent 定义了:

  1. 工程的Java版本为 1.8 。
  2. 工程代码的编译源文件编码格式为 UTF-8
  3. 工程编译后的文件编码格式为 UTF-8
  4. Maven打包编译的版本

再来看 spring-boot-starter-parent 的「build」节点
接下来看POM的 build 节点,分别定义了 resources 资源和 pluginManagement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<resources>
<resource>
<filtering>true</filtering>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>**/application*.yml</include>
<include>**/application*.yaml</include>
<include>**/application*.properties</include>
</includes>
</resource>
<resource>
<directory>${basedir}/src/main/resources</directory>
<excludes>
<exclude>**/application*.yml</exclude>
<exclude>**/application*.yaml</exclude>
<exclude>**/application*.properties</exclude>
</excludes>
</resource>
</resources>

我们详细看一下 resources 节点,里面定义了资源过滤,针对 application 的 yml 、 properties 格式进行了过滤,可以支持支持不同环境的配置,比如 application-dev.yml 、 application-test.yml 、 application-dev.properties 、 application-dev.properties 等等。

pluginManagement 则是引入了相应的插件和对应的版本依赖

最后来看spring-boot-starter-parent的父依赖 spring-boot-dependencies,spring-boot-dependencies的properties节点

我们看定义POM,这个才是SpringBoot项目的真正管理依赖的项目,里面定义了SpringBoot相关的版本

未完待续…