ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot自动配置原理与实战:从启动流程到自定义Starter

Spring Boot自动配置原理与实战:从启动流程到自定义Starter 很多用Spring Boot的朋友上来就是SpringBootApplication一加再写个Controller就跑起来了。项目跑通当然没问题但你要是问它为什么加一个spring-boot-starter-web就能自动起Tomcat为什么DataSource你不配Bean也能注入为什么不同的项目里有的自动配置生效、有的不生效很多人就开始含糊了。说实话我刚用Spring Boot那会儿也是这个状态把自动配置当黑盒用。直到有一次生产环境出问题某个模块的配置怎么都不生效我才被迫把启动流程和条件装配逻辑从头到尾啃了一遍。啃完之后最大的感受是这玩意儿根本不是什么魔法本质上就是“固定加载流程 一堆条件注解”道理通了之后排查问题、写自定义Starter都会顺畅很多。这篇东西我想从启动流程讲到条件化装配再到自动配置类的实际拆解、诊断报告的读法最后给你一个能直接抄作业的自定义Starter示例。适合刚接触Spring Boot、想搞懂“为什么它能自动配好”的初学者也适合已经被自动配置坑过、想系统排查问题的同学。1. 自动配置到底解决了什么问题——先从一个老Spring项目的痛说起要理解Spring Boot的自动配置最好的方式不是直接背概念而是先看看没有它的时候我们是怎么写Spring项目的。1.1 没有自动配置的时代一份真实的SSM配置清单大概在Spring Boot出来之前做Java Web开发的主流方案是SSM也就是Spring Spring MVC MyBatis。那时候一个项目跑起来XML配置是绕不开的。我给你还原一下当年一个普通项目的配置长什么样一个web.xml里面要配DispatcherServlet、Spring的ContextLoaderListener还要处理字符编码过滤器。一个applicationContext.xml里面要配组件扫描路径、数据源Bean、事务管理器引入MyBatis相关的配置。一个spring-mvc.xml里面要开mvc:annotation-driven/配视图解析器、静态资源映射、拦截器还要扫描Controller。如果用了MyBatis还得配SqlSessionFactoryBean里面再把dataSource、mapperLocations指定一遍。就这几样东西几百行XML是少不了的。而且每个项目之间高度相似都是复制粘贴改一改。更麻烦的是这套配置里藏着无数细节比如DispatcherServlet的init-param配错了或者spring-mvc.xml没被加载项目启动就是不报错但接口全404排查起来极其痛苦。Spring Boot的自动配置本质上就是把这一整套“公共基础设施”的配置工作收编了。你加一个spring-boot-starter-web依赖它就把DispatcherServlet、CharacterEncodingFilter、ViewResolver这些该准备的东西全部准备好你加一个数据库驱动和spring-boot-starter-jdbc它就帮你把DataSource、JdbcTemplate这些Bean也准备好。你写的配置类里那些重复劳动它全包了。1.2 自动配置的设计目标把“约定”变成“默认”自动配置的核心理念是“约定优于配置”。但这句话很多人理解偏了以为约定优于配置就是不用配置了。准确地说它想做的是在你什么都不说的时候给你一套合理的默认行为你有特殊需求的时候又能轻松覆盖它。举一个最典型的例子我做项目时经常遇到一个需求应用需要对序列化有一些定制。如果你用Spring Boot什么都不用做Jackson就会自动配置好一个ObjectMapperJSON序列化开箱即用。如果你觉得它序列化LocalDateTime格式不对你可以自己在容器里定义一个ObjectMapper或者调整application.properties里spring.jackson.date-format。注意这里的核心在于你定义的Bean会覆盖自动配置创建的Bean而不是自动配置的Bean覆盖你。这个机制就依赖后面要讲的ConditionalOnMissingBean。所以自动配置不是“黑魔法”它更像是一个守规矩的管家主人没有明确指示时管家按默认方式办主人明确布置了任务管家自动退到一边绝不给主人添乱。1.3 自动配置不等于“自动注册一切”边界和误区这里必须泼一盆冷水自动配置解决的是基础设施Bean的创建问题不负责你的业务Bean。它会帮你搞定DataSource、RedisTemplate、RestTemplateBuilder、ObjectMapper这些公共组件但绝对不会帮你创建UserService、OrderService这种业务类。原因很简单自动配置是框架层面的东西它不可能知道你的业务里有哪些服务、它们之间怎么依赖。所以业务Bean的职责交给了ComponentScanComponent/Service这套机制。很多初学者在项目里发现“我明明加了Redis依赖为什么RedisTemplate没生效”有时候不是因为自动配置没跑而是自己的配置类不在扫描路径内或者被条件判断给挡住了。还有一个误区是自动配置不是越多越好。在一个大型项目中如果完全不理解自动配置的条件机制很容易出现启动慢、Bean冲突、意外加载了根本用不到的组件等问题。理解了自动配置的边界和条件逻辑之后你才能真正做到“按需使用”而不是靠瞎猜解决。2. 从启动入口拆解Spring Boot自动配置的全流程理解自动配置的第一步是把Spring Boot的启动入口扒开看。main方法里那行SpringApplication.run(Application.class, args)看着简单背后其实是一条相当长的执行链路。2.1 SpringBootApplication是一个复合注解先看每个Spring Boot项目都会贴的SpringBootApplication。这行注解本身并不神秘它其实是三个注解的合体SpringBootConfiguration本质上是一个Configuration标记这是一个配置类。EnableAutoConfiguration自动配置的入口它是整个机制最关键的一把钥匙。ComponentScan组件扫描默认扫描启动类所在包及其子包。这里最值得研究的是EnableAutoConfiguration。点进源码你会发现它通过Import(AutoConfigurationImportSelector.class)往容器里导入了一个非常特殊的类。ImportSelector的作用是告诉Spring“你去加载哪些配置类”。AutoConfigurationImportSelector的核心工作就是去META-INF下的配置文件中找到所有自动配置类然后按顺序交给容器处理。如果你用过Spring Framework的Import你可能会知道ImportSelector并不是新东西。Spring Boot的聪明之处在于它把“选中哪些配置类”这个动作做成了一套可配置、可排序、可排除的机制。2.2 SpringApplication.run()启动流程的关键节点SpringApplication.run()启动流程大致可以分成几个关键阶段创建SpringApplication实例。在这个阶段框架会推断当前应用的类型Servlet还是Reactive、加载spring.factories里定义的ApplicationContextInitializer和ApplicationListener。调用run()方法。这里包含准备环境变量Environment、创建ApplicationContext、执行初始化器、刷新容器等步骤。真正的重头戏发生在refreshContext()阶段。这一步会调用AbstractApplicationContext.refresh()而ConfigurationClassPostProcessor会在这一阶段处理所有Configuration类其中就包括AutoConfigurationImportSelector选出来的那一堆自动配置类。容器刷新完成后Spring Boot还会做一些收尾工作比如启动Web服务器、发布ApplicationReadyEvent等。很多人以为自动配置在run()的一开始就干了其实不是。自动配置类的加载和普通Configuration的解析一样都是在容器刷新阶段完成的只是它多了一个“选哪些类”的前置步骤。2.3 从spring.factories到AutoConfiguration.imports既然AutoConfigurationImportSelector要去META-INF下找自动配置类那它具体去哪儿找这里就涉及Spring Boot很重要的一个演进。在Spring Boot 2.7之前自动配置类统一写在META-INF/spring.factories文件里通过EnableAutoConfiguration这个Key来声明。但这个文件是给整个Spring Boot生态共用的里面什么乱七八糟的Key都有框架要把整个文件加载完再去筛选自己关心的Key效率并不高也不太利于排查。从Spring Boot 2.7开始官方引入了一个新的配置文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。这个文件干净得多每一行就是一个自动配置类的全限定名。Spring Boot 3.0之后spring.factories里的EnableAutoConfiguration配置方式被彻底移除了自动配置统一走AutoConfiguration.imports。这个改动对写自定义Starter的人很重要。你在打包自定义Starter的时候如果是基于Spring Boot 3.x就必须把自动配置类写到AutoConfiguration.imports里如果是维护老项目spring.factories还能用但最好尽快跟上新格式。我自己做新项目时一般直接认准AutoConfiguration.imports。2.4 自动配置类的加载与排序机制自动配置类数量不少Spring Boot自带的就有上百个。如果它们之间有先后关系怎么办比如RedisAutoConfiguration可能定义了RedisTemplate而某个组件自动配置需要依赖它得保证先加载前者。Spring Boot的解决方案是提供三个排序注解AutoConfigureOrder指定排序优先级数值越小越先加载。AutoConfigureBefore指定该配置类必须在某些配置类之前加载。AutoConfigureAfter指定该配置类必须在某些配置类之后加载。AutoConfigurationImportSelector在拿到所有自动配置类后会先做去重再按照这三类注解计算一个排序结果最后才把排序好的数组交给容器去解析。这里有个细节很多人忽略自动配置类之间的“顺序”影响的只是配置类的解析顺序并不完全等同于Bean的创建顺序。但由于很多条件注解比如ConditionalOnMissingBean要判断容器里是否已经有某个Bean所以配置类的解析顺序会直接影响条件判断结果。这也是为什么官方不建议在自动配置类里随意使用ConditionalOnBean因为它的判断结果很可能被解析顺序干扰。3. 条件化装配原理自动配置的“智能开关”如果没有条件化装配上面那一大堆自动配置类全都会被加载那项目启动时必然炸锅没有Redis依赖却创建RedisTemplate没有DataSource依赖却创建DataSource谁受得了条件化装配就是自动配置的“总闸刀”让它能在合适的场景下才生效。3.1 Conditional家族的几个主力注解Spring Boot在Spring Framework原生的Conditional之上扩展了一组非常好用的注解最常见的几个ConditionalOnClass/ConditionalOnMissingClass判断某个类是否在类路径下。这是最常用的“依赖检查”比如RedisAutoConfiguration上就有ConditionalOnClass(RedisOperations.class)类路径下没有Redis相关的类这个自动配置直接跳过。ConditionalOnBean/ConditionalOnMissingBean判断容器中是否已经注册了某个Bean。这是“让位机制”的关键如果用户自己定义了DataSource自动配置就不再多管闲事。ConditionalOnProperty判断配置项是否存在或等于某个值。常用于控制某个功能模块的开关比如ConditionalOnProperty(name demo.enabled, havingValue true)。ConditionalOnWebApplication/ConditionalOnNotWebApplication判断当前应用是不是Web应用。这一组注解组合使用之后一个自动配置类就能做到只有当你引入依赖、没自己定义Bean、配置项也满足条件时它才干活。这就是自动配置能“智能”的根本原因。3.2 条件评估的顺序和边界有一个非常关键的细节想彻底搞懂自动配置必须理解条件注解的评估发生在配置类解析阶段而不是Bean实例化阶段。Spring容器在刷新时会通过ConfigurationClassPostProcessor来处理所有的Configuration类。这个处理过程是分阶段的先解析、再注册。解析阶段会计算每个配置类上的Conditional条件条件不满足的配置类直接不会被解析里面定义的Bean也就跟着消失了。如果条件满足才会接着解析Bean方法然后把Bean定义注册到容器里。这个设计带来了一个边界问题ConditionalOnBean判断的是“当前已经注册的BeanDefinition”而不是“最终会实例化出来的Bean对象”。如果你在一个自动配置类里用ConditionalOnBean(MyService.class)但在解析时MyService的BeanDefinition还没注册即使最后容器里会有这个Bean条件也会判定为不满足。Spring Boot官方对自动配置类的建议是优先使用ConditionalOnMissingBean尽量避免使用ConditionalOnBean原因就是这个评估顺序容易误判。如果你确实需要更精细的控制可以实现ConfigurationCondition接口指定条件评估的ConfigurationPhase让它恰好在某个阶段再判断。3.3 自己动手写一个条件注解光看不练很难有感觉不如自己动手写一个条件注解。假设我们有这样一个需求根据配置文件里的demo.mode来决定装配哪种消息发送器test环境用模拟发送器prod环境用真实发送器。我们先定义一个Condition实现类public class DemoModeCondition implements Condition { Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String mode context.getEnvironment().getProperty(demo.mode, test); MapString, Object attributes metadata.getAnnotationAttributes(com.example.condition.ConditionalOnDemoMode); if (attributes null) { return false; } String expectedMode (String) attributes.get(value); return expectedMode.equalsIgnoreCase(mode); } }然后定义注解本身Target({ElementType.TYPE, ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) Conditional(DemoModeCondition.class) public interface ConditionalOnDemoMode { String value(); }接下来在配置类里使用Configuration public class MessageSenderConfiguration { Bean ConditionalOnDemoMode(test) public MessageSender mockMessageSender() { return new MockMessageSender(); } Bean ConditionalOnDemoMode(prod) public MessageSender realMessageSender() { return new RealMessageSender(); } }这样当demo.modetest时只有mockMessageSender被注册改成prod时realMessageSender生效。虽然这个例子本身用ConditionalOnProperty也能实现但走一遍自定义Condition的过程你对Spring Boot内部条件评估的时机和方式理解会深得多。3.4 条件装配的基础BeanDefinition与注册流程讲到这里有必要稍微停下来补一点底层知识为什么Spring能“跳过”不满足条件的Bean这就要说到BeanDefinition。Spring容器不像我们想象的那样直接new出所有Bean。它首先会把每个Bean元数据注册为一个BeanDefinition这里面记录了Bean的类名、作用域、初始化方法、属性值等各种信息。等到需要实例化某个Bean时容器再根据对应的BeanDefinition反射创建实例。Conditional生效的位置就是在生成BeanDefinition之前。一个配置类如果条件不满足Spring压根不会去解析它的Bean方法也不会生成对应的BeanDefinition。这就像装修房子时如果你确定某个房间不做那设计师根本不会出这个房间的效果图而不是出了图之后才说不要。理解了这个机制你在排查“为什么某个Bean没生效”时脑子里就有了一条清晰的链路先检查依赖有没有引入再检查自动配置类的条件有没有满足最后再看是不是被用户自定义的Bean抢了位置。这三个维度基本能覆盖90%的问题。4. 常见自动配置类的核心逻辑拆解光看原理容易飘落地到具体的自动配置类上才能真正把知识串起来。这里挑三个最常用的自动配置类来拆解你会发现它们其实都遵循同一个套路类上的条件注解 配置属性绑定 条件缺失Bean创建。4.1 DataSourceAutoConfiguration数据库连接的自动配置DataSourceAutoConfiguration是所有数据库相关自动配置的基础。它类的声明大致长这样省略了一些细节AutoConfiguration(before SqlInitializationAutoConfiguration.class) ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) ConditionalOnMissingBean(type io.r2dbc.spi.ConnectionFactory) EnableConfigurationProperties(DataSourceProperties.class) Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceCheckpointRestoreConfiguration.class }) public class DataSourceAutoConfiguration { // ... }逐行拆解一下ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })类路径下必须存在javax.sql.DataSource和org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType。前者来自JDBC相关依赖后者来自spring-jdbc。如果你只引入了驱动没引入spring-boot-starter-jdbc这个条件可能就不满足。ConditionalOnMissingBean(type io.r2dbc.spi.ConnectionFactory)如果项目里已经有R2DBC的响应式连接工厂说明你走的是响应式数据库路线这个JDBC自动配置就退出了。EnableConfigurationProperties(DataSourceProperties.class)把spring.datasource开头的配置项绑定到DataSourceProperties上。DataSourceAutoConfiguration内部还嵌套了几个条件配置类其中最核心的是PooledDataSourceConfiguration它负责用HikariCP创建连接池数据源如果没有连接池可用还有一个EmbeddedDatabaseConfiguration会默认创建一个H2或HSQLDB内存数据库。这也就解释了为什么很多入门Demo里只要加上H2依赖不写任何数据源配置项目也能跑起来。4.2 RedisAutoConfiguration缓存与NoSQL的自动配置Redis的自动配置同样遵循“依赖存在 Bean缺失”的套路。核心的RedisAutoConfiguration类上有这么几个关键注解AutoConfiguration ConditionalOnClass(RedisOperations.class) EnableConfigurationProperties(RedisProperties.class) Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { // ... }ConditionalOnClass(RedisOperations.class)确保项目里引入了Spring Data Redis相关的类。EnableConfigurationProperties(RedisProperties.class)把spring.data.redis前缀的配置项绑定到属性类上注意Spring Boot 2.x是spring.redis3.x改成了spring.data.redis迁移项目时经常有人在这里踩坑。Import里引入了Lettuce和Jedis的连接配置类二选一当前默认是Lettuce。RedisAutoConfiguration内部还会创建两个关键BeanstringRedisTemplate和redisTemplate。这两个Bean方法上都标注了ConditionalOnMissingBean(name redisTemplate)之类的前置条件。意思是如果你自己在容器里定义了RedisTemplate自动配置不会覆盖你的你什么都没定义它就给你一个默认的RedisTemplate。这种“你当家我退位”的设计在自动配置里随处可见。微服务项目里常见的Redis缓存、Session共享、分布式锁其实底层用的都是这套默认Bean只是在上面做扩展。4.3 Web场景WebMvcAutoConfiguration与JacksonWeb应用是Spring Boot最典型的场景。WebMvcAutoConfiguration是一个体量很大、条件非常复杂的自动配置类它上面有一个大条件ConditionalOnMissingBean(WebMvcConfigurationSupport.class)。这个条件的意思是如果你已经手动借WebMvcConfigurationSupport做了完整定制那Spring Boot提供的Web MVC自动配置就整体退出完全交给你自己掌控。一旦触发这个条件静态资源映射、视图解析器、消息转换器等一大堆默认配置都会失效特别容易出现“风格变了”的问题。所以我的建议是除非你真的要全盘接管否则扩展Web MVC还是用WebMvcConfigurer不要继承WebMvcConfigurationSupport。再单独看Jackson的自动配置。JacksonAutoConfiguration会完成ObjectMapper的创建并帮它注册常用的Java 8时间模块、参数名称模块等。在Spring Boot 3.x里它还负责提供HttpMessageConverter的JSON转换器。项目里常见的LocalDateTime序列化格式不对问题往往不是框架没配好而是默认的格式策略不满足你这时就需要通过spring.jackson.*配置项或者自定义ObjectMapper来调整。5. 实操怎么快速查看和调试当前项目的自动配置原理讲了一大堆最终都得落地到排查问题的能力上。这一节我说点实操层面的东西保证你用得上。5.1 开启自动配置报告Positive和Negative是什么意思排查自动配置问题首先要学会看“自动配置报告”。方法很简单在application.properties文件中加一行debugtrue启动项目后控制台会输出一份详细的CONDITIONS EVALUATION REPORT条件评估报告分四大部分Positive matches匹配成功、已经生效的自动配置列表。里面会列出具体的自动配置类以及启动日志中实际记录的“匹配原因”比如“在类路径下找到了类xxxx”。Negative matches匹配失败、没有生效的自动配置列表。这里同样会给出失败原因比如“类xxxx在类路径下不存在”或者“已经存在bean xxxx”。Exclusions被手动排除的自动配置。Unconditional classes没有任何条件限制、一定会加载的自动配置类。我排查问题时的习惯是先说清楚“我的预期是什么”然后重点看Negative matches。比如我引入了一个开源工具但它的自动配置没有生效我就去Negative matches里搜它的类名原因通常一目了然——要么是某个依赖缺失要么是某个条件判断为false。有了这个报告排查效率会提升一个量级。5.2 排除不想用的自动配置类有时候某个自动配置类确实会捣乱。比如你引入了Redis依赖但想用自定义配置完全接管不想让Spring Boot的Redis自动配置介入。排除的方式大致有三种第一种在启动类上指定SpringBootApplication(exclude { RedisAutoConfiguration.class }) public class Application { // ... }第二种在配置文件中指定spring.autoconfigure.excludeorg.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration第三种如果你想排除的是某个Starter里的自动配置类但又不想一个个写可以结合AutoConfiguration.imports文件来定位。实践中的选择原则是能通过条件注解避免的冲突尽量不排除确实要排除优先在配置文件中声明方便运维环境不重新编译就调整。尤其注意排除的是自动配置类不是Starter依赖本身。很多时候你只需要排除其中一个类没必要把整个依赖去掉。5.3 实战案例多数据源项目的自动配置排错过程给你讲一个我实际踩过的坑会让你更有体感。曾经有一个项目要引入MySQL和另一个数据源我想当然地自定义了一个DataSource的Bean。结果项目启动后控制台只看到SQL日志还是连的默认库我就怀疑是自动配置跟自定义配置打架了。打开debugtrue报告在Negative matches里看到了DataSourceAutoConfiguration的失败原因ConditionalOnMissingBean (types: javax.sql.DataSource) did not find any beans。这就奇怪了我明明自己定义了DataSource为什么它还认为没有后来仔细一看我自定义的DataSource是放在一个普通的Service里通过Bean方法创建的而那个Service所在的包没有被启动类的ComponentScan覆盖到。BeanDefinition都没注册进容器ConditionalOnMissingBean当然判断它是缺失的。折腾了半天根因不是自动配置的问题而是我的配置类压根没被扫描到。把包扫描路径修正之后自动配置自动退出一切正常。这个案例告诉我们排查任何自动配置问题第一步永远先确认“条件判断的依据是什么”。条件判断的是BeanDefinition的状态而不是你自己以为的状态。启动日志里标注的“did not find any beans”是最诚实的它不会管你脑子里觉得“我已经定义了Bean”。5.4 排查自动配置问题的通用套路根据我这些年的经验遇到自动配置不生效的问题按下面的顺序排查基本不会跑偏确认依赖是否真的引入了。打开External Libraries搜一下相关类的jar包在不在。这个是最常见的原因没有之一。打开debugtrue查看条件评估报告。优先看Negative matches确认是哪个条件把你想要的自动配置挡掉了。检查自己的Bean定义是否真的被容器扫描到。包路径、ComponentScan和自动配置类上ConditionalOnMissingBean的交互是最容易出问题的地方。检查配置项是否真的生效。比如ConfigurationProperties绑定的前缀Spring Boot 2.x和3.x有些前缀改过一处不一致就会导致配置读不到。如果还不行直接在AutoConfigurationImportSelector#getCandidateConfigurations方法上打断点一步一步看它返回了哪些自动配置类。这是最底层的排查方式一般不会查不出结果。6. 自定义Starter从自动配置原理到生产级实践读懂自动配置最好的验证方式就是亲手写一个Starter。做微服务、做中间件封装、做内部公共库的时候这个技能基本是必需品。6.1 一个最小的自定义Starter结构先看一个最简的Starter工程长什么样。假设我们要做一个“DEMO短信发送”的Starterdemo-spring-boot-starter/ ├── pom.xml ├── src/main/java/com/example/demo/ │ ├── DemoMessageSender.java │ ├── DemoMessageSenderProperties.java │ └── DemoMessageSenderAutoConfiguration.java └── src/main/resources/ └── META-INF/ └── spring/ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports注意Spring Boot 3.x的项目自动配置类的声明必须写在org.springframework.boot.autoconfigure.AutoConfiguration.imports文件里每行一个全限定类名。如果你还在用spring.factories至少在3.x上已经行不通了。这是新手写Starter时最容易踩的第一个坑。6.2 配置属性类与元数据的编写接下来要写配置属性类让使用方可以通过配置文件定制行为ConfigurationProperties(prefix demo.message) public class DemoMessageSenderProperties { /** * 短信服务地址。 */ private String endpoint http://default.example.com; /** * 连接超时时间单位毫秒。 */ private Duration connectTimeout Duration.ofSeconds(3); // getter / setter 省略 }为了在IDE里拿到配置提示最好在pom.xml里引入元数据生成依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-configuration-processor/artifactId optionaltrue/optional /dependency这个依赖会在编译期生成spring-configuration-metadata.json。引入到使用方项目后写demo.message.endpoint之类的配置就有自动补全和注释提示了。这个细节很多人不重视但在公司内部推广公共Starter时体验差别真的很大。6.3 自动配置类的条件组合一个成熟的自动配置类条件通常会组合好几个维度参照官方风格来写AutoConfiguration ConditionalOnClass(DemoMessageSender.class) ConditionalOnProperty(prefix demo.message, name enabled, havingValue true, matchIfMissing true) EnableConfigurationProperties(DemoMessageSenderProperties.class) public class DemoMessageSenderAutoConfiguration { Bean ConditionalOnMissingBean public DemoMessageSender demoMessageSender(DemoMessageSenderProperties properties) { return new DemoMessageSender(properties); } }这个自动配置类表达了三个层级的控制ConditionalOnClass确保这个Starter被引入时才生效防止类路径错误。ConditionalOnProperty提供一个总开关让使用方通过demo.message.enabledfalse关闭整个模块。ConditionalOnMissingBean给使用方保留替换权只要他自定义了DemoMessageSender自动配置就自动退位。用这种三段式条件组合既给使用方极大的自由度又保证了默认开箱即用这也是官方自动配置常用的套路。6.4 Starter的打包与使用最后一步是打包和使用。需要注意几点Starter工程的pom.xml里依赖一般声明为compile这样使用方引入后传递依赖才不会被遗漏但如果有可选的实现依赖建议标成optional避免强制传导。发布到公司内部私有仓库时建议同时发布源码包和文档说明方便其他团队使用。我遇到过最无语的坑是AutoConfiguration.imports文件路径拼错或者文件名拼错导致Starter引入后完全没效果但控制台一点报错都没有。排查到最后发现是拼写问题这个只能靠细心。最后说点实在的从刚接触Spring Boot时的“黑盒使用”到后来被迫研究启动流程、条件装配再到自己写Starter这条路走下来最大的收获不是记住了哪个注解的作用而是养成了一种排查问题的思维方式看到某个Bean异常先问“它的加载入口在哪”再问“它的生效条件是什么”最后问“它被谁影响了”。这个思路放在任何框架里都通用。最后再分享一个小技巧在IDEA里启动项目后按两下Shift搜索AutoConfigurationImportSelector在getCandidateConfigurations方法里打断点跑一下你能亲眼看到自动配置类是怎么被一个个选出来的。这个调试方式比看十遍文档都有用。
返回列表