springboot 2 编程笔记 一
SpringBoot2核心技术与响应式编程 (一)
接上文 《springboot 2 编程笔记》
上篇文章我们详细解释了微服务核心概念
该篇主要讲解springboot的自动配置原理和实践
👉为了更好地理解SpringBoot内容,建议先掌握Maven工程相关知识。有了Maven基础后,学习SpringBoot会事半功倍。
基础入门
spring 能做什么?
- 微服务
- 响应式编程
- 分布式云开发
- web开发
- 无服务开发
- 事件驱动
- 批处理业务
SpringBoot优点
- 创建独立Spring应用
- 内嵌web服务器
- 自动starter依赖,简化构建配置
- 自动配置Spring以及第三方功能
- 提供生产级别的监控、健康检查及外部化配置
- 无代码生成、无需编写XML
创建SpringBoot2项目
我的idea2023默认只能创建springboot3工程,想创建springboot2项目需要些方法,在这里记录一下
点击Spring Initializr后替换为下面的服务器URL即可。
https://start.aliyun.com

创建web项目后,启动项目查看能否在浏览器上访问
如果我们想修改port端口号,可以将端口名添加到我们创建的 配置文件application.properties中,在pom文件中添加SpringBoot打包插件(可以把项目打成jar包,直接在目标服务器执行即可)
<!--SpringBoot打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>


修改后重启项目,在浏览器上查看是否生效
依赖管理机制
在软件开发过程中,模块化开发是常见做法。当不同开发者负责不同模块时,可能出现依赖版本不一致的问题。例如:
- DAO模块开发者使用spring-context 5.1.9版本
- Service模块开发者使用spring-context 5.2.0版本
当Service模块需要引用DAO模块,且这两个模块部署在同一个微服务中时,不同版本的spring-context jar包就会产生冲突。为了解决这个问题,我们需要通过依赖管理机制来统一各模块使用的依赖版本。
Maven项目通常采用聚合模块作为父项目,在父模块中统一管理spring-context等依赖的版本号,而dao和service模块则作为子模块继承父模块的配置。
springboot在做依赖管理时也是如此
我们在项目的pom文件中加上父引用
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
其中的引用关系是:项目的父引用是spring-boot-parent,spring-boot-parent 的父项目是 spring-boot-dependencies ,spring-boot-dependencies 里面定义了开发过程中能遇到的所有的jar包的版本号
引用其他版本的依赖
如果你不认可SpringBoot版本仲裁中定义的MySQL,想使用MySQL的其他版本,你可以在项目的pom文件中添加配置,这样就可以覆盖spring-boot-dependencies 里面定义的MySQL版本号。
添加配置如下:
场景启动器
Spring Boot 启动器的命名格式为:spring-boot-starter-*.jar
只需引入特定场景的 starter,相关依赖就会自动被引入。
例如:我们引入spring-boot-starter-security场景启动器,只需在<dependencies> </dependencies>标签内添加依赖即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.7.18</version>
</dependency>
所有场景启动器的最底层的依赖都会依赖
springboot自动配置的核心依赖java <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <scope>compile</scope> </dependency>
注意:
引入的非版本仲裁的依赖需要写版本号
自定义场景启动器
我们可以根据业务需要制作特定的场景启动器,但为了区分SpringBoot官方的场景启动器在命名格式上需要注意:
格式:XXX-spring-boot-starter.jar
自动配置特性
- 自动配置Tomcat
- 引入Tomcat依赖
- 配置Tomcat
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.6.13</version> </dependency>
- 自动配好SpringMVC
- 自动配好Web常见功能,如:字符编码问题
- SpringBoot帮我们配置好了所有web开发的常见场景
- 默认的包结构
- 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来。
- 无需以前的包扫描配置
- 想要改变扫描路径,
@SpringBootApplication(scanBasePackages=“com.study")
- 各种配置拥有默认值
- 默认配置最终都是映射到
MultipartProperties - 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象。
- 默认配置最终都是映射到
- 按需加载所存自动配置项
- 非常多的starter
- 引入了哪些场景这个场景的自动配置才会开启
底层注解
@Configuration
告诉 springboot 这是一个配置类 == 配置文件(在 Spring 容器中默认是单实例)
原理解析:
Spring 的很多注解都是“复合注解”。@Configuration 本身是由 @Component 注解“修饰”的。你可以查看 @Configuration 的源码,其定义开头一定是这样的:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 关键!这表明 @Configuration 本身也是一种特殊的 @Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
在上面代码中,因为 @Configuration 被 @Component 标注了,所以 @Configuration 注解的类也会被组件扫描(ComponentScan)发现,并注册为一个 Bean。它不仅仅是一个配置类,它本身也是 Spring 容器中的一个 Bean。
@AliasFor(annotation = Component.class)
-
@AliasFor 注解,字面意思就是“作为……的别名”。它用于在注解之间或同一注解内部声明属性的别名,从而实现元数据属性的覆盖或镜像。
在这段代码中:@AliasFor(annotation = Component.class) 非常明确地指出了:我(@Configuration 的 value 属性)是另一个注解(@Component)的 value 属性的别名。
因为 @Configuration 的 value 属性是 @Component 的 value 属性的别名,所以你可以像使用 @Component 一样,使用 @Configuration 的 value 属性来为这个配置类本身指定一个在 Spring 容器中的 Bean 名称。
@configuration(proxyBeanMethods = false/true)
这个配置决定了 Spring 是否对该 @Configuration 配置类进行 CGLIB 代理增强。
-
proxyBeanMethods = true(默认值):Spring 会创建这个配置类的代理子类。从而拦截 @Bean 方法调用,保证 @Bean 方法返回的也是单例 Bean。
原理图如下:
-
proxyBeanMethods = false:Spring 不会创建代理。配置类就是一个普通类。每次调用它的 @Bean 方法,都会真正执行一次方法体,可能会产生新的实例。
@Bean
配置类里面使用@Bean注解在方法上给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值就是组件在容器中的实例(默认是单实例)
// 该注解可以标注在方法上或其他注解上
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
@Documented
public @interface Bean {
// 与"name"属性互为别名。用于定义Bean的名称(ID)
@AliasFor("name")
String[] value() default {};
// 与"value"属性互为别名。用于定义Bean的名称(ID)
@AliasFor("value")
String[] name() default {};
// 【已过时】自动装配模式。现代Spring应用应使用@Autowired注解
@Deprecated
Autowire autowire() default Autowire.NO;
// 是否作为自动装配的候选者(默认true)。设为false时,此Bean不会被@Autowired自动注入
boolean autowireCandidate() default true;
// 指定Bean初始化后调用的方法名
String initMethod() default "";
// 指定Bean销毁前调用的方法名。默认值INFER_METHOD会自动检测close或shutdown方法
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
}
核心要点解析:
-
Bean命名:
value() 和 name() 功能完全相同,用于指定Bean在容器中的名称
示例:
@Bean(“myService”) 或 @Bean(name = “myService”) -
自动装配控制:
autowireCandidate 控制该Bean是否参与自动装配过程
设为 false 时,只能通过名称显式获取,不会被 @Autowired 自动注入 -
生命周期方法:
initMethod:Bean初始化后执行的方法
destroyMethod:Bean销毁前执行的方法
Spring会自动检测标准的销毁方法(如 close(), shutdown()) -
过时属性:
autowire 已不推荐使用,应使用更现代的 @Autowired 注解
@Import 导入组件
@Import 注解的主要目的是将多个配置类或组件类合并到一个主配置类中。它允许你将配置分散到多个类中,然后通过导入的方式将它们组合起来,实现模块化和解耦
为了更好地理解 @Import 的工作原理,我们可以看一下Spring容器启动时如何处理它:
用法三:
- 导入普通的配置类(
@Configuration类)
这是最直接的用法,用于将其他配置类引入到当前配置类中。
当 Spring 处理 MyConfig 时,会同时处理 DatabaseConfig 和 TransactionConfig,相当于把三个配置类的内容合并了。

- 导入组件类(普通的 @Component 类)
你可以直接导入任何被 @Component 标记的类(包括 @Service, @Repository, @Controller),Spring 会将其注册为 Bean。
注意:这种方式要求被导入的类必须有默认的无参构造函数
-
导入特殊的选择器(高级用法)
这是最强大的用法,允许你通过编程方式动态决定要导入哪些配置。- ImportSelector:根据条件动态选择要导入的配置类
- ImportBeanDefinitionRegistrar:直接编程式注册Bean定义
//创建一个环境配置类 public class EnvBasedImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 这里可以根据条件动态返回要导入的配置类 if ("prod".equals(System.getenv("APP_ENV"))) { return new String[] {DataSourceConfig.class.getName()}; } else { return new String[] {TransactionConfig.class.getName()}; } } }
@Conditional
@Conditional 是一个 Spring 注解,用于条件化地注册 Bean 或配置类。它的核心思想是:只有在满足特定条件时,才会将标注的组件(如 @Bean, @Component, @Configuration)注册到 Spring 容器中;如果不满足,则完全忽略它。
这为 Spring 应用带来了巨大的灵活性,允许你根据当前的环境、属性配置、类路径是否存在某个类、或其他任何自定义逻辑来决定是否启用某个配置。
原理解析:
@Conditional 的原理核心在于 Spring 容器在加载配置时的处理过程。
-
解析配置阶段:当 Spring 容器启动时,它会解析所有 @Configuration 类。
-
收集 Bean 定义:对于每个带有 @Bean 的方法,Spring 会先创建一个 BeanDefinition 对象(Bean 的定义信息),但此时并不会立即实例化 Bean。
-
条件评估(Condition Evaluation):
- 对于每个被 @Conditional(或其派生注解)标注的 BeanDefinition,Spring 会调用其对应的 Condition 实现类的 matches(…) 方法。
- ConditionContext 参数提供了丰富的上下文信息,用于辅助判断:
- BeanFactory:检查已存在的 Bean。
- Environment:访问配置属性。
- ResourceLoader:加载资源。
- ClassLoader:检查类路径。
- AnnotatedTypeMetadata 参数提供了正在被评估的注解的元数据,你可以从中获取注解的属性值。
-
注册或跳过:
- 如果 matches 方法返回 true,那么这个 BeanDefinition 会被正式注册到容器中,后续会被实例化。
- 如果返回 false,这个 BeanDefinition 会被直接丢弃,如同它从未存在过一样。
Spring Boot 预定义了大量基于 @Conditional 的“派生注解”,它们更语义化,使用起来也更方便。这些注解位于 org.springframework.boot.autoconfigure.condition 包下。
| 注解 | 目的 | 等效的 @Conditional 表达式 |
|---|---|---|
| @ConditionalOnBean | 当容器中存在指定 Bean 时 | @Conditional(OnBeanCondition.class) |
| @ConditionalOnMissingBean | 当容器中不存在指定 Bean 时 | @Conditional(OnMissingBeanCondition.class) |
| @ConditionalOnClass | 当类路径中存在指定类时 | @Conditional(OnClassCondition.class) |
| @ConditionalOnMissingClass | 当类路径中不存在指定类时 | @Conditional(OnMissingClassCondition.class) |
| @ConditionalOnProperty | 当指定的配置属性具有特定值时 | @Conditional(OnPropertyCondition.class) |
| @ConditionalOnResource | 当类路径中存在指定资源文件时 | @Conditional(OnResourceCondition.class) |
| @ConditionalOnWebApplication | 当应用是 Web 应用时 | @Conditional(OnWebApplicationCondition.class) |
| @ConditionalOnNotWebApplication | 当应用不是 Web 应用时 | @Conditional(OnNotWebApplicationCondition.class) |
| @ConditionalOnExpression | 当 SpEL 表达式为 true 时 | @Conditional(OnExpressionCondition.class) |
| @ConditionalOnJava | 当运行在指定的 Java 版本时 | @Conditional(OnJavaCondition.class) |
| @ConditionalOnJndi | 当指定的 JNDI 存在时 | @Conditional(OnJndiCondition.class) |
| @ConditionalOnCloudPlatform | 当指定的云平台被激活时 | @Conditional(OnCloudPlatformCondition.class) |
下面以@ConditionalOnProperty注解为例,演示其具体使用方法。
一、在配置文件中添加配置项
# 设置 true,MyFeatureService Bean 会被创建
myfeature.enabled=true
二、在配置类添加标签,在main方法添加判断Bean创建与否的代码
myConfig类中
/**
*只有当配置文件中 `myfeature.enabled` 属性的值为 true 时,才创建这个 Bean
*matchIfMissing = false 表示如果配置文件中根本没有这个属性,则视为不满足条件
*/
@Bean("serviceA")
@ConditionalOnProperty(name = "myfeature.enabled", havingValue = "true", matchIfMissing = false)
public ServiceA serviceA() {
return new ServiceA(); // 方法体:创建 ServiceA 实例
}
main方法:
boolean serviceA = configurableApplicationContext.containsBean("serviceA");
System.out.println("容器中 serviceA 组件:"+serviceA);
结果如下:
如果 myfeature.enabled=false 或者 不写则不会创建ServiceA组件

@lmportResource 原生配置文件引入
@ImportResource 注解主要用于将传统的 XML 格式的 Spring 配置文件导入到基于注解的 Spring 应用程序中,实现新旧配置方式的混合使用
这里就不再演示
🤔 总结
@ImportResource 注解是一个有用的“桥梁”,它帮助我们在Spring Boot的注解世界中继续使用传统的XML配置。但对于新项目,强烈建议拥抱Spring Boot推荐的基于Java的配置方式,这样代码更类型安全、更简洁、也更容易重构。对于遗留项目,可以将其作为迁移过程中的辅助工具,同时逐步将XML配置现代化。
@ConfigurationProperties 配置绑定
@ConfigurationProperties 是一个 Spring Boot 注解,它的主要作用是:将配置文件(如 application.properties 或 application.yml)中的属性值,批量绑定到一个 Java 对象的字段上。
注意:
@ConfigurationProperties注解后要添加(prefix = "myapp.project)",以明确指定要读取的配置项来源。@Component // 1. 将其声明为Spring容器管理的Bean @ConfigurationProperties(prefix = "myapp.project") // 2. 指定配置的统一前缀 public class ProjectProperties { private String name; private String version; private String description; private List<String> authors; private Database database; // 嵌套属性 // 必须提供Getter和Setter方法 public Database getDatabase() { return database; } public void setDatabase(Database database) { this.database = database; } // 4. 静态内部类用于嵌套属性 public static class Database { private String host; private Integer port; private Credentials credentials; // 再次嵌套 // Getter and Setter ... public String getHost() { return host; } public void setHost(String host) { this.host = host; } public Integer getPort() { return port; } public void setPort(Integer port) { this.port = port; } public Credentials getCredentials() { return credentials; } public void setCredentials(Credentials credentials) { this.credentials = credentials; } public static class Credentials { private String username; private String password; // Getter and Setter ... public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } } }在yml文件里添加配置:
# application.yml myapp: project: name: "My Awesome Project" version: "1.0.0" description: "This is a spring boot project" authors: - "Alice" - "Bob" database: host: "localhost" port: 3306 credentials: username: "admin" password: "secret"Bean会读取yml文件的配置项
简单来说,它让你能够:
- 集中管理配置:将所有相关的配置属性分组在一起。
- 类型安全:配置值会被自动转换为定义的 Java 类型(如 String, Integer, List, Map 等)。
- 避免样板代码:无需为每个配置属性使用 @Value(“${property}”) 注解。
一般有三种使用方法:
- 在类上使用
@ConfigurationProperties+@Component
这种方式最简单直接,将配置类同时声明为一个 Spring 管理的组件(Bean)。 - 在类上使用 @ConfigurationProperties + 在配置类中使用 @EnableConfigurationProperties
这种方式更显式,适合不希望用 @Component 扫描,或者需要第三方库的配置类。 - 在 @Bean 方法上使用 @ConfigurationProperties
这种方式适用于为你无法直接修改源码的类绑定配置。@Configuration public class MyConfig { @Bean // 创建一个Bean @ConfigurationProperties(prefix = "myapp.project") // 在此处指定前缀 public ProjectProperties projectProperties() { return new ProjectProperties(); // 返回一个空实例,属性由Spring注入 } }
SpringBoot自动配置原理
在主启动程序上我们点开@SpringBootApplication配置类,它是由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解组成
点击查看发现,该方法使用了@Configuration注解进行修饰,说明当前main方法属于一个配置类。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
观看学习视频时,有网友指出讲解内容存在偏差。我结合查阅的资料和个人理解作了修正说明。若仍有表述不够清晰之处,欢迎指正探讨。
自动配置的核心逻辑位于此处,深入查看可见它是一个复合注解,由@AutoConfigurationPackage和@Import两个注解组合而成。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage(意为自动配置包)
该注解内部通过@Import实现功能:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class}) //给容器中导入一个组件
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
点击Registrar查看代码:
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
这段代码的核心目的就是:获取 Spring Boot 主启动类所在的包路径,并将其注册到 Spring 容器中,作为自动配置和组件扫描的“基准包”。(找到你的主启动类(比如 MyApplication)所在的包,然后告诉 Spring:“以后自动扫描和配置,就从这里开始!”)
我们打断点并debugger启动程序发现里面上传了2个参数 AnnotationMetadata 和 BeanDefinitionRegistry
将这行核心代码拆解为几个步骤来理解:
-
new PackageImports(metadata):
- 这里的
metadata参数包含了 被@AutoConfigurationPackage注解标注的类 的元信息,在标准的 Spring Boot 应用中,这通常就是你的主启动类(标注了 @SpringBootApplication 的那个类(@SpringBootApplication包含@EnableAutoConfiguration
包含@AutoConfigurationPackage包含@Import({AutoConfigurationPackages.Registrar.class})))。 PackageImports是一个工具类,它的作用就是从AnnotationMetadata中解析出该类所在的包路径。例如,如果你的主启动类是com.study.springboot2_01_helloworld.SpringBoot2_01_HelloWorldApplication,那么解析出的包名就是com.study.springboot2_01_helloworld。
- 这里的
-
.getPackageNames():- 这个方法会返回一个包含包名的集合。虽然大多数情况下这里只包含一个包路径(主启动类所在的包),但设计上支持多个。
-
.toArray(new String[0]):- 这是一个常见的 Java 技巧,用于将集合转换为字符串数组。这里的 new String[0] 只是为了指定返回数组的类型。
-
AutoConfigurationPackages.register(registry, ...):- 这是最关键的一步。它负责将解析得到的包路径数组注册到 Spring 的
BeanDefinitionRegistry(也就是 Spring 容器)中。 - 注册时,Spring 会创建一个特殊的 Bean,其名称通常为
org.springframework.boot.autoconfigure.AutoConfigurationPackages,这个 Bean 就持有这些基准包路径。
- 这是最关键的一步。它负责将解析得到的包路径数组注册到 Spring 的
-
原理图如下:

🎯 主要作用与设计意图
这段代码和 @AutoConfigurationPackage 注解的设计,主要有两个核心作用:
为自动配置提供“基准包”:
Spring Boot 的自动配置机制需要知道从哪里开始扫描你的组件(如 @Component, @Service, @Repository 等)。通过将主启动类所在的包注册为“基准包”,Spring Boot 就能自动扫描该包及其所有子包下的组件,无需像传统 Spring 应用那样在 XML 或 Java 配置中手动指定 context:component-scan 或 @ComponentScan 的 basePackages。这极大地简化了配置,体现了 Spring Boot “约定优于配置” 的理念。
供其他自动配置类使用:
一些第三方库的 Spring Boot 自动配置类(例如 Spring Data JPA、MyBatis 等)需要知道这个“基准包”路径,以便进行它们自己的扫描或配置。
例如,MyBatis 的自动配置类 (MybatisAutoConfiguration) 可能会利用这个信息来自动扫描 Mapper 接口,而你不需要在 MyBatis 配置中手动指定所有 Mapper 的包路径。
@Import({AutoConfigurationImportSelector.class}) 给容器批量导入组件
Spring Boot 自动配置的核心入口,而 AutoConfigurationImportSelector 是这个机制中负责决策和加载的大脑。它的主要使命是告诉 Spring 容器:“应该自动配置哪些功能,以及按什么顺序配置”。工作流程如下图示:
点击查看AutoConfigurationImportSelector的核心代码:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);//给容器批量导入组件
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
// 获取@EnableAutoConfiguration注解的属性(如exclude, excludeName)
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 【核心步骤】获取到所有需要导入到容器中的配置类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 移除重复的配置类(防止多个jar包声明了相同的配置)
configurations = this.removeDuplicates(configurations);
//获取用户明确指定要排除的配置类(通过exclude/excludeName属性)
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
// 检查要排除的类是否合法(是否在候选列表中)
this.checkExcludedClasses(configurations, exclusions);
// 从候选列表中移除所有被排除的类
configurations.removeAll(exclusions);
// 【最关键的过滤步骤】根据条件注解进行筛选
configurations = this.getConfigurationClassFilter().filter(configurations);
// 触发事件,通知所有监听器最终的自动配置列表
this.fireAutoConfigurationImportEvents(configurations, exclusions);
// 将最终确定的配置列表和排除列表封装返回
return new AutoConfigurationEntry(configurations, exclusions);
}
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//这里调用了 `SpringFactoriesLoader` 类里面的 loadFactoryNames 方法
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
🔍 关键步骤详解:
-
步骤 3:
getCandidateConfigurations- 加载候选配置
这是“发现”阶段。该方法通过SpringFactoriesLoader机制,从所有 Jar 包的META-INF/spring.factories文件中,读取org.springframework.boot.autoconfigure.EnableAutoConfigurationkey 对应的所有配置类的全限定名。(例如,spring-boot-autoconfigure jar 包下的 spring.factories 文件就包含了上百个自动配置类)- 在 spring-boot-autoconfigure jar 包中,spring.factories 文件包含大量自动配置类(具体数量因 Boot 版本而异,例如 2.6.13 版本包含 133 个配置项)。:

org.springframework.boot:spring-boot-autoconfigure 这个 JAR 包中的 /META-INF/spring.factories 文件是自动配置类的清单。告诉 Spring Boot 有哪些“候选人”。是 Spring Boot 官方提供的、最全面的自动配置列表。它定义了所有 Spring Boot 内置支持的组件的自动配置类。
查看 SpringFactoriesLoader 类源码,找到
loadFactoryNames方法:public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { // 1. 确定要使用的类加载器(如果参数为空,则使用默认的) ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } // 2. 获取目标工厂接口/类的全限定名(例如:org.springframework.boot.autoconfigure.EnableAutoConfiguration) String factoryTypeName = factoryType.getName(); // 3. 调用核心加载方法,并获取该类型对应的工厂实现类名称列表 return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { // 1. 首先检查缓存,如果已经为这个ClassLoader加载过,直接返回缓存结果,提高性能 Map<String, List<String>> result = (Map)cache.get(classLoader); if (result != null) { return result; } else { // 2. 初始化一个空的结果Map,用于存放解析结果 Map<String, List<String>> result = new HashMap(); try { // 3. 【关键步骤】查找类路径下所有的 META-INF/spring.factories 文件 // 注意:这里用的是 getResources(复数),不是 getResource Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories"); // 4. 遍历找到的每一个 spring.factories 文件 while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); // 5. 将文件内容加载为Java Properties对象 Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); // 6. 遍历Properties中的每一个键值对 while(var6.hasNext()) { Map.Entry<?, ?> entry = (Map.Entry)var6.next(); String factoryTypeName = ((String)entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); // 7. 遍历实现类名称数组,将每个类名添加到结果Map对应的列表中 String[] var10 = factoryImplementationNames; int var11 = factoryImplementationNames.length; for(int var12 = 0; var12 < var11; ++var12) { String factoryImplementationName = var10[var12]; // computeIfAbsent: 如果Map中还没有这个factoryTypeName的键,就创建一个新ArrayList // 然后无论新旧的List,都把当前的实现类名称加进去 ((List)result.computeIfAbsent(factoryTypeName, (key) -> { return new ArrayList(); })).add(factoryImplementationName.trim()); } } } // 8. 对结果Map中的每一个List进行去重,并转换为不可修改的列表(保证安全) result.replaceAll((factoryType, implementations) -> { return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); }); // 9. 将最终结果放入缓存,以备下次使用 cache.put(classLoader, result); return result; } catch (IOException var14) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14); } } }这段代码是 Spring Framework 中 SpringFactoriesLoader 类的核心方法,它实现了 Spring 工厂加载机制,这是 Spring Boot 自动配置的基石。它的作用是从类路径中所有
META-INF/spring.factories文件中加载并解析出特定类型的工厂实现类。 - 在 spring-boot-autoconfigure jar 包中,spring.factories 文件包含大量自动配置类(具体数量因 Boot 版本而异,例如 2.6.13 版本包含 133 个配置项)。:
🌟 核心作用与设计思想总结
| 特性 | 说明 | 优点 |
|---|---|---|
| 全局扫描 | 使用 classLoader.getResources() 扫描整个类路径下所有 JAR 包中的 META-INF/spring.factories 文件。 | 可扩展性:任何第三方库都可以通过在自己 JAR 包中放置此文件来扩展 Spring。 |
| 统一约定 | 文件内容遵循 接口/抽象类全名=实现类全名1,实现类全名2,… 的 Properties 格式。 | 解耦:框架定义接口,实现由各个模块提供,双方不需要直接依赖。 |
| 缓存机制 | 使用静态 Map cache 以 ClassLoader 为键缓存加载结果。 | 高性能:避免每次都需要进行耗时的 I/O 扫描和解析。 |
| 多种工厂类型 | 一个文件中可以定义多种类型的工厂。不仅是自动配置,还用于初始化器、监听器等。 | 统一管理:一套机制服务多种扩展需求。 |
这一步之后,configurations 列表包含了所有“可能”被应用的自动配置类。
- 步骤 8:
getConfigurationClassFilter().filter(configurations) - 条件过滤
这是“决策”阶段,也是整个自动配置过程最智能、最核心的一步。它根据一系列条件注解(@Conditional...)来检查每个候选配置类是否真的应该被启用。
这些条件检查包括(但不限于):@ConditionalOnClass:类路径下是否存在指定的类?
例如,DataSourceAutoConfiguration只有在类路径下存在DataSource.class时才会生效。@ConditionalOnBean/@ConditionalOnMissingBean:容器中是否已经存在/缺少某个 Bean?- 这是实现“用户自定义配置优先”的关键。如果你自己定义了一个 DataSource Bean,那么 Spring Boot 提供的默认
DataSource配置就会因为@ConditionalOnMissingBean条件不满足而跳过。
- 这是实现“用户自定义配置优先”的关键。如果你自己定义了一个 DataSource Bean,那么 Spring Boot 提供的默认
@ConditionalOnProperty:应用配置文件中某个属性是否有特定的值?- 例如,很多功能可以通过 application.properties 中的 spring.somefeature.enabled=true/false 来开关。
@ConditionalOnWebApplication/@ConditionalOnNotWebApplication:当前应用是否是 Web 应用?
🌟 核心作用与设计思想总结
Spring Boot 的自动配置遵循“约定优于配置”和“按需加载”的原则。虽然 spring.factories 中定义了上百个配置类,但并非全部生效。其核心机制是通过一系列 @Conditional 条件注解进行判断,最终只有满足当前应用环境和配置的类才会被激活。
这里以AopAutoConfiguration为例,剖析其决策过程:
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop",name = {"auto"},havingValue = "true",matchIfMissing = true)
public class AopAutoConfiguration {
public AopAutoConfiguration() {}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass({"org.aspectj.weaver.Advice"})
@ConditionalOnProperty(prefix = "spring.aop",name = {"proxy-target-class"},havingValue = "true",matchIfMissing = true)
static class ClassProxyingConfiguration {
ClassProxyingConfiguration() {}
@Bean
static BeanFactoryPostProcessor forceAutoProxyCreatorToUseClassProxying() {
return (beanFactory) -> {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory;
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
};
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({Advice.class})
static class AspectJAutoProxyingConfiguration {
AspectJAutoProxyingConfiguration() {}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop",name = {"proxy-target-class"},havingValue = "true",matchIfMissing = true)
static class CglibAutoProxyConfiguration {
CglibAutoProxyConfiguration() {
}
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop",name = {"proxy-target-class"},havingValue = "false")
static class JdkDynamicAutoProxyConfiguration {
JdkDynamicAutoProxyConfiguration() {
}
}
}
}
-
总开关确认
AopAutoConfiguration 类首部通过@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)注解,为整个自动配置机制提供了总开关控制。该注解会检查应用配置中是否存在 spring.aop.auto 属性,当其值为 true 时,该配置类才会激活。其中 matchIfMissing = true 是关键配置属性,表示当配置文件中未显式定义 spring.aop.auto 属性时,系统将默认视其为 true 值。这种设计确保了 AOP 自动配置的默认启用机制。
因此,AopAutoConfiguration 默认处于生效状态。只有在配置文件中明确设置 spring.aop.auto=false 时,才会禁用该自动配置功能。
-
内部配置决策(按条件选择)
-
在 AspectJAutoProxyingConfiguration 配置类上使用了 @ConditionalOnClass({Advice.class}) 条件注解。Advice 类来自 org.aspectj:aspectjweaver 这个依赖,但是我们的项目中并没有引入它。因此,这个条件不成立,导致 AspectJAutoProxyingConfiguration 配置类没有被激活(即‘没有生效’)。
-
由于项目中没有引入 org.aspectj:aspectjweaver 依赖,无法满足 AspectJAutoProxyingConfiguration 配置类 @ConditionalOnClass({Advice.class}) 的条件,因此基于 AspectJ 的完整代理模式没有生效。
然而,正是由于缺少这个依赖,反而满足了 ClassProxyingConfiguration 配置类 @ConditionalOnMissingClass({“org.aspectj.weaver.Advice”}) 的条件。结合其 @ConditionalOnProperty 条件的默认值(matchIfMissing = true),该配置类被成功激活。
这相当于 Spring Boot 自动为我们 fallback 到了一个基础版的 AOP 自动配置。它确保了 Spring AOP 的核心功能(如 @Transactional 事务管理)依然可用,只是无法使用需要 AspectJ 支持的、更强大的切面编程功能。
-
经过这一步的筛选,configurations 列表中就只剩下了真正满足当前应用环境条件的、需要被启用的自动配置类。
我们debugger启动程序查看内容:
刚开始从spring factories文件中加载了全部的133项配置

经过getConfigurationClassFilter().filter(configurations)条件过滤后,真正被启用的只有28项
这一步之后,configurations 列表包含了所有真正被启用的自动配置类。
SpringBoot的自动装配流程此时才完成一半。接下来生效的配置类会向容器中注入大量组件,这些组件一旦存在于容器中,就意味着相应功能已具备。系统会优先使用用户自定义的组件,只有在用户没有配置时才会采用默认配置。
-
以
HttpEncodingAutoConfiguration为例,这个类是 SpringBoot 用于自动化配置 HTTP 请求和响应字符编码的核心组件,我们通过源码解析来深入阐述其工作原理。@Configuration(proxyBeanMethods = false) //标准配置类声明,使用 Lite 模式提升性能。 @EnableConfigurationProperties({ServerProperties.class}) //核心配置绑定。启用对 ServerProperties 类的配置绑定。所有以 server. 为前缀的配置属性(如 server.servlet.encoding.charset, server.port)将被读取并注入到 ServerProperties Bean 中。注意,编码相关的配置是 ServerProperties 的一个嵌套属性。 @ConditionalOnWebApplication(type = Type.SERVLET) //此配置仅在基于 Servlet 的 Web 应用中生效。 @ConditionalOnClass({CharacterEncodingFilter.class}) //条件检测,确保类路径下存在 Spring 的 CharacterEncodingFilter 类。这是字符编码过滤器的实现类。 /** * prefix = "server.servlet.encoding", value = {"enabled"}: 检查配置项 server.servlet.encoding.enabled 的值。 * matchIfMissing = true: 如果配置文件中未显式设置此属性,则默认视为 true。这意味着该自动配置默认是启用的,体现了 Spring Boot 的"开箱即用"理念。 * 用户只有在显式设置为 false (server.servlet.encoding.enabled=false) 时才会禁用此自动配置。 */ @ConditionalOnProperty(prefix = "server.servlet.encoding", value = {"enabled"}, matchIfMissing = true) // 总开关。这是最关键的条件注解: public class HttpEncodingAutoConfiguration { // 配置属性注入 private final Encoding properties; public HttpEncodingAutoConfiguration(ServerProperties properties) {this.properties = properties.getServlet().getEncoding();} // 核心 Bean 定义:字符编码过滤器 @Bean @ConditionalOnMissingBean // 关键条件:用户未自定义时生效 public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); // 1. 创建可排序的过滤器 filter.setEncoding(this.properties.getCharset().name()); // 2. 设置编码 (e.g., UTF-8) filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));// 3. 是否强制请求编码 filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));// 4. 是否强制响应编码 return filter; } @Bean public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {return new LocaleCharsetMappingsCustomizer(this.properties);} static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered { private final Encoding properties; LocaleCharsetMappingsCustomizer(Encoding properties) { this.properties = properties; } public void customize(ConfigurableServletWebServerFactory factory) { if (this.properties.getMapping() != null) { factory.setLocaleCharsetMappings(this.properties.getMapping()); } } public int getOrder() { return 0; } } }一、类级别注解分析

在类的开头用了@EnableConfigurationProperties和@ConditionalOnProperty注解@EnableConfigurationProperties({ServerProperties.class})
核心配置绑定。启用对ServerProperties类的配置绑定。所有以server.为前缀的配置属性(如server.servlet.encoding.charset,server.port)将被读取并注入到ServerPropertiesBean 中。注意,编码相关的配置是ServerProperties的一个嵌套属性@ConditionalOnProperty(prefix = "server.servlet.encoding", value = {"enabled"}, matchIfMissing = true)
总开关。这是最关键的条件注解:-
prefix = “server.servlet.encoding”, value = {“enabled”}: 检查配置项 server.servlet.encoding.enabled 的值。
-
matchIfMissing = true: 如果配置文件中未显式设置此属性,则默认视为 true。这意味着该自动配置默认是启用的,体现了 Spring Boot 的"开箱即用"理念。用户只有在显式设置为 false (server.servlet.encoding.enabled=false) 时才会禁用此自动配置。
-
二、配置属性注入

-
通过构造器注入已经绑定好配置的
ServerPropertiesBean。 -
从中提取出嵌套的编码配置部分 (
properties.getServlet().getEncoding()) 并存入字段this.properties。这个 Encoding 对象包含了charset,force,enabled,mapping等具体配置值。
三、核心 Bean 定义:字符编码过滤器

这是本配置类最核心的方法。-
@ConditionalOnMissingBean:用户自定义优先原则的体现。只有在当前 Spring 容器中不存在任何CharacterEncodingFilter类型的 Bean 时,才会执行此方法创建默认的过滤器。如果用户已经自己定义了一个,那么这个自动配置就会跳过。 -
创建与配置:
-
实例化一个
OrderedCharacterEncodingFilter(是CharacterEncodingFilter的子类,支持排序)。 -
setEncoding(...): 设置字符集。从配置中获取,例如server.servlet.encoding.charset=UTF-8。 -
setForceRequestEncoding(...): 设置是否强制请求使用该编码。从配置中获取,例如server.servlet.encoding.force-request=true。 -
setForceResponseEncoding(...): 设置是否强制响应使用该编码。从配置中获取,例如server.servlet.encoding.force-response=true。
-
四、定制器 Bean:区域设置与字符集映射

定制器 Bean:区域设置与字符集映射-
这个 Bean 的作用是进一步定制内嵌的 Servlet Web 服务器(如 Tomcat)。
-
它实现了
WebServerFactoryCustomizer接口,这是一个用于对已创建的 Web 服务器工厂进行后期加工的扩展点。 -
customize方法: 如果配置中指定了server.servlet.encoding.mapping属性(这是一个 Map,例如mapping.en=UTF-8, mapping.de=ISO-8859-1),它会将这些区域设置(Locale)与字符集(Charset)的映射关系设置到 Web 服务器工厂中。这为处理不同语言区域的请求提供了更精细的编码控制。 -
Ordered 接口: 指定该定制器的执行顺序。
五、配置属性示例
要使该自动配置生效,可以在application.properties中进行如下配置:# 启用编码过滤(默认true,可省略) server.servlet.encoding.enabled=true # 设置全局字符编码为 UTF-8 server.servlet.encoding.charset=UTF-8 # 强制请求使用配置的编码 server.servlet.encoding.force-request=true # 强制响应使用配置的编码 server.servlet.encoding.force-response=true # (可选) 设置特定区域设置的编码 server.servlet.encoding.mapping.en=UTF-8 server.servlet.encoding.mapping.de=ISO-8859-1
🌟 总结与设计思想
HttpEncodingAutoConfiguration 是一个典型的、功能完备的 Spring Boot 自动配置类,它展示了以下核心设计思想:
-
条件化配置: 通过一系列 @ConditionalOn… 注解,确保只有在正确的应用类型和依赖条件下才生效。
-
外部化配置: 通过 @EnableConfigurationProperties 与 ServerProperties 深度集成,将所有可配置选项暴露给用户。
-
默认约定: 使用 matchIfMissing = true 提供合理的默认行为(默认启用 UTF-8 编码),实现"开箱即用"。
-
用户自定义优先: 核心的 @ConditionalOnMissingBean 注解确保用户随时可以通过自己定义 CharacterEncodingFilter Bean 来完全覆盖此自动配置。
-
扩展性: 通过 WebServerFactoryCustomizer 提供对底层 Web 服务器的低级定制能力,满足更复杂的需求。
最终效果是:开发者仅需引入 Web Starter,无需任何配置,即可获得一个配置好的、强制使用 UTF-8 编码的过滤器,有效解决中文乱码等常见问题。同时,又可以通过简单的配置文件修改或自定义 Bean 来精细控制其行为。
有小伙伴就会有疑问了,我就写了一个简单的SpringBoot项目,也没有配什么端口号,怎么默认启动端口就是8080呢?
我记得我写SpringMVC的时候是要配端口号的啊,这就要讲一下SpringBoot的另一个重要概念:配置绑定功能
这里我们以MongoDB的配置类为例,深入解析其工作原理,先看 MongoProperties 源码
@ConfigurationProperties(prefix = "spring.data.mongodb")
public class MongoProperties {
public static final int DEFAULT_PORT = 27017;
public static final String DEFAULT_URI = "mongodb://localhost/test";
private String host;
private Integer port = null;
private String uri;
private String database;
private String authenticationDatabase;
private final Gridfs gridfs = new Gridfs();
private String username;
private char[] password;
private String replicaSetName;
private Class<?> fieldNamingStrategy;
private UuidRepresentation uuidRepresentation;
private Boolean autoIndexCreation;
public MongoProperties() {
this.uuidRepresentation = UuidRepresentation.JAVA_LEGACY;
}
//get、set方法
这里省略...
public String getUri() {
return this.uri;
}
public String determineUri() {
return this.uri != null ? this.uri : "mongodb://localhost/test";
}
public void setUri(String uri) {
this.uri = uri;
}
public Integer getPort() {
return this.port;
}
public void setPort(Integer port) {
this.port = port;
}
public Gridfs getGridfs() {
return this.gridfs;
}
public String getMongoClientDatabase() {
return this.database != null ? this.database : (new ConnectionString(this.determineUri())).getDatabase();
}
public Boolean isAutoIndexCreation() {
return this.autoIndexCreation;
}
public void setAutoIndexCreation(Boolean autoIndexCreation) {
this.autoIndexCreation = autoIndexCreation;
}
public static class Gridfs {
private String database;
private String bucket;
public Gridfs() {
}
public String getDatabase() {
return this.database;
}
public void setDatabase(String database) {
this.database = database;
}
public String getBucket() {
return this.bucket;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
}
}
-
类定义和注解

@ConfigurationProperties(prefix = "spring.data.mongodb")注解表示这个类用于绑定配置文件中以spring.data.mongodb为前缀的属性这意味着你可以在
application.properties或application.yml中使用如spring.data.mongodb.host=localhost这样的配置 -
常量定义
public static final int DEFAULT_PORT = 27017; public static final String DEFAULT_URI = "mongodb://localhost/test";定义了 MongoDB 的默认端口和默认连接 URI
当没有明确配置时,会使用这些默认值 -
属性字段
类中定义了多个属性字段,每个字段都对应一个配置选项:- host: MongoDB 服务器主机名
- port: MongoDB 服务器端口
- uri: MongoDB 连接 URI(如果设置了 URI,则会忽略单独的 host 和 port 设置)
- database: 要连接的数据库名称
- authenticationDatabase: 认证数据库
- gridfs: GridFS 相关配置(嵌套配置类)
- username: 连接用户名
- password: 连接密码(使用 char[] 而不是 String,出于安全考虑)
- replicaSetName: 副本集名称
- fieldNamingStrategy: 字段命名策略类
- uuidRepresentation: UUID 表示方式
- autoIndexCreation: 是否自动创建索引
-
构造函数
public MongoProperties() { this.uuidRepresentation = UuidRepresentation.JAVA_LEGACY; }在构造函数中设置了 uuidRepresentation 的默认值为 JAVA_LEGACY
-
方法解析
- 标准的 Getter 和 Setter 方法
为每个属性字段提供了标准的 getter 和 setter 方法,是 SpringBoot 配置属性绑定所必需的 - 特殊方法
public String determineUri() { return this.uri != null ? this.uri : "mongodb://localhost/test"; }
这个方法用于确定最终使用的 URI,如果明确配置了 URI,则使用配置的值;否则使用默认 URI
public String getMongoClientDatabase() { return this.database != null ? this.database : (new ConnectionString(this.determineUri())).getDatabase(); }这个方法用于确定要连接的数据库。如果明确配置了数据库名称,则使用配置的值;否则从 URI 中解析出数据库名称
- 标准的 Getter 和 Setter 方法
-
嵌套配置类 Gridfs
public static class Gridfs { private String database; private String bucket; // ... getters and setters }这是一个嵌套的配置类,用于配置 GridFS 相关属性,可以配置 GridFS 的数据库和存储桶名称。对应配置如:
spring.data.mongodb.gridfs.database=mygridfs -
配置示例
基于这个类,你可以在 application.properties 中进行如下配置:# 基本连接配置 spring.data.mongodb.host=localhost spring.data.mongodb.port=27017 spring.data.mongodb.database=mydatabase # 或者使用 URI 方式 spring.data.mongodb.uri=mongodb://user:password@localhost:27017/mydatabase # 认证配置 spring.data.mongodb.username=myuser spring.data.mongodb.password=mypassword spring.data.mongodb.authenticationDatabase=admin # GridFS 配置 spring.data.mongodb.gridfs.database=fs spring.data.mongodb.gridfs.bucket=mybucket # 其他配置 spring.data.mongodb.auto-index-creation=true
通过源码分析,我们了解到这个类是Spring Boot中用于MongoDB配置的属性类,它通过@ConfigurationProperties注解绑定配置文件中的属性。那么问题来了:这些配置文件究竟存放在哪里?是打包在jar文件中吗?
结论先行:这些属性不是写死的,它有配置文件,但这个“配置文件”指的是项目中的 application.properties 或 application.yml 文件。 MongoProperties 类的作用就是作为一个桥梁,将这些外部配置文件中的属性值映射到Java对象的字段上
-
配置文件的来源和位置
Spring Boot应用程序默认会加载名为 application.properties 或 application.yml 的配置文件 -
@ConfigurationProperties的作用:绑定
MongoProperties 类本身不是一个配置文件,它是一个配置属性持有类(或称为配置元数据类)。它的工作流程如下:- 编写配置:在
application.properties中写入MongoDB的相关配置。spring.data.mongodb.host=my-mongo-server.com spring.data.mongodb.port=27017 spring.data.mongodb.database=myappdb - SpringBoot启动并扫描:应用程序启动时,SpringBoot会自动扫描所有标注了
@ConfigurationProperties的类。 - 属性绑定(Magic Happens):SpringBoot 的配置绑定机制会:
- 读取配置文件。
- 根据
prefix = "spring.data.mongodb"找到所有以这个为前缀的属性。 - 截掉前缀,将剩下的部分(例如 host)与 MongoProperties 类中的字段名进行匹配。
- 通过类的
setter方法(如 setHost())将配置文件中字符串形式的的值(“my-mongo-server.com”),转换并注入到对应的字段中。
- 自动配置使用它:SpringBoot 的 MongoDB 自动配置类(通常是 MongoAutoConfiguration)会注入这个已经填充好值的 MongoProperties Bean,并用它来创建和配置真正的 MongoClient 连接对象。
- 编写配置:在
-
配置类的默认值
比如:private Integer port = null;他是字段的默认初始值。如果配置文件中没有设置 spring.data.mongodb.port端口号,那么这个字段的值就是 null。
随后,在 determineUri() 这类方法中,程序会判断:“如果用户没配端口,就用默认的 27017”
配置的优先级是:外部配置文件(最高)> Java代码中的默认初始值(最低)
为了更好地理解,我们可以做一个比喻,假设你在淘宝商城上的多家商店下单了多件商品,有鞋子,衣服,自行车等
- application.properties 文件:就像是一张订单,您在上面写下您的需求:MySQL的database叫什么、账密多少,Redis的服务器地址是多少、端口号多少,Activemq的账密多少、服务器地址多少
- XXXProperties 类:就像是一个个商家的标准订单表单,上面预先印好了各种可填的栏目,比如鞋子的码数、外观,衣服的款式、大小,自行车的配置和运输要求(host, port, database…)。@ConfigurationProperties 注解就是告诉淘宝商城(系统):“第一个表单是用来接 MySQL 订单的,第一个表单是接Redis的,最后一个表单是接Activemq的”。
- Spring Boot系统就像是淘宝商城,他拿你写的的订单,找到对应的各个商家的订单表单,把你填写的内容抄写到表单的对应栏目里,然后把这份填写好的表单交给各个商家(自动配置类)
- 自动配置类:各个商家根据淘宝商城发来的订单表单上填写的要求接单完成交易(创建MySQL连接、Redis链接、activemq连接)
因此,XXXProperties 类本身不包含任何写死的配置,它只是一个空的容器,等待外部配置文件( application.properties)来填充它
这种设计实现了完美的关注点分离:配置(做什么)和代码(怎么做)是分开的
实践
springboot debug模式
项目引入了场景依赖,如何判断哪些场景生效,哪些未生效?
启用SpringBoot的debug模式后,控制台会直接打印出生效和失效的场景信息。
在配置文件application.properties中添加配置:debugger=true

查看、使用引入场景的配置项
如果引入场景不符合当前业务需要,需要修改配置项,怎么找引入场景的配置项呢?
方法有三:
- 查看SpringBoot官方文档
文档链接 - 直接在ide中搜索相关配置
按 ctrl+N 输入 XXXproperties 查看XXX配置类,在@ConfigurationProperties标签内会有写有属性前缀,在applicantion.properties文件中进行相应的配置
我们以SecurityProperties为例
在application.properties文件中添加配置项
3. 可以在容器中放入一些自定义器来自定义一些规则
比如 XXXCustomizer,这里先埋个坑,后期我们会再讲怎么写自定义器
lombok
简化 JavaBean 的开发
@Data:生成getter、setter方法@ToString:生成toString方法@AllArgsConstructor:生成全参构造函数@NoArgsConstructor:生成无参构造函数
热部署插件
如果修改的是静态页就不需要重启
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
Ctrl +F9: 项目重启
Spring Initailizr
需要什么场景就勾选什么场景

更多推荐


所有评论(0)