# 第06天:Spring Boot自动配置原理 ## 📚 今日目标 1. 理解Spring Boot自动配置的原理 2. 理解@Configuration和@Bean 3. 理解条件注解 4. 创建spring.factories文件 --- ## 🎯 知识点1:Spring Boot自动配置原理 ### 为什么需要自动配置? **问题**:每次使用框架都要手动配置很多Bean **解决方案**:Spring Boot自动配置 - 检测classpath中的类 - 自动创建Bean - 开箱即用 ### 自动配置的流程 ``` 1. Spring Boot启动 ↓ 2. 读取META-INF/spring.factories ↓ 3. 加载自动配置类 ↓ 4. 根据条件注解判断是否生效 ↓ 5. 创建Bean ``` --- ## 🎯 知识点2:@Configuration和@Bean ### @Configuration:配置类 ```java @Configuration public class MyConfig { @Bean public MyService myService() { return new MyService(); } } ``` **作用**:标识这是一个配置类,Spring会扫描其中的@Bean方法 ### @Bean:创建Bean ```java @Bean public DataSource dataSource() { return new HikariDataSource(); } ``` **作用**:方法返回的对象会被注册为Spring Bean --- ## 🎯 知识点3:条件注解 ### @ConditionalOnClass:类存在时生效 ```java @ConditionalOnClass(DataSource.class) public class DataSourceAutoConfig { // 只有当classpath中存在DataSource类时才生效 } ``` ### @ConditionalOnMissingBean:Bean不存在时生效 ```java @Bean @ConditionalOnMissingBean public MyService myService() { // 只有当容器中不存在MyService Bean时才创建 return new MyService(); } ``` ### @ConditionalOnProperty:属性存在时生效 ```java @ConditionalOnProperty(prefix = "db-router", name = "enabled", havingValue = "true") public class DBRouterAutoConfig { // 只有当db-router.enabled=true时才生效 } ``` --- ## 🛠️ 实践任务:创建自动配置类 ### 步骤1:创建DataSourceAutoConfig 在 `src/main/java/cn/bugstack/middleware/db/router/config/` 目录下创建 `DataSourceAutoConfig.java`: ```java package cn.bugstack.middleware.db.router.config; import cn.bugstack.middleware.db.router.DBRouterJoinPoint; import cn.bugstack.middleware.db.router.DBRouterConfig; import cn.bugstack.middleware.db.router.dynamic.DynamicDataSource; import cn.bugstack.middleware.db.router.dynamic.DynamicMybatisPlugin; import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy; import cn.bugstack.middleware.db.router.strategy.impl.DBRouterStrategyHashCode; import cn.bugstack.middleware.db.router.util.PropertyUtil; import org.apache.ibatis.plugin.Interceptor; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.support.TransactionTemplate; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** * 数据源自动配置类 * * @author 小傅哥 */ @Configuration @ConditionalOnClass({DataSource.class, org.apache.ibatis.session.SqlSessionFactory.class}) @EnableConfigurationProperties(DBRouterConfig.class) public class DataSourceAutoConfig implements EnvironmentAware { private static final String TAG_GLOBAL = "global"; private static final String TAG_POOL = "pool"; private Map> dataSourceMap = new HashMap<>(); private Map defaultDataSourceConfig; private int dbCount; private int tbCount; private String routerKey; /** * 创建路由配置 */ @Bean public DBRouterConfig dbRouterConfig() { DBRouterConfig config = new DBRouterConfig(); config.setDbCount(dbCount); config.setTbCount(tbCount); config.setRouterKey(routerKey); return config; } /** * 创建路由策略 */ @Bean @ConditionalOnMissingBean public IDBRouterStrategy dbRouterStrategy(DBRouterConfig dbRouterConfig) { return new DBRouterStrategyHashCode(dbRouterConfig); } /** * 创建AOP切面 */ @Bean @ConditionalOnMissingBean public DBRouterJoinPoint point(DBRouterConfig dbRouterConfig, IDBRouterStrategy dbRouterStrategy) { return new DBRouterJoinPoint(dbRouterConfig, dbRouterStrategy); } /** * 创建MyBatis插件 */ @Bean @ConditionalOnMissingBean public Interceptor plugin() { return new DynamicMybatisPlugin(); } /** * 创建动态数据源 */ @Bean @ConditionalOnMissingBean public DataSource createDataSource() { // 创建多个数据源 Map targetDataSources = new HashMap<>(); for (int i = 1; i <= dbCount; i++) { String dbKey = String.format("db%02d", i); DataSource dataSource = createDataSource(dataSourceMap.get(dbKey)); targetDataSources.put(dbKey, dataSource); } // 创建动态数据源 DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(createDataSource(defaultDataSourceConfig)); return dynamicDataSource; } /** * 创建单个数据源 */ private DataSource createDataSource(Map config) { // 这里简化处理,实际应该根据配置创建数据源 // 可以使用HikariCP、Druid等连接池 // 为了简化,这里返回null,实际项目中需要实现 return null; } /** * 创建事务模板 */ @Bean public TransactionTemplate transactionTemplate(DataSource dataSource) { return new TransactionTemplate(new DataSourceTransactionManager(dataSource)); } @Override public void setEnvironment(Environment environment) { // 从环境变量中读取配置 String prefix = "router.jdbc.datasource."; // 读取数据库数量 dbCount = Integer.parseInt(environment.getProperty(prefix + "dbCount", "2")); tbCount = Integer.parseInt(environment.getProperty(prefix + "tbCount", "4")); routerKey = environment.getProperty(prefix + "routerKey", "userId"); // 读取数据源配置 // 这里简化处理,实际应该读取完整配置 } } ``` ### 步骤2:创建spring.factories文件 在 `src/main/resources/META-INF/` 目录下创建 `spring.factories`: ``` org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.bugstack.middleware.db.router.config.DataSourceAutoConfig ``` **说明**: - `EnableAutoConfiguration`:自动配置的key - 后面是自动配置类的全限定名 --- ## 🎓 知识点拓展 ### 拓展1:自动配置的优先级 **用户配置 > 自动配置** ```java // 用户自定义的Bean会覆盖自动配置的Bean @Bean public IDBRouterStrategy dbRouterStrategy() { return new MyCustomStrategy(); // 会覆盖自动配置的 } ``` ### 拓展2:条件注解的组合 ```java @ConditionalOnClass(DataSource.class) @ConditionalOnProperty(prefix = "db-router", name = "enabled", havingValue = "true") public class DBRouterAutoConfig { // 同时满足两个条件才生效 } ``` ### 拓展3:@EnableConfigurationProperties ```java @EnableConfigurationProperties(DBRouterConfig.class) ``` **作用**: - 启用配置属性绑定 - 将application.yml中的配置绑定到DBRouterConfig对象 - 自动注册DBRouterConfig为Bean --- ## ✅ 今日检查清单 - [ ] 理解了Spring Boot自动配置的原理 - [ ] 理解了@Configuration和@Bean - [ ] 理解了条件注解 - [ ] 创建了DataSourceAutoConfig配置类 - [ ] 创建了spring.factories文件 - [ ] 完成了拓展阅读 --- ## 🎯 明日预告 明天我们将学习: - MyBatis插件机制 - 如何拦截SQL执行 - 如何修改SQL语句 --- ## 💡 思考题 1. 为什么需要spring.factories文件? 2. @ConditionalOnMissingBean的作用是什么? 3. 如何让用户能够覆盖自动配置的Bean? --- ## 📚 参考资源 - [Spring Boot自动配置](https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.auto-configuration) - [条件注解](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.condition-annotations) - [创建自定义Starter](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration)