Spring Boot 项目里,注册 Bean 最常用的是 @Component 和 @Bean,但碰到第三方框架的类、或者想按条件批量注册一组配置时,这两个注解就不够用了。@Import 注解专门干这件事,它有三种用法,覆盖了从单个类到整个配置体系的所有场景。
很多人对 @Import 的印象停留在「在 @SpringBootApplication 上引一下配置类」,其实它的三种形态对应的是三套完全不同的注册机制,用错场景代码就会变得很别扭。
最简单的用法:@Import 后面跟一个普通类,这个类会被当作 Bean 直接注册进容器,不需要类上有任何 Spring 注解。典型场景是引入第三方 jar 里的工具类,你又改不了源码。
这种用法的底层原理是 ConfigurationClassParser 把被导入的类当作普通 @Bean 的来源处理,注册时走的 BeanDefinition 注册流程和 @Component 扫描到的类一致。
// 第三方工具类,无法加注解
public class SmsClient {
public void send(String msg) { /* ... */ }
}
// 配置类直接导入
@Configuration
@Import(SmsClient.class)
public class SmsConfig {
}⚠️ 注意:直接导入的类走的是无参构造,如果你的第三方类需要传参,得用下面第二种写法配合 @Bean。
@Import 后面跟一个标了 @Configuration 的类,会把那个配置类里所有 @Bean 方法全部注册进来。这是做模块化配置最常用的方式,把不同业务的配置拆到独立类里,再用一个总配置 @Import 统一组装。
和 @ComponentScan 相比,@Import 指定的是具体类,不会误扫到不需要的 Bean。在 starter 开发里,几乎都是用 @Import 而不是包扫描,因为 starter 不能假设宿主项目的包结构。
// 模块A的配置
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate() {
return new RedisTemplate<>();
}
}
// 模块B的配置
@Configuration
public class MqConfig {
@Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate();
}
}
// 总配置:一处组装所有模块
@Configuration
@Import({RedisConfig.class, MqConfig.class})
public class AppConfig {
} 这种写法的好处是,启动入口只需扫 AppConfig 一个类,模块间的依赖关系在总配置里一眼可见。
ImportSelector 是个接口,实现它的 selectImports 方法返回一组类名字符串,Spring 会把这些类全部注册。这给了你在运行时根据条件决定注册哪些 Bean 的能力,@EnableAsync、@EnableCaching 底层全是这套机制。
Spring Boot 自动装配的 @EnableAutoConfiguration 本质就是通过
AutoConfigurationImportSelector 读取 META-INF/spring.factories,把里面列出的配置类全部 @Import 进来。理解了 ImportSelector,就理解了自动装配的骨架。
// 自定义选择器
public class ModeSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata m) {
// 根据环境变量决定注册哪个
if ("prod".equals(System.getenv("env"))) {
return new String[]{ProdDataSource.class.getName()};
}
return new String[]{DevDataSource.class.getName()};
}
}
// 用 @Import 接入选择器
@Configuration
@Import(ModeSelector.class)
public class DataSourceConfig {
}ImportSelector 让 Bean 的注册从静态声明变成了运行时决策,这是 Spring 扩展体系里最有价值的机制之一。
⚠️ 注意:selectImports 返回的类名必须是全限定名,拼写错了不会编译报错,只会在启动时找不到类抛 ClassNotFoundException,排查时容易忽略。
@Import 三种形态各有定位:直接导入类用于注册改不了的第三方类,导入配置类用于模块化组装,ImportSelector 用于运行时按条件动态注册。三者底层都走 BeanDefinition 注册,但触发时机和灵活度完全不同。
写自定义 starter 或者做条件化装配时,优先考虑 ImportSelector,它比写一堆 @Conditional 散落在各个配置类里要干净得多。
更新时间:2026-07-15
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight All Rights Reserved.
Powered By 61893.com 闽ICP备11008920号
闽公网安备35020302035593号