乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 Spring概述与IoC容器

    • Spring概述与IoC容器
    • Spring Framework 概述
    • IoC 与 DI 核心概念
    • @Configuration 详解
    • @Component 详解
    • @ComponentScan 详解
    • @Import 详解
    • @Profile 详解
    • @PropertySource 详解
    • @Service 详解
    • @Repository 详解
  • 第2章 Bean的定义与依赖注入

    • Bean的定义与依赖注入
    • @Bean 详解
    • @Autowired 详解
    • @Qualifier 详解
    • @Primary 详解
    • @Resource 详解
    • @Inject 详解
    • @Named 详解
    • @Value 详解
    • @Scope 详解
    • @Lazy 详解
  • 第3章 Bean生命周期与作用域

    • Bean生命周期与作用域
    • Bean生命周期概述
    • @PostConstruct
    • @PreDestroy
    • InitializingBean
    • DisposableBean
    • BeanPostProcessor
    • BeanFactoryPostProcessor
  • 第4章 AOP面向切面编程

    • AOP面向切面编程
    • AOP核心概念
    • @EnableAspectJAutoProxy
    • @Aspect
    • @Pointcut
    • @Before
    • @After
    • @AfterReturning
    • @AfterThrowing
    • @Around
  • 第5章 数据访问与事务管理

    • 数据访问与事务管理
    • 数据访问概述
    • @EnableTransactionManagement
    • @Transactional
    • @Transactional 的传播行为
    • @Transactional 的隔离级别
    • @Transactional 的回滚规则
    • @Transactional 的超时与只读属性
    • @TransactionalEventListener
  • 第6章 Spring Boot自动配置基础

    • Spring Boot自动配置基础
    • @SpringBootApplication 注解
    • @EnableAutoConfiguration 注解
    • @ConfigurationProperties 注解
    • @ConditionalOnClass 注解
    • @ConditionalOnMissingBean 注解
    • @ConditionalOnProperty 注解
  • 第7章 从容器到Web: Spring MVC导引

    • Spring MVC 导引
  • 第8章 扩展阅读

    • 扩展阅读
    • Spring 事件机制 — ApplicationEvent / ApplicationListener
    • @EventListener
    • SpEL — Spring 表达式语言
    • 校验 Validation — JSR-303 / JSR-380 Bean Validation
    • 类型转换与数据绑定 — Converter / DataBinder
  • 附录

    • Spring Framework 专业术语
    • Spring 核心知识点
    • Spring 面试高频考点
    • Spring 核心注解速查表

@Import 详解

本章定位:深入理解 Spring 的模块化配置组合机制。@Import 是实现配置类拆分与组合的核心注解,让大型项目的配置结构清晰、可维护、可复用。


定义与作用

@Import 是 Spring 提供的配置导入注解,用于在一个配置类中导入其他配置类或组件类,实现模块化配置的组合。

解决的痛点:当项目规模扩大,单个 @Configuration 类会变得臃肿,包含数据源、事务、Web、缓存等各类 Bean 定义:

// 痛点:配置类臃肿,职责混杂
@Configuration
@ComponentScan("com.feixiang.student")
@EnableTransactionManagement
public class AppConfig {

    // 数据源配置
    @Bean
    public DataSource dataSource() { ... }

    @Bean
    public JdbcTemplate jdbcTemplate() { ... }

    @Bean
    public PlatformTransactionManager transactionManager() { ... }

    // Web 配置
    @Bean
    public FilterRegistrationBean loggingFilter() { ... }

    @Bean
    public ServletRegistrationBean dispatcherServlet() { ... }

    // 缓存配置
    @Bean
    public CacheManager cacheManager() { ... }

    // 消息队列配置
    @Bean
    public ConnectionFactory jmsConnectionFactory() { ... }

    @Bean
    public JmsTemplate jmsTemplate() { ... }
}

@Import 让配置按模块拆分,每个配置类只负责一个职责领域,主配置类通过 @Import 聚合:

// 解决后:模块化配置,主配置类只负责聚合
@Configuration
@ComponentScan("com.feixiang.student")
@Import({DataSourceConfig.class, TransactionConfig.class, WebConfig.class, CacheConfig.class})
public class AppConfig {
    // 简洁清晰,只负责导入其他模块
}

适用位置与常用属性

适用位置

@Import 只能标注在类级别,通常与 @Configuration 配合使用。

@Configuration
@Import(OtherConfig.class)
public class AppConfig {
}

常用属性

属性类型默认值说明
valueClass<?>[]{}要导入的配置类数组。可以是 @Configuration 类、ImportSelector 实现类或 ImportBeanDefinitionRegistrar 实现类

核心原理:配置类导入流程

@Import 触发的导入流程与组件扫描不同,它直接注册指定的配置类,而非扫描类路径:

三种导入类型:

  1. 普通配置类:直接导入,容器会解析其 @Bean 方法和 @ComponentScan
  2. ImportSelector:实现 selectImports() 方法,根据条件动态决定导入哪些类
  3. ImportBeanDefinitionRegistrar:实现 registerBeanDefinitions() 方法,编程式注册 BeanDefinition

完整示例:飞翔科技公司的模块化配置

场景简述

飞翔科技的学生管理系统需要同时支持 Web 访问、定时任务和数据同步。架构师白歌要求将配置拆分为独立的模块:数据层、Web 层、任务调度层。主配置类通过 @Import 按需组合。

操作前:单体配置类

// 操作前:AppConfig.java —— 所有配置挤在一个类中
@Configuration
@ComponentScan("com.feixiang.student")
@EnableTransactionManagement
@EnableScheduling
public class AppConfig {

    // ========== 数据源模块 ==========
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/student_db");
        config.setUsername("root");
        config.setPassword("secret");
        return new HikariDataSource(config);
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    // ========== Web 模块 ==========
    @Bean
    public FilterRegistrationBean loggingFilter() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new RequestLoggingFilter());
        registration.addUrlPatterns("/*");
        return registration;
    }

    // ========== 定时任务模块 ==========
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(5);
        return scheduler;
    }

    @Bean
    public StudentSyncTask studentSyncTask() {
        return new StudentSyncTask();
    }
}

痛点:

  1. 配置类超过 100 行,阅读和维护困难
  2. 不同模块的 Bean 混在一起,无法快速定位
  3. 如果某个模块(如定时任务)在特定环境不需要,无法方便地排除
  4. 多个项目想复用数据源配置,只能复制粘贴

操作后:使用 @Import 模块化拆分

// 操作后:DataSourceConfig.java —— 数据源模块
@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource(
            @Value("${db.url}") String url,
            @Value("${db.username}") String username,
            @Value("${db.password}") String password) {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(url);
        config.setUsername(username);
        config.setPassword(password);
        config.setMaximumPoolSize(10);
        return new HikariDataSource(config);
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}
// 操作后:TransactionConfig.java —— 事务模块
@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
// 操作后:WebConfig.java —— Web 模块
@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean loggingFilter() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new RequestLoggingFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(1);
        return registration;
    }
}
// 操作后:SchedulingConfig.java —— 定时任务模块
@Configuration
@EnableScheduling
public class SchedulingConfig {

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(5);
        scheduler.setThreadNamePrefix("student-sync-");
        return scheduler;
    }
}
// 操作后:AppConfig.java —— 主配置类,通过 @Import 聚合
@Configuration
@ComponentScan("com.feixiang.student")
@Import({
    DataSourceConfig.class,
    TransactionConfig.class,
    WebConfig.class,
    SchedulingConfig.class
})
public class AppConfig {
    // 主配置类只负责聚合,自身不定义 Bean
}
// 操作后:StudentSyncTask.java —— 定时任务组件
@Component
public class StudentSyncTask {
    private static final Logger logger = LoggerFactory.getLogger(StudentSyncTask.class);

    @Scheduled(cron = "0 0 2 * * ?")  // 每天凌晨 2 点执行
    public void syncStudentData() {
        logger.info("[定时任务] 开始同步学生数据...");
        // 同步逻辑
        logger.info("[定时任务] 学生数据同步完成");
    }
}

运行结果及分析

容器启动日志:
[main] INFO  o.s.c.a.AnnotationConfigApplicationContext - 
    Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@6d06d69c
[main] DEBUG o.s.c.a.ConfigurationClassBeanDefinitionReader - 
    Registered bean definition for @Bean method 'com.feixiang.student.config.DataSourceConfig.dataSource'
[main] DEBUG o.s.c.a.ConfigurationClassBeanDefinitionReader - 
    Registered bean definition for @Bean method 'com.feixiang.student.config.DataSourceConfig.jdbcTemplate'
[main] DEBUG o.s.c.a.ConfigurationClassBeanDefinitionReader - 
    Registered bean definition for @Bean method 'com.feixiang.student.config.TransactionConfig.transactionManager'
[main] DEBUG o.s.c.a.ConfigurationClassBeanDefinitionReader - 
    Registered bean definition for @Bean method 'com.feixiang.student.config.WebConfig.loggingFilter'
[main] DEBUG o.s.c.a.ConfigurationClassBeanDefinitionReader - 
    Registered bean definition for @Bean method 'com.feixiang.student.config.SchedulingConfig.taskScheduler'
[main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - 
    Pre-instantiating singletons in ...
    defining beans [appConfig, dataSourceConfig, transactionConfig, webConfig, schedulingConfig,
                     dataSource, jdbcTemplate, transactionManager, loggingFilter, taskScheduler,
                     studentDao, studentService, studentSyncTask]

关键观察:

  1. 4 个被 @Import 的配置类(dataSourceConfig、transactionConfig、webConfig、schedulingConfig)都被注册为 Bean
  2. 每个配置类中的 @Bean 方法被逐一解析并注册
  3. 主配置类 AppConfig 保持简洁,只负责聚合

高级用法:ImportSelector 动态导入

当需要根据运行时条件(如环境变量、类路径存在性)决定导入哪些配置时,使用 ImportSelector:

// 动态导入选择器
public class DataSourceImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        String profile = System.getProperty("spring.profiles.active", "dev");

        if ("prod".equals(profile)) {
            return new String[]{"com.feixiang.student.config.ProdDataSourceConfig"};
        } else {
            return new String[]{"com.feixiang.student.config.DevDataSourceConfig"};
        }
    }
}
// 主配置类使用 ImportSelector
@Configuration
@Import(DataSourceImportSelector.class)
public class AppConfig {
}

高级用法:ImportBeanDefinitionRegistrar 编程式注册

当需要完全控制 BeanDefinition 的注册过程时,使用 ImportBeanDefinitionRegistrar:

// 编程式注册器
public class StudentDaoRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        // 手动创建 BeanDefinition
        RootBeanDefinition beanDefinition = new RootBeanDefinition(StudentDao.class);
        beanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON);
        beanDefinition.setLazyInit(false);

        // 注册到容器
        registry.registerBeanDefinition("studentDao", beanDefinition);
    }
}
// 主配置类使用 Registrar
@Configuration
@Import(StudentDaoRegistrar.class)
public class AppConfig {
}

易错场景与面试考点

反例一:@Import 与 @ComponentScan 重复扫描同一配置类

小崔既用 @ComponentScan 扫描了 config 包,又用 @Import 导入了同一个配置类:

// ❌ 错误:重复注册
@Configuration
@ComponentScan("com.feixiang.student")  // 扫描范围包含 config 包
@Import(DataSourceConfig.class)  // 又显式导入!
public class AppConfig {
}

问题:DataSourceConfig 被注册两次,虽然 Spring 会以后者覆盖前者,但可能导致意外的 Bean 覆盖警告。

纠正:避免扫描范围包含配置类,或统一使用 @Import 管理配置类:

// ✅ 正确:排除配置类扫描,统一用 @Import 管理
@Configuration
@ComponentScan(
    basePackages = "com.feixiang.student",
    excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
@Import(DataSourceConfig.class)
public class AppConfig {
}

反例二:@Import 导入非配置类导致 @Bean 方法不被处理

小崔 @Import 了一个普通类,发现其中的 @Bean 方法没有被注册:

// ❌ 错误:普通类上的 @Bean 方法不会被处理
public class DataSourceHelper {
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

@Configuration
@Import(DataSourceHelper.class)  // 导入的是普通类,非 @Configuration
public class AppConfig {
}

纠正:被 @Import 的类必须标注 @Configuration,否则其中的 @Bean 方法不会被 CGLIB 代理处理:

// ✅ 正确:被导入的类必须是 @Configuration
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

@Configuration
@Import(DataSourceConfig.class)
public class AppConfig {
}

反例三:循环导入

小崔在模块拆分时不小心造成了循环依赖:

// ❌ 错误:循环导入
@Configuration
@Import(TransactionConfig.class)
public class DataSourceConfig {
}

@Configuration
@Import(DataSourceConfig.class)
public class TransactionConfig {
}

启动报错:

BeanCurrentlyInCreationException: Error creating bean with name 'dataSourceConfig':
Requested bean is currently in creation: Is there an unresolvable circular reference?

纠正:消除循环导入,让配置类之间不互相依赖。如果确实需要共享 Bean,通过方法参数注入:

// ✅ 正确:配置类不互相导入,由主配置类统一聚合
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() { ... }
}

@Configuration
public class TransactionConfig {
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        // 通过方法参数注入,而非 @Import
        return new DataSourceTransactionManager(dataSource);
    }
}

@Configuration
@Import({DataSourceConfig.class, TransactionConfig.class})
public class AppConfig {
}

面试高频题

Q1:@Import 和 @ComponentScan 有什么区别?

@ComponentScan 是扫描类路径,自动发现并注册标注了 @Component 及其派生注解的类;@Import 是显式导入指定的类,直接注册到容器,不经过类路径扫描。@Import 通常用于导入 @Configuration 配置类,而 @ComponentScan 用于自动发现业务组件。

Q2:@Import 可以导入哪些类型的类?

三种类型:① 普通 @Configuration 配置类;② 实现 ImportSelector 接口的类,用于动态决定导入哪些配置;③ 实现 ImportBeanDefinitionRegistrar 接口的类,用于编程式注册 BeanDefinition。后两者是 Spring Boot 自动配置的核心机制。

Q3:@Import 的配置类中的 @Bean 方法互相调用,能保证单例吗?

能。被 @Import 的 @Configuration 类同样会被 CGLIB 代理,@Bean 方法调用会被拦截,确保返回容器中的单例实例。这与直接在主配置类中定义 @Bean 方法的语义完全一致。

Q4:Spring Boot 的 @EnableAutoConfiguration 底层用了什么机制?

@EnableAutoConfiguration 底层使用了 @Import(AutoConfigurationImportSelector.class)。AutoConfigurationImportSelector 实现 ImportSelector 接口,从 META-INF/spring.factories 中读取自动配置类列表,根据条件注解(如 @ConditionalOnClass)筛选后动态导入。这是 @Import 机制最典型的应用。

上一页
@ComponentScan 详解
下一页
@Profile 详解