如果你用过 Spring Boot,一定对 “引入依赖就能用” 的体验印象深刻 —— 加个spring-boot-starter-web就有了 Web 环境,这个是 SpringBoot 的自动装配(Auto-Configuration)机制。
自动装配的核心注解
自动装配的逻辑看似复杂,其实核心流程只有 3 步:触发自动装配 → 加载配置类 → 条件筛选组件。
1. 第一步:触发自动装配 ——@SpringBootApplication 注解。
Spring Boot 应用的启动类上,都会有一个@SpringBootApplication注解,比如:
@SpringBootApplication
public class SpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoApplication.class, args);}
}
这个注解是 “组合注解”,它的核心功能来自其中的 **@EnableAutoConfiguration**注解,触发了自动装配的整个流程。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 1. 开启自动配置(核心)
@EnableAutoConfiguration
// 2. 扫描当前包及子包的Bean(如@Controller、@Service)
@ComponentScan(excludeFilters = { ... })
// 3. 允许在启动类上定义Bean(如@Bean)
@SpringBootConfiguration
public @interface SpringBootApplication {// 省略属性...
}
其中,@EnableAutoConfiguration 是自动装配的 “总开关”,它又依赖两个关键机制:
- @Import(AutoConfigurationImportSelector.class):导入一个 “配置选择器”,负责加载自动配置类;
- SpringFactoriesLoader:Spring 的一个工具类,负责从指定文件中加载配置类列表。
@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 {};
}
2. 第二步:加载配置类 —— 从 META-INF/spring.factories 中加载配置
AutoConfigurationImportSelector的核心作用,是通过SpringFactoriesLoader,加载classpath 下所有 META-INF/spring.factories 文件中配置的 “自动配置类”。
(1)什么是 spring.factories?
这是一个键值对格式的配置文件,Spring Boot 的核心 starter(如mybatis-pus)中就自带这个文件,路径在:mybatis-plus-boot-starter\3.5.2\mybatis-plus-boot-starter-3.5.2.jar!\META-INF\spring.factories:
(2)AutoConfigurationImportSelector 的工作流程
当应用启动时,AutoConfigurationImportSelector会执行以下操作:
1、调用selectImports()方法,触发配置类加载;
// 方法作用:选择需要导入的自动配置类,返回类的全限定名数组
public String[] selectImports(AnnotationMetadata annotationMetadata) {// 第一步:判断自动装配是否启用// isEnabled方法会检查@EnableAutoConfiguration注解的启用状态if (!this.isEnabled(annotationMetadata)) {// 如果未启用,返回空数组(NO_IMPORTS是Spring定义的空数组常量)return NO_IMPORTS;} else {// 第二步:获取自动配置条目(核心逻辑)// 该方法会加载、筛选并确定最终需要生效的配置类AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
}
2、调用getAutoConfigurationEntry()方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} // 获取 @EnableAutoConfiguration 注解的属性(如 exclude、excludeName 等)AnnotationAttributes attributes = this.getAttributes(annotationMetadata);// 加载所有候选自动配置类(数据源:META-INF/spring.factories)// 调用我们之前解析过的 getCandidateConfigurations 方法,获取原始候选配置类列表List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);configurations = this.removeDuplicates(configurations);
spring.autoconfigure.excludeSet<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);
@ConditionalOnClass、@ConditionalOnBean)configurations = this.getConfigurationClassFilter().filter(configurations);this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);
}
3、调用getCandidateConfigurations()方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {// 第一个参数:指定加载的工厂类类型(这里是EnableAutoConfiguration.class)// 第二个参数:类加载器(用于读取类路径下的资源)List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), // 返回EnableAutoConfiguration.classthis.getBeanClassLoader() // 获取当前的类加载器);// 如果为空,抛出异常提示(通常是因为找不到META-INF/spring.factories文件)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;
}protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;
}
3. 第三步:条件筛选@Conditiona
加载到自动配置类后,Spring Boot 并不是无条件地启用所有配置,而是通过条件注解进行筛选。
常用的条件注解有:
@ConditionalOnClass
:当类路径中存在指定类时生效@ConditionalOnMissingClass
:当类路径中不存在指定类时生效@ConditionalOnBean
:当容器中存在指定 Bean 时生效@ConditionalOnMissingBean
:当容器中不存在指定 Bean 时生效@ConditionalOnProperty
:当指定的属性有特定值时生效@ConditionalOnWebApplication
:当应用是 Web 应用时生效
例如:
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {// 配置内容...
}
自定义自动配置
1、定义我们需要装配的类
结构:
DataSourceAutoconfiguration类
@Configuration
@EnableConfigurationProperties(DataSourcePropertie.class) // 批量注入
public class DataSourceAutoconfiguration {@Bean()public DataSource dataSource(DataSourcePropertie dataSourcePropertie) {try {ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();comboPooledDataSource.setJdbcUrl(dataSourcePropertie.getJdbcUrl());comboPooledDataSource.setDriverClass(dataSourcePropertie.getDriverName());comboPooledDataSource.setUser(dataSourcePropertie.getUsernames());comboPooledDataSource.setPassword(dataSourcePropertie.getPasswords());return comboPooledDataSource;} catch (PropertyVetoException e) {throw new RuntimeException(e);}}
}
DataSourcePropertie
@ConfigurationProperties(prefix = "datasouce")// 注入前缀为datasouce
public class DataSourcePropertie {private String jdbcUrl = "jdbc:mysql://localhost:3306/summer_practice";private String driverName = "com.mysql.cj.jdbc.Driver";private String usernames = "root1";private String passwords = "1234561";public String getJdbcUrl() {return jdbcUrl;}public void setJdbcUrl(String jdbcUrl) {this.jdbcUrl = jdbcUrl;}public String getDriverName() {return driverName;}public void setDriverName(String driverName) {this.driverName = driverName;}public String getUsernames() {return usernames;}public void setUsernames(String usernames) {this.usernames = usernames;}public String getPasswords() {return passwords;}public void setPasswords(String passwords) {this.passwords = passwords;}
}
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.ape.redisspringbootautoconfigure.DataSourceAutoconfiguration
pom.xml
</properties><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency><!--c3p0--><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
2、配置咋们的启动类(我们只需要在pom.xml中导入需要装配类的坐标)
结构:
pom.xml
<dependency><groupId>com.ape</groupId><artifactId>redis-spring-boot-autoconfigure</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
3、在启动类中就可以拿到我们自定义的配置类
@SpringBootApplication
public class SpringbootStarter04Application {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootStarter04Application.class, args);DataSource bean = context.getBean(DataSource.class);System.out.println(bean);}}
4、配置文件(可选)
由于我们在需要装配的类中添@EnableConfigurationProperties(DataSourcePropertie.class)该注解,所以我们可以在启动类中配置yml文件来替换需要装配的类的数据,从而实现动态的改变其中的数据。
yml
datasouce:usernames: rootpasswords: 123456
总结
Spring Boot 的自动装配机制通过@EnableAutoConfiguration
注解、SpringFactoriesLoader
加载机制以及条件注解,实现了基于约定的自动配置,极大地简化了 Spring 应用的开发。允许我们通过自定义配置来覆盖默认行为,可以实现个性化定制。