SpringBoot10-自动配置原理
一、自动配置
在传统 Spring时代:
你要用数据库,得自己写一堆配置:
DataSource、JdbcTemplate、事务管理器……要用 MVC,也得自己配置视图解析器、消息转换器……
每个常用组件都要写大量
@Bean或 XML,麻烦又容易出错。
Spring Boot 的目标:
自动帮你把常用组件配好,只要你在项目里引入对应的起步依赖(starter),就几乎可以开箱即用,起步依赖中的bean对象会自动注入到IOC容器中。
我们现在注入第三方bean的方式:
不是自动配置。
二、自动配置-源码分析
2-1、@SpringBootApplication
你平常写的启动类就是这样标注的:
@SpringBootApplication public class DemoApp { … }它其实是一个组合注解,里面包含:
@SpringBootConfiguration(相当于@Configuration)@ComponentScan(包扫描)@EnableAutoConfiguration← 自动配置的关键
2-2、@EnableAutoConfiguration
打开自动配置开关。
内部用了
@Import(AutoConfigurationImportSelector.class)这句的意思:交给
AutoConfigurationImportSelector来决定要导入哪些配置类。
2-3、AutoConfigurationImportSelector
它实现了
ImportSelector接口,里面有一个selectImports()方法:String[] selectImports(…)这个方法会去扫描
META-INF/spring.factories或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports这些文件在
spring-boot-autoconfigure这个 jar 包里。里面列出了大量自动配置类,例如:
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration …
2-4、自动配置类
每个自动配置类本质上就是一个普通的@Configuration,例如:
@AutoConfiguration @ConditionalOnClass(DispatcherServlet.class) public class DispatcherServletAutoConfiguration { @Bean public DispatcherServlet dispatcherServlet() { return new DispatcherServlet(); } }它们一般都加了各种条件注解 @Conditional:
@ConditionalOnClass:classpath 有这个类才加载@ConditionalOnMissingBean:容器里没同类 Bean 才创建默认的@ConditionalOnProperty:看配置文件开关
2-5、流程串起来
应用启动→ 执行
@SpringBootApplication@EnableAutoConfiguration生效 → 调用AutoConfigurationImportSelector.selectImports()读取
spring.factories/.imports文件 → 找到所有自动配置类按条件注解筛选(有类、有依赖、没被用户覆盖、配置开关打开…)
注册满足条件的 Bean(如 DispatcherServlet、数据源、MVC 相关组件…)
2-6、通俗总结
Spring Boot 的自动配置就是:
启动时去jar 包里的配置文件里找所有可能的配置类;
然后根据条件注解判断要不要加载;
满足条件的就自动注册 Bean(如 Web 容器、数据源、MVC 组件…)。
你不用手写样板配置,Boot 帮你搞定;如果不想用或要自定义,直接写自己的 Bean 或关掉开关即可。
三、自己封装一个Spring Boot Starter的基本套路
这张图演示的是把第三方 jar(common-pojo)里的类做成 Spring Boot 自动配置,然后在业务项目里“零配置”使用的完整流程。可以按步骤看:
①CommonConfig—— 普通的配置类
@Configuration public class CommonConfig { @ConditionalOnProperty(prefix = "country", name = {"name","system"}) @Bean public Country country(@Value("${country.name}") String name, @Value("${country.system}") String system) { return new Country(name, system); } @Bean public Province province() { return new Province(); } }作用:把
Country、Province这两个第三方类注册成 Spring Bean。@ConditionalOnProperty:只有当配置文件里有country.name和country.system时才创建Country。@Bean:返回实例交给 IoC 容器管理。
如果只是单独项目使用,可以直接
@Import(CommonConfig.class);
但如果想封装成starter,还需要后面的步骤。
②CommonAutoConfig—— 自动配置类
@AutoConfiguration @Import(CommonConfig.class) public class CommonAutoConfig {}用
@AutoConfiguration(或老版本的@Configuration+ 条件注解)声明这是一个自动配置类。@Import(CommonConfig.class):把真正声明 Bean 的CommonConfig引入。
关键点:用户项目不用自己
@Import(CommonConfig.class),而是通过自动配置机制自动加载。
③/④ 注册到spring.factories或.imports
在common-pojo-1.0-SNAPSHOT.jar的resources/META-INF/spring/下:
老版本(Spring Boot 2.x):
spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.itcast.config.CommonAutoConfig新版本(3.x 推荐):
org.springframework.boot.autoconfigure.AutoConfiguration.imports cn.itcast.config.CommonAutoConfig
作用:告诉 Spring Boot:“当有人引入我的 jar 时,把
CommonAutoConfig作为自动配置类。”
业务项目(右边)
@SpringBootApplication public class SpringbootRegistApplication { }用户项目什么都不用写,只要:
引入了
common-pojo这个依赖,并在
application.yml配好country.name、country.system。
Spring Boot 启动时会:
扫描到 jar 包里的
AutoConfiguration.imports。把
CommonAutoConfig加入。CommonAutoConfig又@Import了CommonConfig。满足条件注解后,自动创建
Country、ProvinceBean。
🔥 总结流程
写一个配置类
CommonConfig,里面@Bean注册需要的对象,配合条件注解。写一个自动配置入口
CommonAutoConfig,@AutoConfiguration+@Import(CommonConfig)。在 jar 包的
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
写入CommonAutoConfig的全类名。用户只要引入这个 jar + 在配置文件里写参数,Spring Boot 会自动帮他装配好 Bean,
无需在业务代码手动@Import或@Bean。
这就是自己封装一个Spring Boot Starter的基本套路。
①②写配置类
③④注册到
spring.factories/.imports业务项目直接用
@SpringBootApplication,即“开箱即用”。
项目简单,只有一个配置类 ↓ 直接 @AutoConfiguration + imports 文件 ↓ 不需要 @Import,不需要第二层包装 项目复杂,配置要拆成多个类 ↓ 写一个 @AutoConfiguration 主入口 ↓ 主入口用 @Import 导入多个 @Configuration 子配置 ↓ imports 文件里只写主入口约定俗成的分工是:
@AutoConfiguration→ 只给真正的"自动配置入口"用,必须出现在imports文件里@Configuration→给被@Import导入的辅助配置类用
示例:拆分成多个类(复杂场景)
当你的 starter 要配置的东西很多(数据源、缓存、Web、安全……),全写在一个类里会很长。这时候需要拆分。
但imports文件里如果写一堆类,会变得很乱:
// 这样写太散了,不好维护 com.example.CommonDataSourceConfig com.example.CommonCacheConfig com.example.CommonWebConfig com.example.CommonSecurityConfig所以常见的做法是:
Step 1:写一个"主入口"类,只负责组装
@AutoConfiguration @AutoConfigureAfter(DataSourceAutoConfiguration.class) @Import({CommonDataSourceConfig.class, CommonCacheConfig.class, CommonWebConfig.class}) public class CommonAutoConfiguration { // 这个类可以什么都不做,就是个"总开关" }Step 2:把具体配置拆成普通@Configuration类
@Configuration(proxyBeanMethods = false) public class CommonDataSourceConfig { @Bean public DataSource commonDataSource() { ... } } @Configuration(proxyBeanMethods = false) public class CommonCacheConfig { @Bean public CacheManager commonCacheManager() { ... } }Step 3:imports文件里只写主入口
com.example.autoconfigure.CommonAutoConfiguration四、小结
五、自定义starter
示例:
步骤一:快速创建两个mavne工程
步骤二:配置dmybatis-spring-boot-atutoconfigure工程的pom.xml文件,添加依赖。
仿照以上的依赖,进行添加。
步骤三:编写自动配置类
// 标识当前类是一个自动配置类 @AutoConfiguration public class MybatisAutoConfig { // 注入SqlSessionFactoryBean @Bean public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } // 注入MapperScannerConfigure @Bean public MapperScannerConfigurer mapperScannerConfigurer(BeanFactory beanFactory){ MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); // 扫描的包:启动类所在的包,及其子包 List<String> packages = AutoConfigurationPackages.get(beanFactory); String p = packages.get(0); mapperScannerConfigurer.setBasePackage(p); // 扫描的注解 mapperScannerConfigurer.setAnnotationClass(Mapper.class); return mapperScannerConfigurer; } }步骤四:声明自动配置类的全类名
目的:让这个自定义的自动配置类被springboot读取到。
4-1、创建.import配置文件
配置文件名可以直接从添加的依赖中获取:
在dmybatis-spring-boot-atutoconfigure工程中创建相同路径的文件。
4-2、在配置文件中添加自动配置类的全类名
步骤五:dmybatis-spring-boot-starter模块,添加依赖管理
<!-- 引入自动配置模块对应的依赖 --> <dependency> <groupId>org.example</groupId> <artifactId>dmybatis-spring-boot-autoconfigure</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- 将自动配置模块中的所有依赖也引入 --> <!-- 目的:方便以后排除,添加新的版本的依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <version>3.1.2</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.19</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.4</version> </dependency>步骤六:删除多余的pakages
步骤七:将项目中的mybatis-spring-boot-starter替换为自定义的starter
其余代码均不变!
