java - Java配置类完全指南笔记
·
Java配置类完全指南笔记
📖 目录
一、什么是配置类?
1.1 核心定义
配置类 = 用来配置和组装 Spring 应用的特殊 Java 类
特点:
├─ 使用 @Configuration 注解标记
├─ 包含 @Bean 方法来创建对象
├─ 在应用启动时执行
├─ 负责搭建应用的基础设施
└─ 执行一次后,创建的对象会一直存在
1.2 形象比喻 🏗️
比喻1:工厂的建设过程
传统工厂建设:
────────────────────────────────
工人手动组装每个零件
每次生产都要重新搭建
效率低,容易出错
配置类建设:
────────────────────────────────
@Configuration = 工厂建设图纸
@Bean = 自动化生产线
启动时:
├─ 按图纸建好整个工厂
├─ 安装所有生产线
├─ 配置好所有设备
└─ 工厂就绪,随时生产
运行时:
├─ 订单来了,直接生产
├─ 不需要重新建工厂
└─ 高效、稳定
比喻2:餐厅的开业准备
没有配置类(混乱的餐厅):
────────────────────────────────
每次客人来:
├─ 现场购买食材
├─ 临时招聘厨师
├─ 现场布置餐桌
└─ 效率极低
有配置类(专业餐厅):
────────────────────────────────
@Configuration = 餐厅装修方案
@Bean = 厨房设备、餐桌、厨师团队
开业前(启动时):
├─ 装修好餐厅
├─ 安装好厨房设备
├─ 招聘好厨师团队
├─ 摆放好餐桌椅
└─ 准备好食材供应商
开业后(运行时):
├─ 客人点餐
├─ 直接使用已有设备和团队
├─ 快速出餐
└─ 不需要重新装修
1.3 代码对比
// ========== 没有配置类(原始方式)==========
public class UserService {
public User getUser(Long id) {
// 每次都要手动创建依赖
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/db", "root", "123456");
PreparedStatement ps = conn.prepareStatement("SELECT * FROM user WHERE id = ?");
ps.setLong(1, id);
ResultSet rs = ps.executeQuery();
User user = new User();
if (rs.next()) {
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
}
rs.close();
ps.close();
conn.close();
return user;
}
}
问题:
├─ 每次查询都创建连接(效率低)
├─ 数据库地址硬编码(难维护)
├─ 资源管理复杂(容易忘记关闭)
└─ 重复代码多(100个方法写100遍)
// ========== 有配置类(现代方式)==========
// 1. 配置类(启动时执行一次)
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
// 创建数据库连接池(只创建一次)
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/db");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setMaximumPoolSize(10); // 10个连接
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
// 创建数据库操作工具(只创建一次)
return new JdbcTemplate(dataSource);
}
}
// 2. 业务代码(简洁明了)
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate; // Spring自动注入
public User getUser(Long id) {
// 直接使用,不需要关心连接管理
return jdbcTemplate.queryForObject(
"SELECT * FROM user WHERE id = ?",
new BeanPropertyRowMapper<>(User.class),
id
);
}
}
优势:
├─ 连接池只创建一次(高效)
├─ 配置集中管理(易维护)
├─ 自动资源管理(不会忘记关闭)
└─ 业务代码简洁(专注业务逻辑)
二、为什么需要配置类?
2.1 核心价值
1. 依赖注入(Dependency Injection)
└─ 对象之间的依赖由 Spring 管理,不需要手动 new
2. 解耦(Decoupling)
└─ 业务代码不需要知道对象如何创建
3. 集中管理(Centralized Management)
└─ 所有配置在一个地方,修改方便
4. 生命周期管理(Lifecycle Management)
└─ Spring 自动管理对象的创建和销毁
5. 测试友好(Testability)
└─ 可以轻松替换依赖进行测试
2.2 实际问题场景
场景1:数据库连接管理
// ❌ 没有配置类的灾难
public class OrderService {
public void createOrder(Order order) {
// 每次都创建新连接
Connection conn = DriverManager.getConnection(...);
// 使用连接
conn.close();
}
}
public class UserService {
public void updateUser(User user) {
// 又创建一次连接
Connection conn = DriverManager.getConnection(...);
// 使用连接
conn.close();
}
}
public class ProductService {
public void addProduct(Product product) {
// 再创建一次连接
Connection conn = DriverManager.getConnection(...);
// 使用连接
conn.close();
}
}
问题统计:
├─ 100个Service = 创建100次连接
├─ 1000个请求 = 创建100,000次连接
├─ 数据库崩溃
└─ 应用卡死
// ✅ 有配置类的优雅方案
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
// 启动时创建一次连接池
HikariDataSource ds = new HikariDataSource();
ds.setMaximumPoolSize(20); // 最多20个连接
return ds;
}
}
@Service
public class OrderService {
@Autowired
private DataSource dataSource; // 共享同一个连接池
public void createOrder(Order order) {
// 从连接池获取,用完归还
try (Connection conn = dataSource.getConnection()) {
// 使用连接
}
}
}
效果:
├─ 100个Service = 共享1个连接池
├─ 1000个请求 = 最多用20个连接
├─ 数据库稳定
└─ 应用高效
场景2:多环境配置切换
// ❌ 没有配置类的痛苦
public class EmailService {
public void sendEmail(String to, String content) {
// 硬编码:开发环境
String host = "dev-smtp.example.com";
int port = 25;
// 部署到生产环境时:
// 需要改代码!改完重新编译!
// String host = "prod-smtp.example.com";
// int port = 587;
// 发送邮件...
}
}
切换环境的步骤:
1. 找到所有硬编码的地方(可能有50处)
2. 一个个修改
3. 重新编译
4. 重新打包
5. 重新部署
6. 祈祷没有改漏
// ✅ 有配置类的轻松切换
// application-dev.yml(开发环境)
email:
host: dev-smtp.example.com
port: 25
// application-prod.yml(生产环境)
email:
host: prod-smtp.example.com
port: 587
// 配置类
@Configuration
public class EmailConfig {
@Value("${email.host}")
private String host;
@Value("${email.port}")
private int port;
@Bean
public EmailSender emailSender() {
return new EmailSender(host, port);
}
}
// 业务代码
@Service
public class EmailService {
@Autowired
private EmailSender emailSender; // 自动适配环境
public void sendEmail(String to, String content) {
emailSender.send(to, content);
}
}
切换环境的步骤:
1. 修改启动参数:--spring.profiles.active=prod
2. 完成!代码一行不改!
三、配置类核心注解详解
配置类本身会自动加载,类会被实例化,但类本身不会"执行"。普通方法不会被执行
| 组件类型 | 是否自动执行 | 说明 |
|---|---|---|
@AutoConfiguration 类 | ✅ 自动加载 | 类会被实例化 |
@Bean 方法 | ✅ 自动执行 | 返回值注册到容器 |
@Service/@Component 类 | ✅ 自动加载 | 单独的服务类 |
| 普通方法 | ❌ 不自动执行 | 需要被调用 |
@Resource/@Autowired | ✅ 自动注入 | 依赖注入 |
3.1 @Configuration
/**
* @Configuration:标记这是一个配置类
*
* 作用:
* 1. 告诉Spring:这个类用来配置Bean
* 2. Spring会在启动时扫描并执行这个类
* 3. 类中的 @Bean 方法会被调用
*/
@Configuration
public class AppConfig {
// 配置内容
}
// 等价于传统的 XML 配置:
// <beans>
// <bean id="..." class="..."/>
// </beans>
使用场景:
// ✅ 推荐:每个功能模块一个配置类
@Configuration
public class DatabaseConfig {
// 数据库相关配置
}
@Configuration
public class SecurityConfig {
// 安全相关配置
}
@Configuration
public class RedisConfig {
// Redis相关配置
}
// ❌ 不推荐:所有配置混在一起
@Configuration
public class AllConfig {
// 数据库 + 安全 + Redis + ... 全在一起
// 难以维护,难以理解
}
3.2 @Bean
/**
* @Bean:创建一个由Spring管理的对象
*
* 特点:
* 1. 方法返回的对象会被放入Spring容器
* 2. 默认情况下,Bean的名称就是方法名
* 3. 方法只在启动时执行一次(单例)
* 4. 可以通过 @Autowired 注入到其他地方
*/
@Configuration
public class DatabaseConfig {
@Bean // Bean的名称是 "dataSource"
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/db");
return ds; // 这个对象会被Spring管理
}
@Bean(name = "myDb") // 自定义Bean名称
public DataSource customDataSource() {
return new HikariDataSource();
}
}
@Bean 的执行过程:
启动时发生的事:
════════════════════════════════════════
1. Spring 扫描到 @Configuration 类
2. Spring 调用 @Bean 方法
DataSource ds = dataSource(); // 执行方法
3. Spring 把返回的对象存起来
容器.put("dataSource", ds);
4. 其他地方可以获取这个对象
@Autowired
private DataSource dataSource; // Spring自动注入
运行时:
════════════════════════════════════════
@Bean 方法不会再执行
但创建的对象一直存在
所有地方共享这一个对象
3.3 @Autowired(依赖注入)
/**
* @Autowired:自动注入依赖
*
* 作用:
* 1. 告诉Spring:这里需要一个对象
* 2. Spring会从容器中找到匹配的对象
* 3. 自动注入进来
*/
@Service
public class UserService {
// 方式1:字段注入(最常用,但不推荐)
@Autowired
private UserRepository userRepository;
// 方式2:构造器注入(推荐)
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 方式3:Setter注入
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
依赖注入的形象比喻 🔌:
比喻:电器插座系统
────────────────────────────────────
传统方式(手动new):
├─ 电器:我需要电
├─ 你:好的,我去发电厂买电
├─ 你:自己拉电线到家里
├─ 你:连接到电器上
└─ 电器:终于能用了...
配置类 + 依赖注入:
├─ 发电厂(@Configuration):建好了,发电中
├─ 插座(Spring容器):到处都有
├─ 电器(@Autowired):插上就能用
└─ 你:啥都不用管
代码对应:
────────────────────────────────────
// 发电厂(配置类)
@Configuration
public class PowerConfig {
@Bean
public Electricity electricity() {
return new Electricity(220, "AC"); // 发电
}
}
// 插座(Spring容器)
[Spring Container]
├─ electricity (Electricity对象)
└─ 等待被注入
// 电器(业务代码)
@Component
public class Television {
@Autowired
private Electricity electricity; // 插上插座
public void turnOn() {
electricity.use(); // 直接用电
}
}
3.4 @Value(读取配置)
/**
* @Value:从配置文件读取值
*
* 用途:
* 1. 读取 application.yml 的配置
* 2. 读取环境变量
* 3. 使用 SpEL 表达式
*/
// application.yml
app:
name: MyApp
version: 1.0.0
max-connections: 100
@Component
public class AppInfo {
// 读取配置值
@Value("${app.name}")
private String appName; // "MyApp"
@Value("${app.version}")
private String version; // "1.0.0"
// 设置默认值(如果配置不存在)
@Value("${app.timeout:30}")
private int timeout; // 如果没配置,默认30
// 读取环境变量
@Value("${JAVA_HOME}")
private String javaHome;
// SpEL表达式
@Value("#{${app.max-connections} * 2}")
private int doubleConnections; // 200
}
3.5 @ConfigurationProperties(批量读取)
/**
* @ConfigurationProperties:批量读取配置
*
* 优势:
* 1. 类型安全
* 2. 支持复杂对象
* 3. IDE有提示
* 4. 统一管理
*/
// application.yml
email:
host: smtp.example.com
port: 587
username: admin@example.com
password: secret123
properties:
mail.smtp.auth: true
mail.smtp.starttls.enable: true
// 配置类
@Data
@Component
@ConfigurationProperties(prefix = "email")
public class EmailProperties {
private String host;
private int port;
private String username;
private String password;
private Map<String, String> properties;
}
// 使用
@Configuration
public class EmailConfig {
@Autowired
private EmailProperties emailProperties;
@Bean
public JavaMailSender mailSender() {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(emailProperties.getHost());
sender.setPort(emailProperties.getPort());
sender.setUsername(emailProperties.getUsername());
sender.setPassword(emailProperties.getPassword());
sender.setJavaMailProperties(
convertToProperties(emailProperties.getProperties())
);
return sender;
}
}
四、配置类执行时机
4.1 完整生命周期
应用启动的详细流程:
════════════════════════════════════════════════════════
0秒 - main() 方法执行
│
├─ SpringApplication.run(App.class, args);
│
├─ ▼ 阶段1:准备环境
│ ├─ 读取 application.yml
│ ├─ 读取环境变量
│ └─ 合并配置
│
├─ ▼ 阶段2:创建ApplicationContext(Spring容器)
│ └─ 准备一个空容器
│
├─ ▼ 阶段3:扫描组件
│ ├─ 扫描 @Configuration 类
│ ├─ 扫描 @Component、@Service 类
│ └─ 扫描 @Controller 类
│
├─ ▼ 阶段4:【配置类执行】← 关键时刻
│ │
│ ├─ 4.1 实例化配置类
│ │ new DatabaseConfig()
│ │ new SecurityConfig()
│ │ new RedisConfig()
│ │
│ ├─ 4.2 处理 @Bean 方法
│ │ ├─ 调用 dataSource() 方法
│ │ │ └─ 创建 DataSource 对象
│ │ │ └─ 存入容器: 容器.put("dataSource", ds)
│ │ │
│ │ ├─ 调用 jdbcTemplate() 方法
│ │ │ └─ 创建 JdbcTemplate 对象
│ │ │ └─ 存入容器
│ │ │
│ │ └─ 调用 filterChain() 方法
│ │ └─ 创建 SecurityFilterChain 对象
│ │ └─ 存入容器
│ │
│ └─ 4.3 处理依赖注入
│ ├─ UserService 需要 UserRepository
│ │ └─ 从容器获取 UserRepository
│ │ └─ 注入到 UserService
│ │
│ └─ OrderService 需要 OrderRepository
│ └─ 从容器获取 OrderRepository
│ └─ 注入到 OrderService
│
├─ ▼ 阶段5:启动Web服务器
│ ├─ 启动 Tomcat(默认8080端口)
│ └─ 注册所有 Controller 的路由
│
└─ ▼ 阶段6:启动完成
└─ 日志输出:Started Application in 2.5 seconds
════════════════════════════════════════════════════════
配置类工作结束
创建的对象常驻内存
开始接受用户请求
════════════════════════════════════════════════════════
4.2 可视化时间轴
时间 | 事件 | 内存状态
───────────────────────────────────────────────────────────
0.0s | 应用启动 | 空
0.1s | 读取配置文件 | 配置数据加载
0.3s | 扫描到配置类 | DatabaseConfig.class
| DatabaseConfig | SecurityConfig.class
| SecurityConfig |
| RedisConfig |
| |
0.5s | 【执行配置类】 | 开始创建Bean
| ├─ new DatabaseConfig() |
| ├─ dataSource() 方法执行 | ├─ DataSource对象
| │ └─ 创建HikariDataSource | │
| ├─ jdbcTemplate() 方法执行 | ├─ JdbcTemplate对象
| │ └─ 创建JdbcTemplate | │
| └─ ... | └─ ...
| |
1.0s | 所有Bean创建完成 | Spring容器已满
| | ├─ dataSource
| | ├─ jdbcTemplate
| | ├─ userService
| | ├─ orderService
| | └─ ...
| |
1.5s | Tomcat启动 | 监听8080端口
| |
2.0s | 应用启动完成 | 等待请求
| |
───────────────────────────────────────────────────────────
| 【配置类不再执行】 |
| 【Bean对象一直存在】 |
───────────────────────────────────────────────────────────
2.5s | 用户请求1到达 | 使用已有Bean
| GET /api/users | ├─ 从容器获取
| ├─ 进入过滤器链 | │ userService
| ├─ 调用UserController | └─ 执行业务逻辑
| └─ 调用UserService |
| └─ 使用 jdbcTemplate | (配置类不执行)
| |
3.0s | 用户请求2到达 | 使用已有Bean
| POST /api/orders | (还是那些对象)
| └─ 使用 OrderService |
| └─ 使用 jdbcTemplate | (配置类不执行)
| |
4.0s | 用户请求3到达 | 使用已有Bean
| ... | (配置类不执行)
4.3 代码验证
@Configuration
public class DemoConfig {
public DemoConfig() {
System.out.println("========================================");
System.out.println("DemoConfig 构造函数执行");
System.out.println("时间:" + LocalDateTime.now());
System.out.println("========================================");
}
@Bean
public DataSource dataSource() {
System.out.println(">>> dataSource() 方法执行");
System.out.println(">>> 时间:" + LocalDateTime.now());
System.out.println(">>> 创建DataSource对象");
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/db");
System.out.println(">>> DataSource创建完成");
return ds;
}
}
@Service
public class UserService {
@Autowired
private DataSource dataSource;
public UserService() {
System.out.println(">>> UserService 构造函数执行");
System.out.println(">>> 时间:" + LocalDateTime.now());
}
@PostConstruct
public void init() {
System.out.println(">>> UserService 依赖注入完成");
System.out.println(">>> dataSource 是否为null? " + (dataSource == null));
}
public User getUser(Long id) {
System.out.println("▶▶▶ getUser() 方法执行(处理用户请求)");
System.out.println("▶▶▶ 时间:" + LocalDateTime.now());
// 业务逻辑...
}
}
// 运行输出:
========================================
DemoConfig 构造函数执行
时间:2024-10-27T10:00:00.500
========================================
>>> dataSource() 方法执行
>>> 时间:2024-10-27T10:00:00.520
>>> 创建DataSource对象
>>> DataSource创建完成
>>> UserService 构造函数执行
>>> 时间:2024-10-27T10:00:00.800
>>> UserService 依赖注入完成
>>> dataSource 是否为null? false
[启动完成,等待请求...]
[用户请求到达]
▶▶▶ getUser() 方法执行(处理用户请求)
▶▶▶ 时间:2024-10-27T10:01:30.000
[又一个用户请求]
▶▶▶ getUser() 方法执行(处理用户请求)
▶▶▶ 时间:2024-10-27T10:02:45.000
// 注意:配置类和@Bean方法只执行一次(启动时)
// 但getUser()每次请求都执行
五、完整实战案例
案例1:电商系统的配置
5.1 项目结构
src/main/java/com/example/shop/
├── config/ # 配置包
│ ├── DatabaseConfig.java # 数据库配置
│ ├── RedisConfig.java # Redis配置
│ ├── SecurityConfig.java # 安全配置
│ └── EmailConfig.java # 邮件配置
│
├── controller/ # 控制器
│ ├── UserController.java
│ ├── ProductController.java
│ └── OrderController.java
│
├── service/ # 服务层
│ ├── UserService.java
│ ├── ProductService.java
│ └── OrderService.java
│
└── ShopApplication.java # 启动类
src/main/resources/
├── application.yml # 主配置
├── application-dev.yml # 开发环境
└── application-prod.yml # 生产环境
5.2 数据库配置
# application.yml(主配置)
spring:
profiles:
active: dev # 激活开发环境
# application-dev.yml(开发环境)
spring:
datasource:
url: jdbc:mysql://localhost:3306/shop_dev
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10 # 开发环境10个连接够了
minimum-idle: 5
connection-timeout: 20000
idle-timeout: 300000
# application-prod.yml(生产环境)
spring:
datasource:
url: jdbc:mysql://prod-db.example.com:3306/shop_prod
username: shop_user
password: ${DB_PASSWORD} # 从环境变量读取
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 50 # 生产环境需要更多连接
minimum-idle: 20
connection-timeout: 30000
idle-timeout: 600000
/**
* 数据库配置类
*
* 作用:配置数据源和事务管理
*/
@Configuration
@EnableTransactionManagement // 启用事务
public class DatabaseConfig {
@Value("${spring.datasource.url}")
private String jdbcUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.hikari.maximum-pool-size}")
private int maxPoolSize;
/**
* 数据源Bean
*
* 说明:
* - 使用HikariCP连接池(性能最好)
* - 启动时创建一次
* - 所有Service共享这一个连接池
*/
@Bean
public DataSource dataSource() {
System.out.println(">>> 创建数据源...");
System.out.println(">>> JDBC URL: " + jdbcUrl);
System.out.println(">>> 最大连接数: " + maxPoolSize);
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setMaximumPoolSize(maxPoolSize);
dataSource.setMinimumIdle(5);
dataSource.setConnectionTimeout(20000);
// 可选:设置连接测试查询
dataSource.setConnectionTestQuery("SELECT 1");
System.out.println(">>> 数据源创建完成!");
return dataSource;
}
/**
* JdbcTemplate Bean
*
* 说明:
* - 依赖 dataSource
* - Spring会自动注入上面创建的dataSource
*/
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
System.out.println(">>> 创建JdbcTemplate...");
return new JdbcTemplate(dataSource);
}
/**
* 事务管理器
*
* 说明:
* - 管理数据库事务
* - 配合 @Transactional 注解使用
*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
5.3 Redis配置
# application-dev.yml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 3000
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 2
# application-prod.yml
spring:
redis:
host: prod-redis.example.com
port: 6379
password: ${REDIS_PASSWORD}
database: 0
timeout: 5000
lettuce:
pool:
max-active: 50
max-idle: 20
min-idle: 10
/**
* Redis配置类
*
* 作用:配置Redis连接和序列化方式
*/
@Configuration
public class RedisConfig {
/**
* Redis连接工厂
*
* 说明:
* - Spring Boot已经自动配置了
* - 这里只是展示如何自定义
*/
@Bean
public RedisConnectionFactory redisConnectionFactory(
@Value("${spring.redis.host}") String host,
@Value("${spring.redis.port}") int port,
@Value("${spring.redis.password}") String password) {
System.out.println(">>> 创建Redis连接工厂...");
System.out.println(">>> Redis Host: " + host + ":" + port);
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(host);
config.setPort(port);
if (password != null && !password.isEmpty()) {
config.setPassword(password);
}
return new LettuceConnectionFactory(config);
}
/**
* RedisTemplate配置
*
* 说明:
* - 配置Key和Value的序列化方式
* - Key用String序列化
* - Value用JSON序列化
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory connectionFactory) {
System.out.println(">>> 创建RedisTemplate...");
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// Key序列化:使用String
StringRedisSerializer keySerializer = new StringRedisSerializer();
template.setKeySerializer(keySerializer);
template.setHashKeySerializer(keySerializer);
// Value序列化:使用JSON
Jackson2JsonRedisSerializer<Object> valueSerializer =
new Jackson2JsonRedisSerializer<>(Object.class);
template.setValueSerializer(valueSerializer);
template.setHashValueSerializer(valueSerializer);
template.afterPropertiesSet();
System.out.println(">>> RedisTemplate创建完成!");
return template;
}
/**
* 缓存管理器
*
* 说明:
* - 配合 @Cacheable 注解使用
* - 自动管理缓存
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 默认1小时过期
.disableCachingNullValues(); // 不缓存null值
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}
5.4 Security配置(Token认证)
# application.yml
security:
token:
header: Authorization
secret: ${JWT_SECRET:my-dev-secret-key-123456}
expire-time: 7200 # 2小时(秒)
refresh-expire-time: 2592000 # 30天(秒)
permit-all-urls:
- /api/auth/login
- /api/auth/register
- /api/products/** # 商品列表公开访问
- /doc.html
- /swagger-ui/**
/**
* Security配置类
*
* 作用:配置Spring Security的安全策略
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private TokenAuthenticationFilter tokenAuthenticationFilter;
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private AccessDeniedHandler accessDeniedHandler;
@Value("${security.permit-all-urls}")
private List<String> permitAllUrls;
/**
* 密码加密器
*
* 说明:
* - 使用BCrypt算法
* - 注册时加密密码
* - 登录时验证密码
*/
@Bean
public PasswordEncoder passwordEncoder() {
System.out.println(">>> 创建密码加密器(BCrypt)");
return new BCryptPasswordEncoder();
}
/**
* 认证管理器
*
* 说明:
* - 负责用户认证
* - 登录时使用
*/
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
/**
* 安全过滤器链
*
* 说明:
* - 这是配置的核心
* - 定义哪些URL需要认证
* - 定义Token过滤器的位置
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
System.out.println(">>> 配置Spring Security过滤器链...");
System.out.println(">>> 白名单URL: " + permitAllUrls);
return http
// 1. 禁用CSRF(使用Token,不需要CSRF防护)
.csrf(csrf -> csrf.disable())
// 2. 禁用Session(无状态认证)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
// 3. 配置跨域
.cors(Customizer.withDefaults())
// 4. 配置URL访问规则
.authorizeHttpRequests(auth -> auth
// 白名单:允许匿名访问
.requestMatchers(permitAllUrls.toArray(new String[0]))
.permitAll()
// 静态资源:允许匿名访问
.requestMatchers("/", "/*.html", "/*.css", "/*.js")
.permitAll()
// 管理员接口:需要ADMIN角色
.requestMatchers("/api/admin/**")
.hasRole("ADMIN")
// 其他请求:需要认证
.anyRequest()
.authenticated()
)
// 5. 添加Token验证过滤器
.addFilterBefore(
tokenAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class
)
// 6. 异常处理
.exceptionHandling(ex -> ex
// 未认证时的处理
.authenticationEntryPoint(authenticationEntryPoint)
// 无权限时的处理
.accessDeniedHandler(accessDeniedHandler)
)
.build();
}
}
5.5 邮件配置
# application.yml
email:
host: smtp.gmail.com
port: 587
username: noreply@shop.com
password: ${EMAIL_PASSWORD}
protocol: smtp
default-encoding: UTF-8
properties:
mail.smtp.auth: true
mail.smtp.starttls.enable: true
mail.smtp.starttls.required: true
/**
* 邮件配置属性
*/
@Data
@Component
@ConfigurationProperties(prefix = "email")
public class EmailProperties {
private String host;
private int port;
private String username;
private String password;
private String protocol = "smtp";
private String defaultEncoding = "UTF-8";
private Map<String, String> properties = new HashMap<>();
}
/**
* 邮件配置类
*/
@Configuration
public class EmailConfig {
@Autowired
private EmailProperties emailProperties;
/**
* 邮件发送器
*
* 说明:
* - 配置SMTP服务器
* - 支持发送HTML邮件
*/
@Bean
public JavaMailSender mailSender() {
System.out.println(">>> 创建邮件发送器...");
System.out.println(">>> SMTP服务器: " + emailProperties.getHost());
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(emailProperties.getHost());
sender.setPort(emailProperties.getPort());
sender.setUsername(emailProperties.getUsername());
sender.setPassword(emailProperties.getPassword());
sender.setProtocol(emailProperties.getProtocol());
sender.setDefaultEncoding(emailProperties.getDefaultEncoding());
// 设置SMTP属性
Properties props = new Properties();
emailProperties.getProperties().forEach(props::setProperty);
sender.setJavaMailProperties(props);
System.out.println(">>> 邮件发送器创建完成!");
return sender;
}
}
5.6 业务代码使用配置
/**
* 用户服务
*
* 说明:
* - 使用配置类创建的Bean
* - 不需要关心这些Bean是如何创建的
* - 专注业务逻辑
*/
@Service
@Transactional
public class UserService {
// 所有依赖都自动注入
@Autowired
private JdbcTemplate jdbcTemplate; // 来自DatabaseConfig
@Autowired
private RedisTemplate<String, Object> redisTemplate; // 来自RedisConfig
@Autowired
private PasswordEncoder passwordEncoder; // 来自SecurityConfig
@Autowired
private JavaMailSender mailSender; // 来自EmailConfig
/**
* 用户注册
*/
public User register(RegisterDTO dto) {
// 1. 加密密码(使用配置的passwordEncoder)
String encodedPassword = passwordEncoder.encode(dto.getPassword());
// 2. 保存到数据库(使用配置的jdbcTemplate)
String sql = "INSERT INTO user (username, password, email) VALUES (?, ?, ?)";
jdbcTemplate.update(sql, dto.getUsername(), encodedPassword, dto.getEmail());
// 3. 缓存用户信息(使用配置的redisTemplate)
User user = getUserByUsername(dto.getUsername());
String cacheKey = "user:" + user.getId();
redisTemplate.opsForValue().set(cacheKey, user, 1, TimeUnit.HOURS);
// 4. 发送欢迎邮件(使用配置的mailSender)
sendWelcomeEmail(user.getEmail());
return user;
}
/**
* 用户登录
*/
public String login(LoginDTO dto) {
// 1. 查询用户
User user = getUserByUsername(dto.getUsername());
if (user == null) {
throw new BusinessException("用户不存在");
}
// 2. 验证密码(使用配置的passwordEncoder)
if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) {
throw new BusinessException("密码错误");
}
// 3. 生成Token
String token = generateToken(user);
// 4. 缓存Token(使用配置的redisTemplate)
String tokenKey = "token:" + token;
redisTemplate.opsForValue().set(tokenKey, user.getId(), 2, TimeUnit.HOURS);
return token;
}
/**
* 获取用户信息
*/
public User getUserById(Long id) {
// 1. 先从缓存获取(使用配置的redisTemplate)
String cacheKey = "user:" + id;
User user = (User) redisTemplate.opsForValue().get(cacheKey);
if (user != null) {
System.out.println(">>> 从缓存获取用户: " + id);
return user;
}
// 2. 缓存未命中,从数据库查询(使用配置的jdbcTemplate)
System.out.println(">>> 从数据库查询用户: " + id);
String sql = "SELECT * FROM user WHERE id = ?";
user = jdbcTemplate.queryForObject(
sql,
new BeanPropertyRowMapper<>(User.class),
id
);
// 3. 写入缓存
if (user != null) {
redisTemplate.opsForValue().set(cacheKey, user, 1, TimeUnit.HOURS);
}
return user;
}
private void sendWelcomeEmail(String to) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject("欢迎注册我们的商城");
message.setText("感谢您的注册!");
mailSender.send(message);
}
private String generateToken(User user) {
// JWT生成逻辑...
return "token-" + user.getId();
}
private User getUserByUsername(String username) {
String sql = "SELECT * FROM user WHERE username = ?";
return jdbcTemplate.queryForObject(
sql,
new BeanPropertyRowMapper<>(User.class),
username
);
}
}
5.7 启动日志输出
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.0.0)
2024-10-27 10:00:00.123 INFO --- Starting ShopApplication
2024-10-27 10:00:00.456 INFO --- The following profiles are active: dev
>>> 创建数据源...
>>> JDBC URL: jdbc:mysql://localhost:3306/shop_dev
>>> 最大连接数: 10
>>> 数据源创建完成!
>>> 创建JdbcTemplate...
>>> 创建Redis连接工厂...
>>> Redis Host: localhost:6379
>>> 创建RedisTemplate...
>>> RedisTemplate创建完成!
>>> 创建密码加密器(BCrypt)
>>> 配置Spring Security过滤器链...
>>> 白名单URL: [/api/auth/login, /api/auth/register, /api/products/**]
>>> 创建邮件发送器...
>>> SMTP服务器: smtp.gmail.com
>>> 邮件发送器创建完成!
2024-10-27 10:00:02.789 INFO --- Tomcat started on port(s): 8080
2024-10-27 10:00:02.890 INFO --- Started ShopApplication in 2.767 seconds
[应用启动完成,等待请求...]
六、配置类高级用法
6.1 条件装配(@Conditional)
/**
* 根据条件决定是否创建Bean
*/
@Configuration
public class ConditionalConfig {
/**
* 只在开发环境创建
*/
@Bean
@Profile("dev")
public MockDataService mockDataService() {
return new MockDataService();
}
/**
* 只在生产环境创建
*/
@Bean
@Profile("prod")
public RealDataService realDataService() {
return new RealDataService();
}
/**
* 只在类路径存在Redis时创建
*/
@Bean
@ConditionalOnClass(RedisTemplate.class)
public CacheService redisCacheService() {
return new RedisCacheService();
}
/**
* 只在配置了某个属性时创建
*/
@Bean
@ConditionalOnProperty(name = "feature.email.enabled", havingValue = "true")
public EmailService emailService() {
return new EmailService();
}
/**
* 只在没有其他EmailService时创建(默认实现)
*/
@Bean
@ConditionalOnMissingBean(EmailService.class)
public EmailService defaultEmailService() {
return new MockEmailService();
}
}
6.2 导入其他配置(@Import)
/**
* 主配置类
*/
@Configuration
@Import({
DatabaseConfig.class,
RedisConfig.class,
SecurityConfig.class
})
public class AppConfig {
// 主配置
}
// 或者使用@EnableXxx的方式
@Configuration
@EnableCaching // 启用缓存(会导入CachingConfiguration)
@EnableAsync // 启用异步(会导入AsyncConfiguration)
@EnableScheduling // 启用定时任务(会导入SchedulingConfiguration)
public class AppConfig {
}
6.3 Bean的作用域
@Configuration
public class ScopeConfig {
/**
* 单例(默认)
* 整个应用只创建一次
*/
@Bean
@Scope("singleton") // 可省略,默认就是singleton
public UserService userService() {
return new UserService();
}
/**
* 原型
* 每次获取都创建新对象
*/
@Bean
@Scope("prototype")
public Order order() {
return new Order();
}
/**
* 请求作用域
* 每个HTTP请求创建一个
*/
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public RequestInfo requestInfo() {
return new RequestInfo();
}
/**
* Session作用域
* 每个Session创建一个
*/
@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserCart userCart() {
return new UserCart();
}
}
作用域对比:
┌─────────────────────────────────────────────────────┐
│ singleton(单例) │
│ ──────────────────────────────────────────────── │
│ 应用启动 → 创建1个对象 │
│ 请求1 → 使用同一个对象 │
│ 请求2 → 使用同一个对象 │
│ 请求3 → 使用同一个对象 │
│ 应用关闭 → 销毁这个对象 │
│ │
│ 示例:DataSource, UserService, ProductService │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ prototype(原型) │
│ ──────────────────────────────────────────────── │
│ 每次注入 → 创建新对象 │
│ @Autowired Order order1; → 对象A │
│ @Autowired Order order2; → 对象B(新创建) │
│ │
│ 示例:订单对象、购物车项 │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ request(请求作用域) │
│ ──────────────────────────────────────────────── │
│ 请求1开始 → 创建对象A → 请求1结束 → 销毁对象A │
│ 请求2开始 → 创建对象B → 请求2结束 → 销毁对象B │
│ │
│ 示例:RequestInfo(请求信息) │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ session(会话作用域) │
│ ──────────────────────────────────────────────── │
│ 用户A登录 → 创建购物车A → 用户A登出 → 销毁购物车A │
│ 用户B登录 → 创建购物车B → 用户B登出 → 销毁购物车B │
│ │
│ 示例:购物车、用户会话信息 │
└─────────────────────────────────────────────────────┘
6.4 Bean的生命周期回调
@Configuration
public class LifecycleConfig {
/**
* 方式1:使用 @Bean 的属性
*/
@Bean(initMethod = "init", destroyMethod = "cleanup")
public DataSource dataSource() {
return new HikariDataSource();
}
/**
* 方式2:实现接口
*/
@Bean
public MyService myService() {
return new MyService();
}
}
public class MyService implements InitializingBean, DisposableBean {
/**
* 初始化回调(Bean创建后)
*/
@Override
public void afterPropertiesSet() {
System.out.println(">>> MyService 初始化完成");
// 初始化逻辑...
}
/**
* 销毁回调(应用关闭前)
*/
@Override
public void destroy() {
System.out.println(">>> MyService 销毁");
// 清理资源...
}
}
/**
* 方式3:使用注解(推荐)
*/
@Component
public class ResourceManager {
private ExecutorService executorService;
/**
* 初始化方法
*/
@PostConstruct
public void init() {
System.out.println(">>> ResourceManager 初始化");
executorService = Executors.newFixedThreadPool(10);
}
/**
* 销毁方法
*/
@PreDestroy
public void cleanup() {
System.out.println(">>> ResourceManager 清理资源");
if (executorService != null) {
executorService.shutdown();
}
}
}
生命周期完整流程:
Bean的完整生命周期:
═══════════════════════════════════════════════════════
1. 实例化
└─ Spring调用构造函数创建对象
2. 属性赋值
└─ @Autowired、@Value 注入依赖
3. Aware接口回调
├─ BeanNameAware.setBeanName()
├─ BeanFactoryAware.setBeanFactory()
└─ ApplicationContextAware.setApplicationContext()
4. BeanPostProcessor前置处理
└─ postProcessBeforeInitialization()
5. 初始化
├─ @PostConstruct 方法执行
├─ InitializingBean.afterPropertiesSet()
└─ @Bean(initMethod) 指定的方法
6. BeanPostProcessor后置处理
└─ postProcessAfterInitialization()
7. Bean可用
└─ 正常使用
8. 销毁
├─ @PreDestroy 方法执行
├─ DisposableBean.destroy()
└─ @Bean(destroyMethod) 指定的方法
七、最佳实践
7.1 配置类的组织结构
✅ 好的组织方式:
src/main/java/com/example/config/
├── DatabaseConfig.java # 数据库配置
├── RedisConfig.java # Redis配置
├── SecurityConfig.java # 安全配置
├── WebMvcConfig.java # Web MVC配置
├── MessageQueueConfig.java # 消息队列配置
└── properties/ # 配置属性类
├── DatabaseProperties.java
├── RedisProperties.java
└── SecurityProperties.java
优势:
├─ 职责明确
├─ 易于查找
├─ 便于维护
└─ 团队协作友好
❌ 不好的组织方式:
src/main/java/com/example/config/
└── ApplicationConfig.java # 所有配置混在一起
问题:
├─ 文件过大(1000+行)
├─ 难以定位
├─ 容易冲突
└─ 难以复用
7.2 配置的优先级
配置来源的优先级(从高到低):
═══════════════════════════════════════════════════════
1. 命令行参数
java -jar app.jar --server.port=8081
2. Java系统属性
-Dserver.port=8081
3. 操作系统环境变量
export SERVER_PORT=8081
4. application-{profile}.yml
application-prod.yml
5. application.yml
默认配置
6. @PropertySource 指定的文件
7. 默认值(代码中设置的)
@Value("${server.port:8080}") # 默认8080
实际应用:
# application.yml(默认配置)
server:
port: 8080
# application-prod.yml(生产环境覆盖)
server:
port: 80
# 环境变量(最高优先级)
export SERVER_PORT=9000
# 最终结果:
如果设置了环境变量 → 使用9000
否则,如果是prod环境 → 使用80
否则 → 使用8080
7.3 敏感信息处理
# ❌ 错误:敏感信息写在配置文件
spring:
datasource:
password: MyPassword123! # 提交到Git!危险!
# ✅ 正确:使用环境变量
spring:
datasource:
password: ${DB_PASSWORD} # 从环境变量读取
# ✅ 正确:使用Spring Cloud Config
# 从配置中心读取,配置中心会加密存储
生产环境部署:
# 方式1:环境变量
export DB_PASSWORD=SecurePassword123
export JWT_SECRET=MyJwtSecret456
java -jar app.jar
# 方式2:启动参数
java -jar app.jar \
--spring.datasource.password=SecurePassword123 \
--security.token.secret=MyJwtSecret456
# 方式3:配置文件(不提交到Git)
# application-prod.yml(.gitignore忽略)
spring:
datasource:
password: SecurePassword123
# 方式4:使用配置中心(推荐)
# Nacos、Apollo、Spring Cloud Config
7.4 多数据源配置
/**
* 多数据源配置
*
* 场景:
* - 主库(写)
* - 从库(读)
* - 第三方数据库
*/
@Configuration
public class MultiDataSourceConfig {
/**
* 主数据源(默认)
*/
@Bean
@Primary // 标记为主数据源
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 从数据源(只读)
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 主数据源的JdbcTemplate
*/
@Bean
@Primary
public JdbcTemplate primaryJdbcTemplate(
@Qualifier("primaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
/**
* 从数据源的JdbcTemplate
*/
@Bean
public JdbcTemplate secondaryJdbcTemplate(
@Qualifier("secondaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
// 配置文件
spring:
datasource:
primary:
jdbc-url: jdbc:mysql://master-db:3306/shop
username: root
password: ${MASTER_DB_PASSWORD}
secondary:
jdbc-url: jdbc:mysql://slave-db:3306/shop
username: readonly
password: ${SLAVE_DB_PASSWORD}
// 使用
@Service
public class UserService {
@Autowired
@Qualifier("primaryJdbcTemplate")
private JdbcTemplate writeDb; // 写操作
@Autowired
@Qualifier("secondaryJdbcTemplate")
private JdbcTemplate readDb; // 读操作
public void createUser(User user) {
writeDb.update("INSERT INTO user ...", ...);
}
public User getUser(Long id) {
return readDb.queryForObject("SELECT * FROM user ...", ...);
}
}
八、常见误区
8.1 误区1:配置类会被多次执行
// ❌ 错误理解
@Configuration
public class MyConfig {
@Bean
public DataSource dataSource() {
// 我以为每次请求都会执行这个方法
System.out.println("创建数据源");
return new HikariDataSource();
}
}
// ✅ 正确理解
/**
* @Bean 方法只在启动时执行一次
* 之后所有地方共享同一个DataSource对象
*/
启动时:
├─ dataSource()执行 → 创建DataSource对象A
└─ 存入容器
请求1:使用对象A
请求2:使用对象A
请求3:使用对象A
...
全都是同一个对象A!
8.2 误区2:@Bean方法可以随便调用
// ❌ 错误做法
@Configuration
public class BadConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
public JdbcTemplate jdbcTemplate() {
// 直接调用@Bean方法
DataSource ds = dataSource(); // 危险!
return new JdbcTemplate(ds);
}
}
// ✅ 正确做法
@Configuration
public class GoodConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
// 通过参数注入
return new JdbcTemplate(dataSource);
}
}
原因:
├─ 直接调用:每次都创建新对象
├─ 参数注入:Spring确保使用同一个对象
└─ Spring会拦截@Bean方法调用,确保单例
8.3 误区3:配置类必须有无参构造函数
// ❌ 错误:以为配置类不能有依赖
@Configuration
public class BadConfig {
// 无法注入依赖?
}
// ✅ 正确:配置类可以注入依赖
@Configuration
@RequiredArgsConstructor // Lombok生成构造函数
public class GoodConfig {
private final SecurityProperties properties; // 可以注入
private final TokenService tokenService; // 可以注入
@Bean
public SecurityFilterChain filterChain() {
// 使用注入的依赖
return http
.addFilter(new TokenFilter(tokenService))
.build();
}
}
8.4 误区4:所有配置都放在yml文件
// ❌ 不要什么都放yml
spring:
datasource:
url: ...
security:
complex-object: # 复杂对象不适合yml
filters:
- name: filter1
order: 1
config:
key1: value1
key2: value2
handlers:
- ...
// ✅ 简单配置用yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/db
username: root
password: ${DB_PASSWORD}
// ✅ 复杂配置用Java代码
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain() {
// 复杂的对象组装
return http
.addFilter(customFilter1)
.addFilter(customFilter2)
.exceptionHandling(...)
.build();
}
}
原则:
├─ 简单数据 → yml配置
├─ 复杂对象 → Java配置
└─ 业务逻辑 → Java配置
8.5 误区5:忘记不同环境的配置
// ❌ 只有一个配置文件
application.yml:
datasource:
url: jdbc:mysql://localhost:3306/db # 开发环境地址
部署到生产:崩溃!连接不到localhost
// ✅ 多环境配置
application.yml: # 公共配置
application:
name: myapp
application-dev.yml: # 开发环境
datasource:
url: jdbc:mysql://localhost:3306/dev_db
application-prod.yml: # 生产环境
datasource:
url: jdbc:mysql://prod-db.example.com:3306/prod_db
password: ${DB_PASSWORD} # 环境变量
启动时指定环境:
java -jar app.jar --spring.profiles.active=prod
九、总结
核心要点回顾
1. 配置类 ≠ 配置文件
├─ 配置类是Java代码
├─ 在启动时执行
└─ 用来创建和组装Bean
2. 执行时机
├─ 启动时执行一次
├─ @Bean方法被调用
├─ 创建的对象放入容器
└─ 运行时不再执行
3. 核心注解
├─ @Configuration:标记配置类
├─ @Bean:创建Bean对象
├─ @Autowired:注入依赖
├─ @Value:读取单个配置
└─ @ConfigurationProperties:批量读取配置
4. 最佳实践
├─ 按功能模块组织配置类
├─ 敏感信息用环境变量
├─ 多环境用profile
└─ 简单配置用yml,复杂配置用Java
形象记忆口诀
配置类,搭舞台 🏗️
启动时,只一回 ⏰
创建Bean,存起来 📦
业务来,直接用 ✅
不重建,效率高 🚀
环境切,改参数 🔄
代码稳,配置活 💡
快速检查清单
✅ 每个功能模块有独立配置类
✅ @Configuration 注解没遗漏
✅ @Bean 方法返回类型正确
✅ 敏感信息使用环境变量
✅ 多环境配置已准备
✅ Bean的作用域选择正确
✅ 生命周期回调已实现
✅ 条件装配合理使用
✅ 配置类有清晰的注释
✅ 测试配置已准备
最后总结:
配置类就像是建房子的施工队,在应用启动时(开工),按照图纸(代码)把房子(Spring容器)建好。建好之后,住户(业务代码)就可以直接入住使用了,不需要每次都重建房子。
配置类的本质是:在启动时执行的代码,用来创建和组装应用所需的各种对象,这些对象会被Spring管理并在运行时重复使用。
更多推荐


所有评论(0)