ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SpringBoot自动配置与Bean管理机制:配置优先级及源码解析

SpringBoot自动配置与Bean管理机制:配置优先级及源码解析 SpringBoot 的“自动配置”到底是怎么发生的Bean 的管理背后又藏着哪些设计这些问题我当年学的时候也绕了很久今天这篇笔记就掰开揉碎讲清楚。这个标题里的内容是 Javaweb 学习路线中非常关键的一环。很多人能跑通 SpringBoot 项目但一问到“为什么加一个 starter 依赖功能就自动生效了”“配置文件里的参数优先级到底谁说了算”就卡住了。这篇文章适合所有正在学 SpringBoot、准备面试、或者想深入理解框架原理的 Java 开发者。我会把配置优先级、Bean 的管理和核心源码入口一次讲透并且结合我实际踩过的坑给你一套可直接复用的理解和排查方法。1. 内容整体设计与思路拆解1.1 为什么 SpringBoot 学习必须啃原理先聊一个很多人困惑的问题跑通一个 SpringBoot 项目太简单了为什么还要学原理我见过太多同学用 SpringBoot 写 CRUD 很溜但换个场景就懵了。比如明明配置了application.properties但某些值就是不生效想让某个类被 Spring 管理加了Component却报空指针项目里有两个数据源启动直接失败不知道为什么面试被问“SpringBoot 自动配置原理”只能背出“约定大于配置”这句话这些问题全都能在原理层面找到答案。SpringBoot 看起来是“简化配置”但它的底层是 Spring Framework 那一套完整的 IoC 容器和 AOP 机制。不啃原理你永远只能停留在“会用 API”的层面出了问题毫无排查头绪。记住一句话SpringBoot 的所有魔法本质上都是 Spring Framework 的 IoC 容器在背后运作SpringBoot 只是把这套容器做了更智能的封装。1.2 Day11 这个阶段应该建立的知识地图到了 Day11你已经不是纯新手的阶段了。我建议你心里要有这样一张知识地图第一层SpringBoot 是什么怎么快速搭建项目起步依赖怎么用第二层配置文件怎么写YAML 语法多环境配置第三层SpringBoot 内部机制也就是今天要讲的包括配置加载顺序、Bean 的实例化与装配、自动配置原理第四层整合第三方技术比如 MyBatis、Redis、MQ 等Day11 对应的就是第三层。这一层学好了你再看任何 SpringBoot 整合教程都会有“原来如此”的感觉。因为所有 starter 的整合逻辑万变不离其宗都是通过自动配置类往容器里注册 Bean。1.3 这套知识在实际项目中的价值我工作这些年配置优先级的知识在排查线上问题时帮过大忙。举个例子有一次生产环境的日志级别怎么调都不对明明在配置中心改了logging.level但日志还是打得很琐碎。后来排查发现项目本地application.properties里写了一个logging.level.rootDEBUG而配置中心的参数优先级在某些设置下反而低于本地文件。这就是典型的配置优先级问题不懂这个原理你连排查方向都找不到。再说Bean 管理。几乎所有框架整合都绕不开 Bean 的注册和获取。你理解了 Bean 的作用域、生命周期、条件装配你就理解了为什么Autowired有时候注入的是代理对象为什么某些 Bean 在单元测试里是 null为什么Configuration和Component看起来差不多但底层处理完全不同。源码分析更是如此。你不用把 SpringBoot 源码全读完但核心类如SpringApplication、AutoConfigurationImportSelector、ConfigurationClassPostProcessor的职责你必须要懂这是面试官最爱问、也是排查复杂问题必须依赖的知识。2. 配置优先级谁说了算2.1 SpringBoot 的配置来源有哪些SpringBoot 的配置来源非常多这是它灵活的原因也是新手容易懵的原因。我来梳理一下常见来源按我实际接触的频率排序命令行参数java -jar app.jar --server.port8081Java 系统属性System.setProperty()或在启动命令加-Dserver.port8081操作系统环境变量SERVER_PORT8081配置文件application.properties/application.yml/application.yaml配置中心如 Nacos、Spring Cloud Config每种来源都有它的适用场景。命令行参数适合临时指定端口、Profile环境变量适合容器化部署配置文件是日常开发的主力。但问题来了当一个 key 同时出现在多个来源里到底听谁的这就是配置优先级要解决的事。2.2 内部配置文件的三方对决properties vs yml vs yaml先说最基础的。在 SpringBoot 项目内部如果你同时放置了application.properties、application.yml、application.yaml三个文件而且配置了同一个 key加载优先级properties yml yaml这个优先级是 SpringBoot 内部加载机制决定的。ConfigDataEnvironmentPostProcessor在处理配置数据时会按固定顺序加载不同类型的文件。我实测过同一个server.port如果三种文件都写了最终生效的一定是 properties 里的值。注意很多教程说“yml 和 yaml 优先级相同”这其实不准确。SpringBoot 2.4 之后加载顺序是明确的 properties 优先于 ymlyml 优先于 yaml。虽然日常开发不会同时用三种文件但面试问到或者排查怪问题时知道这个顺序能让你更快定位。所以我的建议是统一使用一种格式。我个人推荐application.yml因为它的层级结构清晰不容易写错缩进而且大部分开源项目都在用。团队协作时混用格式最容易出问题。2.3 完整优先级链路从高到低排列SpringBoot 官方文档给出了一套完整的配置优先级。我把关键的挑出来从高到低排列命令行参数最高Java 系统属性-D参数操作系统环境变量application-{profile}.properties/yml带 Profile 的配置文件application.properties/yml主配置文件通过PropertySource加载的配置这个顺序意味着什么呢我用一个场景说明你的application.yml里写了server.port: 8080但部署时用java -jar app.jar --server.port9090启动最终端口是 9090。命令行参数覆盖了配置文件的值。这就是为什么生产环境临时调整端口不需要改代码重新打包直接用启动参数覆盖就行。还有 Profile 的优先级要特别注意application-prod.yml的优先级高于application.yml。SpringBoot 启动时会先加载主配置再加载application-{profile}配置后者会覆盖前者的同名配置项。这也是多环境配置能生效的底层原因。2.4 实战场景为什么我改了配置却不生效来复盘几个我实际遇到的“配置不生效”案例。案例一Profile 匹配失误我在application.yml里写了spring: profiles: active: dev然后在application-dev.yml里配置了数据源。结果启动时发现用的还是主配置里的数据源。排查半天发现原来application-test.yml文件也存在而某次部署时环境变量里设置了SPRING_PROFILES_ACTIVEtest。环境变量的优先级高于配置文件里的spring.profiles.active所以 dev 配置根本没被加载。所以当你发现“配置没生效”时第一件事不是改配置而是确认当前激活的是哪个 Profile。可以用启动日志里的The following profiles are active:这一行来确认。案例二本地配置文件覆盖了配置中心公司项目用 Nacos 做配置中心某天我在本地application.yml里调试加了spring.datasource.url提交代码后忘了删。结果同事拉代码启动数据库地址一直是本地调试地址配置中心的值完全不生效。这就是因为本地文件属于配置文件来源优先级高于远程配置中心的默认优先级。解决方案是本地调试配置可以用application-local.yml并通过 Profile 控制或者遵守团队规范不在公共配置里写环境相关的值。2.5 独家心得如何快速判断配置生效值我在排查配置问题时有一套固定打法分享给你启动时开启配置日志在application.yml里加debug: trueSpringBoot 会打印所有自动配置类的匹配情况也能帮你确认加载了哪些配置用 Actuator 的env端点访问/actuator/env可以看到所有配置来源及每个 key 的最终值还能看到每个值来自哪个来源极其好用临时修改验证用命令行参数覆盖确认修改入口比如我想确认server.port到底生效的是哪个值就先访问 Actuator 的 env 端点搜索server.port能看到类似这样的信息{ propertySources: [ { name: commandLineArgs, properties: { server.port: { value: 9090 } } }, { name: application.yml, properties: { server.port: { value: 8080 } } } ] }一眼就能看出谁覆盖了谁不用瞎猜。这个技能在排查线上问题时能救命的。3. Bean 的管理从实例化到销毁3.1 Bean 实例化的三种方式Spring 容器怎么创建一个 Bean这是我学 Spring 的时候第一个真正理解“容器”概念的点。Bean 实例化有三种方式方式一构造函数实例化最常用Component public class UserService { public UserService() { System.out.println(UserService 被创建了); } }Spring 容器通过反射调用无参构造函数创建对象。注意如果你定义了有参构造函数而没写无参构造函数Spring 会报错除非你用Autowired标注有参构造或者只有一个构造函数。方式二静态工厂方法public class CarFactory { public static Car createCar() { return new Car(); } }配置类里这样注册Bean public Car car() { return CarFactory.createCar(); }这种方式在整合第三方库时很常见。比如某些 SDK 只提供静态工厂方法不让你直接 new。方式三实例工厂方法public class CarFactory { public Car createCar() { return new Car(); } }注册方式Bean public CarFactory carFactory() { return new CarFactory(); } Bean public Car car(CarFactory factory) { return factory.createCar(); }这三种方式本质上都是告诉 Spring“怎么把这个对象创建出来”。理解了这个你就明白为什么有些类你不能直接new必须通过 Spring 获取——因为创建逻辑在容器里。3.2 Bean 的生命周期从诞生到销毁Bean 的生命周期是面试高频题也是理解容器管理价值的关键。我按时间线梳理实例化Spring 通过构造器创建 Bean 实例前面讲的三种方式之一属性填充Spring 通过Autowired、Resource或 XML 配置把依赖注入进去初始化前执行BeanPostProcessor的postProcessBeforeInitialization方法初始化执行PostConstruct标注的方法或实现InitializingBean接口的afterPropertiesSet方法初始化后执行BeanPostProcessor的postProcessAfterInitialization方法AOP 代理一般在这里生成使用Bean 被容器管理被业务代码调用销毁前执行PreDestroy标注的方法或实现DisposableBean接口的destroy方法这里有个重要的细节AOP 代理对象的生成发生在初始化后阶段。这意味着如果你在PostConstruct方法里调用this的方法不会被 AOP 拦截只有从容器中获取的代理对象调用才会被拦截。这个特性在排查事务失效问题时非常关键。3.3 Bean 的作用域Singleton 与 PrototypeSpring 默认的 Bean 作用域是singleton单例即整个容器中只有一个实例。你多次Autowired获取到的是同一个对象。Component Scope(prototype) public class TaskExecutor { // 每次获取都是新对象 }prototype作用域下每次从容器获取都会创建新实例。我遇到过一个问题一个singleton的 Service 里注入了prototype的组件导致每次调用拿到的都是同一个实例原型作用域完全失效。这就是典型的单例 Bean 依赖原型 Bean的问题。解决方案有两种使用Lookup方法注入使用ObjectProviderT推荐Component public class SingletonService { Autowired private ObjectProviderPrototypeBean prototypeBeanProvider; public void doSomething() { PrototypeBean bean prototypeBeanProvider.getObject(); // 每次调用都是新实例 } }这种问题在业务中很少遇到但理解了作用域原理遇到时就不会一脸懵。3.4 默认使用 CGLIB 代理一个容易忽略的细节SpringBoot 2.x 之后Configuration类默认使用 CGLIB 代理而Component默认不代理。这个细节很多人忽略但它影响深远。为什么Configuration要代理看这个例子Configuration public class AppConfig { Bean public DataSource dataSource() { return new DataSource(); } Bean public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(dataSource()); } }如果AppConfig不被代理那么jdbcTemplate()方法里调用的dataSource()会直接执行方法逻辑每次都会new一个新的DataSource那容器里的DataSourceBean 就不是同一个对象了。CGLIB 代理保证了Bean方法在被调用时如果容器中已有对应 Bean直接返回容器中的实例不会重复创建。这就是Bean 的单例性保证。理解这一点之后你就知道为什么Bean方法里创建的对象会被 Spring 管理为什么必须在Configuration类中使用Bean而不是在Component类中使用。虽然Component里的Bean也能生效但不会经过 CGLIB 代理如果Bean方法之间互相调用就会出现重复实例问题。注意SpringBoot 2.2 开始proxyBeanMethods默认是true。如果你确定不需要方法间调用保证单例比如某些特殊场景可以设Configuration(proxyBeanMethods false)关闭代理来提升启动性能。3.5 条件装配Bean 按需注册的魔法SpringBoot 的自动配置本质上就是大量使用条件装配注解来决定哪些 Bean 该注册、哪些不该注册。最常用的是ConditionalOnClass当类路径存在某个类时才注册 BeanConditionalOnMissingBean当容器中不存在某个 Bean 时才注册ConditionalOnProperty当配置项满足条件时才注册举个例子Bean ConditionalOnMissingBean public ObjectMapper objectMapper() { return new ObjectMapper(); }这个配置的意思是只有当容器里没有ObjectMapper类型的 Bean 时SpringBoot 才会注册这个默认的。如果你自己定义了一个定制化的ObjectMapper自动配置的就不会覆盖你。这是 SpringBoot 自动配置“可覆盖”的底层原理。你推荐整合 MyBatis 时写的MapperScan本质也是通过Import注册MapperScannerRegistrar来扫描并注册 Mapper 接口的 Bean。4. 原理与源码分析从启动到自动配置4.1 源码分析的入口SpringApplication源码分析不要从main方法开始要从SpringApplication.run()开始。这是整个 SpringBoot 的启动入口。SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }run方法内部做了几件核心事情创建SpringApplication实例调用run(String... args)方法启动 Spring 容器refreshContext执行ApplicationRunner和CommandLineRunner回调我当年看源码喜欢追着调用链往下看但很快发现方向错了。正确的做法是带着问题看代码我想知道自动配置是怎么加载的那就直接搜AutoConfigurationImportSelector。4.2SpringBootApplication的三角关系SpringBootApplication是一个组合注解它包含了三个关键注解SpringBootConfiguration其实就是Configuration标记这是配置类EnableAutoConfiguration开启自动配置机制核心中的核心ComponentScan扫描当前包及其子包下的Component、Service、Controller等注解很多人踩过一个坑ComponentScan默认扫描范围是主启动类所在包及其子包。如果你的 Controller 或 Service 放到了包外面的目录Spring 根本扫描不到启动不报错但访问接口 404。经验主启动类一定要放在项目包的最外层。这不是代码风格问题是 SpringBoot 工作机制决定的。4.3EnableAutoConfiguration的加载机制EnableAutoConfiguration内部的核心是Import(AutoConfigurationImportSelector.class)。AutoConfigurationImportSelector实现了ImportSelector接口它的selectImports方法会返回一个字符串数组数组里是你要加载的自动配置类全类名。这些配置类名从哪里来在 SpringBoot 2.x 中通过加载所有META-INF/spring.factories文件SpringBoot 3.x 改成了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中org.springframework.boot.autoconfigure.EnableAutoConfiguration键对应的值。你可以打开spring-boot-autoconfigure这个 jar 包在META-INF下找到这个文件打开看一眼里面维护了一堆配置类org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration ...这就回答了开头那个问题为什么加了一个 starter 依赖功能就自动生效了因为 starter 依赖里包含了自动配置的 jar 包SpringBoot 启动时会自动扫描并加载这些配置类配置类里通过ConditionalOnClass判断类路径中是否存在所需依赖存在就注册对应的 Bean。4.4 条件注解在源码中如何发挥作用我拿DataSourceAutoConfiguration举例。它的源码大致结构是AutoConfiguration ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) ConditionalOnMissingBean(type io.r2dbc.spi.ConnectionFactory) EnableConfigurationProperties(DataSourceProperties.class) Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceCheckpointRestartConfiguration.class }) public class DataSourceAutoConfiguration { // 内部类池化数据源自动配置 Configuration(proxyBeanMethods false) Conditional(DataSourceAutoConfiguration.PooledDataSourceCondition.class) ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class, DataSourceConfiguration.Generic.class }) protected static class PooledDataSourceConfiguration {} }展开看逻辑是这样的ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })确保类路径中有数据源相关类否则直接跳过这个配置类ConditionalOnMissingBean如果你自己定义了一个DataSourceBean自动配置就不生效使用你的定义EnableConfigurationProperties(DataSourceProperties.class)把spring.datasource前缀的配置项绑定到DataSourceProperties类上这就是为什么你只需要在配置文件中写spring.datasource.urlSpringBoot 就能自动创建一个 HikariDataSource。因为在PooledDataSourceConfiguration里有一个Hikari配置类它通过ConditionalOnClass(HikariDataSource.class)检测到 Hikari 依赖存在再根据配置项创建DataSourceBean。4.5 配置属性绑定ConfigurationProperties 的原理上面提到了EnableConfigurationProperties(DataSourceProperties.class)这里展开讲一下。ConfigurationProperties注解的作用是把配置文件中的值绑定到一个 Java 对象上。ConfigurationProperties(prefix spring.datasource) public class DataSourceProperties { private String url; private String username; private String password; // getter/setter }SpringBoot 启动时会创建一个DataSourcePropertiesBean并把spring.datasource.*开头的配置值通过 setter 方法绑定到对应属性上。这个机制的背后是 Spring Boot 的ConfigurationPropertiesBindingPostProcessor它也是一个BeanPostProcessor在 Bean 初始化阶段执行属性绑定。我在实际工作中经常用这种方式把自定义配置整理成对象比如custom: upload: path: /data/upload max-size: 104857600 sms: access-key: xxx secret-key: xxx对应配置类Component ConfigurationProperties(prefix custom) public class CustomProperties { private Upload upload new Upload(); private Sms sms new Sms(); // getter/setter }这样管理配置比在业务代码里到处写Value要优雅得多。这也是为什么 SpringBoot 项目里很少看到Value满天飞的原因。5. 实操验证手动复现自动配置过程5.1 环境准备与项目搭建原理讲再多不如动手验证一次。我建议你也跟我一样从一个空项目开始手动走一遍自动配置的过程。先创建一个 SpringBoot 项目只引入最基础的spring-boot-starter-web和spring-boot-starter后者一般会被 web 间接引入。然后用 IDE 打开依赖树确认spring-boot-autoconfigure在依赖列表里。打开 Maven 依赖面板找到spring-boot-autoconfigure对应的 jar 包。用 IDE 打开它的源码找到META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件你应该能看到一长串自动配置类名。5.2 第一步看自动配置类如何被加载在启动类里临时加一个ApplicationRunner打印所有被加载的自动配置类SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }然后加一个配置类实现ApplicationContextAware来查看容器中的 BeanComponent public class ContextPrinter implements ApplicationContextAware, ApplicationRunner { private ApplicationContext context; Override public void setApplicationContext(ApplicationContext applicationContext) { this.context applicationContext; } Override public void run(ApplicationArguments args) { String[] beanNames context.getBeanDefinitionNames(); System.out.println(Total beans: beanNames.length); for (String name : beanNames) { System.out.println(name); } } }启动项目你会看到容器里有大量 Bean比如dispatcherServlet、characterEncodingFilter、requestContextFilter等。这些你并没有手动注册全是自动配置干的。这就是自动配置最直观的验证。5.3 第二步开启调试模式看条件判断在application.yml里加debug: true重启项目日志里会出现类似这样的内容Positive matches: ----------------- DispatcherServletAutoConfiguration matched: - ConditionalOnClass found required classes org.springframework.web.servlet.DispatcherServlet, jakarta.servlet.Servlet (OnClassCondition) - ConditionalOnWebApplication found session scope (OnWebApplicationCondition) Negative matches: ----------------- ActiveMQAutoConfiguration: Did not match: - ConditionalOnClass did not find required class jakarta.jms.ConnectionFactory (OnClassCondition)Positive matches表示自动配置类匹配成功、生效了Negative matches表示没生效并且会告诉你为什么。这是排查“为什么某个自动配置没生效”的最快途径。5.4 第三步手动模拟一个自动配置类理解了流程之后我建议你手动写一个自动配置类把整个过程串一遍。先定义一个配置属性类ConfigurationProperties(prefix demo) public class DemoProperties { private String message default message; // getter/setter }然后写自动配置类AutoConfiguration EnableConfigurationProperties(DemoProperties.class) ConditionalOnProperty(prefix demo, name enabled, havingValue true) public class DemoAutoConfiguration { Bean ConditionalOnMissingBean public DemoService demoService(DemoProperties properties) { return new DemoService(properties.getMessage()); } }接着在src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件里加上com.example.config.DemoAutoConfiguration最后在启动类上加上SpringBootApplication(scanBasePackages com.example)保证包扫描范围运行项目你会看到demoService这个 Bean 被自动注册了。这个练习做完你对自动配置的理解会从“听过概念”变成“亲手实现过”面试时被问到原理你能从AutoConfigurationImportSelector讲到条件注解再讲到配置属性绑定整个链路完全打通。5.5 实战建议源码应该怎么读很多同学想读源码但打开 SpringBoot 源码就迷失了。我给你一套方法不要从头读。源码是成千上万行线性读根本记不住。带着问题读比如“DataSource 是怎么自动配置的”“内嵌 Tomcat 是怎么启动的”先看类名和注释。Spring 源码的类名和 Javadoc 写得非常清晰比如AutoConfigurationImportSelector一眼就知道是“自动配置导入选择器”抓主链路别陷在细节里。比如看SpringApplication.run()核心就是refreshContext()别纠结那些回调逻辑跟着断点走。在selectImports方法里打断点看返回的数组里都有哪些类一步步往下走配一个源码阅读项目不要直接看 jar 包里的反编译代码。用 Maven 引入依赖时IDE 会自动关联源码 jar源码分析不是把代码抄一遍而是理解设计思想。SpringBoot 的自动配置核心思想就是通过条件注解 外部化配置把常见的 Bean 装配方案做成可插拔、可覆盖的模块。6. 常见问题与排查技巧实录6.1 高频问题为什么 Autowired 注入为 null这是群里问烂了的问题。排查思路按顺序来确认类是否被 Spring 管理被注入的类和注入的类是否都在ComponentScan扫描范围内。主启动类包路径对不对确认没有多个同类 Bean多个实现类时Autowired按类型注入会失败需要用Qualifier指定名字确认不是在 new 出来的对象里注入手动new的对象不在 Spring 容器中Spring 不会帮你注入依赖确认 Bean 创建顺序如果 A 的构造函数里提前使用了 B 的某些方法而 B 还没创建完可能拿到不完整对象还有一种隐蔽场景在Configuration类中的Bean方法里直接new了一个依赖对象导致这个对象不经过 Spring 容器其内部的Autowired字段全部为 null。解决办法是让Bean方法的参数由 Spring 注入而不是手动new。6.2 高频问题自动配置为什么不生效排查自动配置不生效我有一套标准流程确认依赖是否引入自动配置生效的前提是 jar 包在类路径中开debug: true看日志在Negative matches里找对应的配置类看“Did not match”的原因确认条件注解是否满足最常见的是ConditionalOnClass找不到类、ConditionalOnProperty的值没配确认是否被自己的 Bean 覆盖ConditionalOnMissingBean情况下你定义了同类型 Bean自动配置就不生效了举个例子你加了 MyBatis 的依赖但 DataSource 没生效。开 debug 一看DataSourceAutoConfiguration显示DataSourceAutoConfiguration: Did not match: - ConditionalOnClass did not find required class org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType这说明你的依赖里缺了spring-jdbc。加依赖即可解决。6.3 高频问题配置项冲突如何处理当一个配置项在多个位置出现时之前讲过优先级。实际处理方式开发环境统一用application.yml不要混用多格式多环境用application-{profile}.yml区分激活方式用SPRING_PROFILES_ACTIVE环境变量或spring.profiles.active生产环境临时调整用命令行参数覆盖无需改动配置文件和重新打包敏感信息用环境变量或配置中心不要写进配置文件提交到 git遇到“改了配置不生效”时最有效的操作是用 Actuator 的env端点查看实际生效值和来源而不是反复改配置文件。提示Actuator 的 env 端点在生产环境要保护起来。暴露信息太多有安全风险。建议只在内网环境开放或通过management.endpoints.web.exposure.include控制暴露范围。6.4 独家排错小技巧我在排查 SpringBoot 配置和 Bean 问题时沉淀了几个小技巧技巧一启动失败时打印完整堆栈如果启动失败但信息不够可以在application.yml里加logging: level: root: DEBUG org.springframework.boot.autoconfigure: DEBUG这样能看到大量自动配置的判定日志虽然不是每次都需要但必要时很有用。技巧二用 ApplicationContext 列出所有 Bean临时写一个接口返回容器中所有 Bean 的名字和类型用来确认哪些 Bean 被注册了、类型是什么。排查NoSuchBeanDefinitionException时特别管用。技巧三Bean 的创建顺序用 DependsOn 控制虽然不推荐滥用但某些特殊场景比如静态初始化依赖 Bean可以用DependsOn指定 Bean 的创建顺序。注意这只是治标方案根因往往是设计问题。技巧四启动参数覆盖一切线上临时要改端口、改日志级别用命令行参数。注意命令行参数和其他来源的参数在执行SpringApplication.run(args)时会统一处理所以 args 必须传递进去否则命令行参数不生效。7. 这一天的学习我建议你这样收尾Day11 的内容量不小但我建议你别急着往下学。先做这几件事第一把配置优先级那张链路图记下来找时间自己动手验证两三个优先级场景。不需要全验证选命令行参数覆盖配置文件、Profile 覆盖主配置这两个场景就够了能加深理解。第二把 Bean 的生命周期和三种实例化方式整理成笔记。面试常考而且你后面学 Spring Cloud、阅读框架源码都会用到这些概念。第三根据 5.4 节手动实现一个自动配置类。哪怕只是照着抄一遍也能帮你把AutoConfigurationImportSelector、条件注解、配置属性绑定这几个点串起来。第四把spring-boot-autoconfigure里的AutoConfiguration.imports文件翻一遍。你不需要理解每个配置类但扫一遍名字就能对 SpringBoot 的“势力范围”有个全局印象。源码这块我理解你不可能一次全读懂。我的建议是第一遍只求看懂主链路第二遍带着自己的疑问深入。比如你看完自动配置类的加载过程产生了“如果我自己定义一个同名配置类会怎么样”的疑问带着这个问题去看条件注解的源码理解会非常透彻。SpringBoot 的核心原理学到这个程度你已经甩开大多数只会 CRUD 的人了。下一步学 Spring Cloud 时你会发现服务注册、配置中心、负载均衡这些组件本质上都是把一个或多个 Bean 注册到容器里通过条件注解和配置项控制启停。Day11 打下的底子会在后面反复用到。
返回列表