简单来说:
配置类是一个专门用于告诉 Spring:“我要哪些对象成为 Bean,被 Spring 管理”的类。
它的作用主要是:配置和注册 Bean 到 Spring 容器中,也就是你说的“管理 Bean”。
方式 | 举例 | 本质 |
---|---|---|
注解扫描方式 | @Component / @Service / @Controller |
自动注册到容器 |
Java 配置类方式 | @Configuration + @Bean |
手动注册到容器,完全可控 |
@Bean
管理吗?❌ 不是必须的!具体视情况而定:
如果你的类是 常规组件类,比如:
业务类(@Service
)
数据访问类(@Repository
)
控制器(@RestController
/ @Controller
)
工具类(@Component
)
这些你直接加注解让 Spring 扫描就行,不需要写在配置类里。
@Component
public class EmailService { ... }
然后配置类只需要:
@Configuration
@ComponentScan("com.example") // Spring 自动扫描并管理 @Component 的类
public class AppConfig {}
@Bean
注册的情况:适合以下几类场景:
情况 | 示例 | 为什么适合用 @Bean |
---|---|---|
你不能改源码 | 第三方库中的类 | 你没法加 @Component |
需要复杂构造逻辑 | 创建一个带参数的对象 | @Bean 方法可写逻辑 |
想精确控制生命周期、依赖等 | 自定义初始化方法等 | 更灵活 |
同一个类要注册多个 Bean | 多个不同配置的实例 | 每个 @Bean 都独立 |
举例:
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public DataSource dataSource() {
return new HikariDataSource(); // 你可以写初始化逻辑
}
}
类类型 | 用法建议 |
---|---|
你自己写的业务类 | 用注解:@Component / @Service |
你不能改的类 / 初始化复杂 | 用配置类 @Bean 注册 |
想统一注册/组合多个类 | 用配置类统一管理逻辑 |
配置类的作用是手动定义和管理 Spring 容器中的 Bean,但不是所有类都要放进配置类里。常规类推荐用注解 + 扫描,特殊情况才用
@Bean
注册,这样灵活又清晰。
场景说明:
我们有一个 Server
类,需要为它注册两个不同的实例:
一个叫 devServer
,端口是 8080;
一个叫 prodServer
,端口是 80。
package com.example;
public class Server {
private String environment;
private int port;
public Server(String environment, int port) {
this.environment = environment;
this.port = port;
}
public void start() {
System.out.println("Starting " + environment + " server on port " + port);
}
}
package com.example.config;
import com.example.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public Server devServer() {
return new Server("dev", 8080);
}
@Bean
public Server prodServer() {
return new Server("prod", 80);
}
}
【注意】:
这两个bean,返回值都是Server,同一个,不同的实例!
package com.example;
import com.example.config.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
Server dev = context.getBean("devServer", Server.class);
Server prod = context.getBean("prodServer", Server.class);
dev.start(); // 输出: Starting dev server on port 8080
prod.start(); // 输出: Starting prod server on port 80
context.close();
}
}
Starting dev server on port 8080
Starting prod server on port 80
优点 | 说明 |
---|---|
配置灵活 | 可以为不同环境或用途构造不同 Bean |
互不影响 | 每个 @Bean 是独立对象 |
使用方便 | 在代码中通过名字或类型获取想要的实例 |
@Autowired
@Qualifier("devServer")
private Server server;
你可以通过 @Qualifier
指定注入哪个 Bean 实例。
当你想根据不同配置注册同一个类的多个实例时,用
@Bean
方法来命名和构造它们是最清晰灵活的方式。
适用场景:比如一个数据源配置多个 DataSource
的真实项目场景示例。
DataSource
这是真实企业开发中常见的高级用法场景:配置多个数据源(多库访问)。
你希望项目中同时连接:
主库(primary):用于写操作
从库(replica):用于读操作
这时我们就需要在 Spring 中注册两个不同的 DataSource
实例,并使用 @Qualifier
精确注入。
com.zaxxer
HikariCP
application.yml
示例)spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/main_db
username: root
password: root
replica:
url: jdbc:mysql://localhost:3306/replica_db
username: root
password: root
DataSourceConfig.java
package com.example.config;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean(name = "primaryDataSource")
@Primary // 默认首选(如果有歧义时优先)
public DataSource primaryDataSource(
@Value("${spring.datasource.primary.url}") String url,
@Value("${spring.datasource.primary.username}") String username,
@Value("${spring.datasource.primary.password}") String password
) {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
@Bean(name = "replicaDataSource")
public DataSource replicaDataSource(
@Value("${spring.datasource.replica.url}") String url,
@Value("${spring.datasource.replica.username}") String username,
@Value("${spring.datasource.replica.password}") String password
) {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
@Qualifier
package com.example.dao;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
@Repository
public class UserDao {
private final DataSource writeDataSource;
private final DataSource readDataSource;
public UserDao(
@Qualifier("primaryDataSource") DataSource writeDataSource,
@Qualifier("replicaDataSource") DataSource readDataSource
) {
this.writeDataSource = writeDataSource;
this.readDataSource = readDataSource;
}
public void writeToDb() {
System.out.println("写入操作用的 DataSource 是:" + writeDataSource);
}
public void readFromDb() {
System.out.println("读取操作用的 DataSource 是:" + readDataSource);
}
}
public class MainApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(DataSourceConfig.class, UserDao.class);
UserDao dao = context.getBean(UserDao.class);
dao.writeToDb();
dao.readFromDb();
}
}
控制输出
写入操作用的 DataSource 是:HikariDataSource (jdbc:mysql://localhost:3306/main_db)
读取操作用的 DataSource 是:HikariDataSource (jdbc:mysql://localhost:3306/replica_db)
场景 | 举例 |
---|---|
主从库分离 | 主库写、从库读,提高性能 |
多租户系统 | 不同租户连接不同数据库 |
动态切换数据源 | 数据源通过上下文线程绑定,动态决定 |
技术 | 说明 |
---|---|
@Bean(name="xxx") |
注册命名 Bean |
@Primary |
指定默认 Bean |
@Qualifier |
精确注入指定 Bean |
@Value |
从配置文件中读取属性 |
通过配置类中定义多个
@Bean
,可以为同一个类(如DataSource
)创建不同配置的实例,并通过@Qualifier
精确控制注入,实现多数据源管理。