@Configuration 详解:Spring 配置类的核心注解
@Configuration 是 Spring Framework 提供的注解,用于标记一个类为配置类 。它告诉 Spring 容器:这个类中包含了一个或多个 @Bean 定义方法,这些方法返回的对象需要被注册为 Spring 容器中的 Bean。
一、基本用法
java
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
ds.setUsername("root");
ds.setPassword("123456");
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
在 Spring Boot 启动类中,@SpringBootApplication 已经包含了 @Configuration,所以启动类本身就是一个配置类。你可以在启动类中直接添加 @Bean 方法。
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
二、@Configuration 的本质
@Configuration 类在 Spring 容器中被 ConfigurationClassPostProcessor(一个 BeanFactoryPostProcessor)处理。这个处理器会解析 @Configuration 类,提取所有 @Bean 方法,并将它们转换为 Spring 容器中的 Bean 定义。与其他 @Component 类的处理方式不同,@Configuration 类中的 @Bean 方法会被 CGLIB 代理,保证方法之间互相调用时返回的是同一个 Bean 实例。
@Configuration vs @Component
@Component 标注的类也可以通过 @Bean 方法定义 Bean,但 @Configuration 和 @Component 在处理 @Bean 方法时有本质区别:
| 对比维度 | @Configuration | @Component |
|---|---|---|
| 代理方式 | CGLIB 代理 | 不代理 |
| 方法间调用 | 返回容器中的单例 Bean | 每次调用都执行方法体,创建新实例 |
| Bean 定义模式 | Full 模式 | Lite 模式 |
| 适用场景 | 需要方法间调用保证单例 | 不需要方法间调用 |
示例说明:
java
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
// userService 内部调用 userDao() 方法
// 由于 CGLIB 代理,userDao() 返回的是容器中已有的单例 Bean
return new UserService(userDao());
}
@Bean
public UserDao userDao() {
return new UserDao();
}
}
如果这个类用 @Component 而不是 @Configuration,userService() 中调用 userDao() 时不会经过 Spring 容器,会直接执行方法体,每次都创建一个新的 UserDao 实例,破坏单例性。
三、@Bean 方法详解
@Bean 方法的作用是将方法返回值注册为 Spring 容器中的 Bean。
java
@Configuration
public class AppConfig {
// Bean 名称默认为方法名 dataSource
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
// 自定义 Bean 名称
@Bean("myDataSource")
public DataSource dataSource2() {
return new HikariDataSource();
}
}
方法参数注入
当 @Bean 方法有参数时,Spring 会自动从容器中查找匹配的 Bean 并注入:
java
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
Spring 会从容器中查找 DataSource 类型的 Bean 作为参数传入。
指定 Bean 的作用域
java
@Bean
@Scope("prototype")
public MyBean myBean() {
return new MyBean();
}
初始化与销毁回调
java
@Bean(initMethod = "init", destroyMethod = "cleanup")
public MyBean myBean() {
return new MyBean();
}
四、与其他注解的配合
@ComponentScan
@Configuration 类通常与 @ComponentScan 配合使用,指定 Bean 的扫描路径。@SpringBootApplication 已经包含了 @ComponentScan,所以不需要单独添加。
java
@Configuration
@ComponentScan("com.example.service")
public class AppConfig {
}
@PropertySource
在 @Configuration 类上使用 @PropertySource 加载自定义配置文件:
java
@Configuration
@PropertySource("classpath:custom.properties")
public class AppConfig {
@Value("${custom.value}")
private String customValue;
}
@Import
在 @Configuration 类上使用 @Import 导入其他配置类:
java
@Configuration
@Import({DatabaseConfig.class, SecurityConfig.class})
public class AppConfig {
}
@Profile
在 @Configuration 类或 @Bean 方法上使用 @Profile,控制配置在特定 Profile 下生效:
java
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataSource dataSource() {
// 开发环境数据源
}
}
@Configuration
@Profile("prod")
public class ProdConfig {
@Bean
public DataSource dataSource() {
// 生产环境数据源
}
}
五、@Configuration 在 Spring Boot 中的位置
在 Spring Boot 项目中,@Configuration 类通常放在以下位置:
启动类
java
@SpringBootApplication
public class Application {
// 启动类本身就是配置类
}
独立的配置类
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
// 覆盖 Spring MVC 默认配置
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor());
}
}
java
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
}
自动配置类
Spring Boot 的自动配置类本质上也是 @Configuration 类,但会加上条件注解控制生效时机:
java
@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.datasource.type")
public DataSource dataSource() {
// ...
}
}
六、@Configuration 与 Spring Boot 自动配置
Spring Boot 通过 @EnableAutoConfiguration 加载自动配置类。这些自动配置类就是 @Configuration 类,加上条件注解后决定何时生效。
自动配置的加载机制:
@EnableAutoConfiguration通过AutoConfigurationImportSelector从META-INF/spring.factories(Spring Boot 2.7 之前)或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 2.7 之后)读取自动配置类列表。- 每个自动配置类都是
@Configuration类,带有@ConditionalOnXxx条件注解。 - 条件满足时,配置类中的
@Bean方法被执行,对应的 Bean 注册到容器中。
条件注解:
| 注解 | 条件 |
|---|---|
@ConditionalOnClass |
classpath 中存在指定类 |
@ConditionalOnMissingClass |
classpath 中不存在指定类 |
@ConditionalOnBean |
容器中存在指定 Bean |
@ConditionalOnMissingBean |
容器中不存在指定 Bean |
@ConditionalOnProperty |
配置属性满足条件 |
@ConditionalOnWebApplication |
是 Web 应用 |
七、常见问题
问题:@Bean 方法在 @Configuration 和 @Component 中的区别
这是最容易混淆的地方。在 @Component 类中定义 @Bean 方法,方法间相互调用时不经过 Spring 容器,不会保持单例。@Configuration 类中的 @Bean 方法会被 CGLIB 代理,保证单例性。
问题:@Configuration 类是否需要 @ComponentScan?
@Configuration 本身不带 @ComponentScan,但如果类上同时有 @SpringBootApplication,其中包含 @ComponentScan,默认会扫描启动类所在包及其子包。
问题:@Configuration 类是否需要放在指定位置?
@Configuration 类需要被 @ComponentScan 扫描到,或被 @Import 导入,或通过 @EnableConfigurationProperties 注册。@SpringBootApplication 的默认扫描范围是启动类所在包及所有子包。
八、最佳实践
- 将
@Configuration类集中在专门的包中(如config),便于管理 - 不同的功能模块分开配置,比如
DataSourceConfig、RedisConfig、WebConfig分别负责不同功能 - 通过
@Profile区分不同环境的配置,避免在代码中使用环境判断 - 利用
@ConditionalOnMissingBean提供默认实现,允许用户通过自定义 Bean 覆盖默认配置 - 配置类中使用
@Bean方法注入依赖时,通过方法参数注入,而不是在方法内部调用其他@Bean方法
九、总结
@Configuration 是 Spring 配置体系的基石。它定义了配置类,通过 @Bean 方法向容器注册 Bean。与 @Component 的区别在于,@Configuration 类中的 @Bean 方法会被 CGLIB 代理,确保方法间调用时返回容器中的单例 Bean。在 Spring Boot 中,@SpringBootApplication 本身就包含了 @Configuration,所以启动类本身就是一个配置类。自动配置的核心也是基于 @Configuration 配合条件注解实现的。
理解 @Configuration 的本质,就理解了 Spring 容器是如何从配置类中提取 Bean 定义的。在 Spring Boot 中,@Configuration 是管理 Bean 定义、覆盖默认配置、定制应用行为的主要入口。