# springboot3
**Repository Path**: losermly/springboot3
## Basic Information
- **Project Name**: springboot3
- **Description**: 学习springboot3
- **Primary Language**: Java
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-08-15
- **Last Updated**: 2025-08-20
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# springboot3
#### 介绍
学习springboot3
springboot倡导约定大于配置,简化了配置
#### 软件架构
1. springBoot 3.3.5
2. jdk21
## springBoot的jar和普通jar的区别
springBoot的jar是可执行jar,普通jar是普通jar,运行时需要指定启动类
### springBoot的jar目录结构
|- springboot.jar:
|- BOOT-INF
|- META-INF
|- org
|- x.jar
|- META-INF
|- org
1. BOOT-INF:自己编写的应用程序一级应用程序需要的依赖
2. META-INF
3. org:当执行java -jar时,在该目录下找到程序入口并执行(main(String[] args))
## springBoot外部化配置
配置存储在代码之外
### 外部化配置的优先级
按照优先级进行读取,如果一下路径的都有配置文件,那么程序会读取优先级最高的配置文件中的文件属性进行使用
后续的相同属性就不会使用
优先找properties文件,没有的话再找yaml文件
1. file: ./config/
2. file: ./
3. classpath: /config/
4. classpath: /
5. 通过运行参数查找:java -jar xxx.jar --spring.config.location=file://D:/path/to/my.properties
#### application.properties
这个配置可以不写,不写的话springboot就引用自动配置的默认配置
#### application.yaml
## springBoot自动配置
### 自动配置概述
#### spring boot的两大核心
springBoot框架的两大核心特性可以概括为“启动器”(starter),“自动配置”(auto-configuration)
1. 启动器(Starter):
springBoot提供了一系列的Stater POMs,他们是一组预定义的依赖关系;
当在项目中引入一个start pom时,他会自动包含所有必要的spring组件以及合理的默认设置。开发者不
需要手动管理复杂的依赖关系,也不需要担心版本冲突的问题,减少了配置上的出错可能
2. 自动配置(Auto-Configuration):
当添加了特定的starter pom后,springBoot会根据类路
径上存在的jar包来自动配置bean(比如:springBoot发现类路径上存在mybatis相关的类,例如
SqlSessionFactory.class,那么SpringBoot将自动配置mybatis相关的所有bean)。
如果开发者没有显示的提供任何域特定功能相关的配置,springboot将使用其默认配置来自动设置这些功
能。当然,如果需要的话,用户也可以覆盖这些默认设置。
这两个特性结合在一起,使得使用spring boot开发应用程序变得更加简单快速,减少了大量的样板代码
和重复配置的工作。让程序员专注业务逻辑的开发,在环境方面耗费最少的时间。
#### 自动配置带来的便捷(示例)
1. spring继承Mybatis框架
applicationContext.xml配置
~~~xml
~~~
2. springBoot配置Mybatis框架
引用starter
application.yaml
~~~yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowLoadLocalInfile=true&allow
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
~~~
#### 默认配置
springboot为了实现功能提供了许多的默认配置
例如:tomcat的默认端口是8080,可以通过**application.yaml**文件进行覆盖修改
~~~yaml
server:
# tomcat端口
port: 8888
# thymeleaf配置
thymeleaf:
prefix: classpath:/templates/
suffix: .html
~~~
这些配置都会通过**@ConfigurationProperties(prefix = "")**注解绑定到对应的bean的属性上,这个bean一般称为属性类。例如:
**ServerProperties**: 服务器属性类,专门负责配置服务器相关配置
~~~java
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
}
~~~
#### springboot框架提供的条件注解
springboot依靠框架中的条件注解来实现按需加载,满足条件时才会实例化一个bean
条件注解以 **@ConditionalOnxxxxxx**开头
+ @ConditionalOnClass:当指定的类位于类路径上时,才会实例化一个bean
+ @ConditionalOnMissingClass:当指定类不存在时,才创建Bean
+ @ConditionalOnBean:当容器中有指定的Bean时,才会实例化一个Bean
+ @ConditionalOnMissingBean:当容器中没有指定的Bean时,才会实例化一个Bean
+ @ConditionalOnProperty:当配置文件中存在指定属性时,才会实例化一个Bean
+ @ConditionalOnResource:当自定资源存在时,才会创建Bean
+ @ConditionalOnWebApplication:当是web应用时,才会创建Bean
+ @ConditionalOnNotWebApplication:当不是web应用时,才会创建Bean
### 自动配置的实现原理
#### 程序没有开始执行前导入的依赖
1. 导入 **spring-boot-starter-web**
2. 关联导入 **spring-boot-starter**、**spring-boot-starter-json**、**spring-boot-starter-tomcat**、**spring-web**、**spring-webmvc**
1. **spring-boot-starter**: 是springboot的核心启动器,任何启动器导入时都会关联导入springboot核心启动器
3. 核心启动器导入后,关联了一个jar包: **spring-boot-autoconfigure**
1. 这个jar中存放的是springboot框架官方支持的自动配置类
#### 从main方法开始执行之后发生的流程
1. 在main方法执行时,主入口类上使用 **@SpringBootApplication**进行了标注
2. **@SpringBootApplication**注解是复合注解,它被一下3个注解标注
1. **@SpringBootConfiguration**: 它被 **@Configuration**标注,表示主入口类就是一个配置类,这时开始加载该配置
2. **@ComponentScan**:默认扫描的是主入口所在包及其子包下的文件。此时 **spring-boot-autoconfigure**包是扫描不到的,其中的 **XxxAutoConfiguration**自动配置类是无法加载的
3. **@EnableAutoConfiguration**:启动自动配置,自动配置类的加载和生效全靠这个注解
3. **@EnableAutoConfiguration**:被 **@Import(AutoConfigurationImportSelector.class)** 标注
1. **@Import(AutoConfigurationImportSelector.class): 将 **AutoConfigurationImportSelector** 作为一个Bean加载到IoC容器中
2. 这个Bean负责手机和选择所有符合条件的自动配置类
4. 添加断点,跟踪 **AutoConfigurationImportSelector** 源码:负责自动配置类加载,并选择那些生效
~~~java
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
RourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取自动配置类的类名
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 移除重复的
configurations = removeDuplicates(configurations);
// 获取排除的
Set exclusions = getExclusions(annotationMetadata, attributes);
// 检查排除的,确保排除的类不会被加载
checkExcludedClasses(configurations, exclusions);
// 移除需要排除的自动配置类
configurations.removeAll(exclusions);
// 进行筛选,选择只需要的自动配置类。在这里会读取@ConditionalOnxxx的注解进行处理
configurations = getConfigurationClassFilter().filter(configurations);
//
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
/**
* 获取自动配置类的全类名
*/
protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
.getCandidates();
Assert.notEmpty(configurations,
"No auto configuration classes found in "
+ "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
}
public final class ImportCandidates implements Iterable {
/**
* 获取自动配置类信息的地址
*/
private static final String LOCATION = "META-INF/spring/%s.imports";
public static ImportCandidates load(Class> annotation, ClassLoader classLoader) {
Assert.notNull(annotation, "'annotation' must not be null");
ClassLoader classLoaderToUse = decideClassloader(classLoader);
// 拼接全类名
// location = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"
String location = String.format(LOCATION, annotation.getName());
Enumeration urls = findUrlsInClasspath(classLoaderToUse, location);
List importCandidates = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
importCandidates.addAll(readCandidateConfigurations(url));
}
return new ImportCandidates(importCandidates);
}
}
~~~
#### 自动配置类处理的事情
**自动配置类导入了一堆相关的组件(一个组件一个功能),而每个组件获取配置时都是从属性类中获取,而属性类恰好又和配置文件绑定。**
以 **SslAutoConfiguration** 自动配置类为例
~~~java
/**
* 通过AutoConfigurationImportSelector#getAutoConfigurationEntry()获取到对应的需要创建的XxxAutoConfiguration的类
*/
// 标记为自动配置类,让springBoot进行处理。
// 在AutoConfigurationImportSelector#getAutoConfigurationEntry()方法中指定了使用AutoConfiguration注解,去加载这个注解标注的类
@AutoConfiguration
// springboot中约定的配置,使用这个注解的话表示启用ConfigurationProperties注解
// ConfigurationProperties注解表示读取配置文件中的信息放入类中,并且交给IoC容器进行管理
// 读取配置文件给SslProperties类进行初始化
@EnableConfigurationProperties(SslProperties.class)
public class SslAutoConfiguration {
// 自动的配置,创建相关联的bean和属性
}
// 导入EnableConfigurationPropertiesRegistrar类作为bean加入IoC容器中,并作用于当前类
// 和@Bean作用类似
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
}
~~~
#### 自动配置的原理总结
1. 运行环境准备阶段
2. 引入web启动器
3. 传递引用了自动配置的jar包
4. 自动配置的jar包中有152个自动配置类,运行环境准备完毕
2. 运行阶段
3. @EnableAutoConfiguration启用自动配置,将152个自动配置类全部加载到IoC容器中,然后根据开发场景筛选出必须的自动配置类
4. 自动配置类加载了一堆组件
5. 每个组件需要的数据来自属性类
6. 属性类又和配置文件(application.yaml)绑定在一起
3. 因此,最终结果就是:导入启动器,配置配置文件,启动项目,就可以使用对应功能进行开发了
### 自动配置的加载WebMvc中的AutoConfigurationImportSelector方法的实现流程
执行的方法顺序
~~~text
SpringApplication:
ConfigurableApplicationContext run(Class> primarySource, String... args);
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args);
public ConfigurableApplicationContext run(String... args);
private void refreshContext(ConfigurableApplicationContext context);
protected void refresh(ConfigurableApplicationContext applicationContext);
AbstractApplicationContext:
public void refresh();
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory);
PostProcessorRegistrationDelegate:
public static void invokeBeanFactoryPostProcessors;
private static void invokeBeanDefinitionRegistryPostProcessors(Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup);
ConfigurationClassPostProcessor:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry);
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry)
ConfigurationClassParser:
public void parse(Set configCandidates);
~~~
1. 程序从主入口进入,执行SpringApplication.run()方法
~~~java
@SpringBootApplication
public class SSMApplication {
public static void main(String[] args) {
SpringApplication.run(SSMApplication.class, args);
}
}
public class SpringApplication {
public static ConfigurableApplicationContext run(Class> primarySource, String... args) {
return run(new Class>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
public ConfigurableApplicationContext run(String... args) {
// 创建容器
context = createApplicationContext();
// 刷新springIoC信息,spring容器已经在上面初始化完成,并放入了spring需要的核心bean工厂类信息
refreshContext(context);
}
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
refresh(context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
// 调用容器的刷新方法
applicationContext.refresh();
}
}
~~~
2. 刷新容器,将需要的bean注册到容器中
~~~java
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
public final void refresh() throws BeansException, IllegalStateException {
// 调用父类GenericWebApplicationContext的方法
super.refresh();
}
}
public class GenericWebApplicationContext extends GenericApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {}
// 设置beanFactory属性
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {}
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
// 执行bean工厂的后置处理
invokeBeanFactoryPostProcessors(beanFactory);
}
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}
}
final class PostProcessorRegistrationDelegate{
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {
// 注册bean
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
}
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
}
// bean注册接口
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
// 类配置的处理
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor{
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// 解析@Configuration注解标注的类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
do {
// candidates中存储的是springBoot的启动类,是通过SpringApplication.run(Class> clazz, args)中传入的
// 启动类使用@SpringBootApplication进行标注的,而SpringBootApplication中又使用了@SpringBootConfiguration
// @SpringBootConfiguration又使用了@Configuration进行标注
parser.parse(candidates);
}
while (!candidates.isEmpty());
}
}
~~~
3. 解析@Configuration注解。
1. 先获取@Import注解标注指定的ImportSelector类:
```java
protected final void parse(AnnotationMetadata metadata, String beanName);
protected void processConfigurationClass(ConfigurationClass configClass, Predicate filter);
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate filter);
private Set getImports(SourceClass sourceClass);
private void collectImports(SourceClass sourceClass, Set imports, Set visited);
```
2. 调用ImportSelector实现类的getImports方法获取指定要加载的类进行初始化
```java
public void parse(Set configCandidates);
DeferredImportSelectorHandler{
public void process();
public void processGroupImports();
}
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection importCandidates, Predicate exclusionFilter, boolean checkForCircularImports);
```
~~~java
class ConfigurationClassParser {
// 解析@Configuration标签
public void parse(Set configCandidates) {
// 处理ImportSelector的实现类,其中就包含了核心类AutoConfigurationImportSelector类
this.deferredImportSelectorHandler.process();
}
// 通过原注解解析获取配置类
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate filter) throws IOException {
// 通过注解获取对应的类
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate filter)
throws IOException {
// 处理@Import注解
// 启动类使用@SpringBootApplication进行标注的,而SpringBootApplication中又使用了@EnableAutoConfiguration注解进行标注
// @EnableAutoConfiguration中使用了@Import(AutoConfigurationImportSelector.class)和@AutoConfigurationPackage
// @AutoConfigurationPackage中使用了@Import(AutoConfigurationPackages.Registrar.class)
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
}
// 返回所有带有Import注解指定需要导入的类,也就是:AutoConfigurationImportSelector和AutoConfigurationPackages.Registrar
private Set getImports(SourceClass sourceClass) throws IOException {
Set imports = new LinkedHashSet<>();
Set visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set imports, Set visited) throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
// 判断注解是否为Import
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
// 类包含有Import注解,将其放入集合中进行返回
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection importCandidates, Predicate exclusionFilter,
boolean checkForCircularImports) {
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
Class> candidateClass = candidate.loadClass();
// 实例化bean
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
}
}
} catch (BeanDefinitionStoreException ex) { throw ex; }
}
private class DeferredImportSelectorHandler {
// 存入importSelector的映射关系
private final Map