⚡Spring JDBC 完整体系 · 第 2 讲 · Spring 容器最小案例
·
⚡ Spring JDBC 完整体系 · 第 2 讲 · Spring 容器最小案例
相关开源地址:
📋 前置知识
| 你只需要会 | 你完全不需要会 |
|---|---|
| Java 基础(类、接口、注解) | Spring Boot 自动配置原理 |
| 会用 IDE 运行 Maven 项目 | Web 容器、Servlet 知识 |
| 第 1 讲:裸 JDBC Driver | 数据库驱动、连接池知识 |
📚 全套教程总览(20 讲从零到架构师)
| 篇章 | 涵盖讲次 | 核心目标 |
|---|---|---|
| 开篇筑基 | 第 1-3 讲 | 认知梳理,建立 Spring JDBC 的底层坐标系 |
| API 精讲 | 第 4-7 讲 | 全面掌握 JdbcTemplate + 事务体系 |
| 工具封装 | 第 8-9 讲 | 手搓条件工具 + 代码生成器,从“使用者”变“创造者” |
| 认知升华 | 第 10 讲 | 模板确定性 vs AI 不确定性,建立技术判断力 |
| 高阶扩展 | 第 11-17 讲 | 多数据源、分库分表、分布式事务 |
| 底层观测 | 第 18-19 讲 | SQL 拦截、性能监控、连接池调优 |
| 终极收尾 | 第 20 讲 | 全链路复盘 + 手搓持久层框架 |
一、开篇 · 为什么要理解 Spring 容器?
上一讲体验了裸 JDBC 的繁琐资源管理,这一讲正式进入 Spring 容器。
Spring 容器本质上就是一个 Map。@Bean / @Repository 往 Map 里存,@Autowired 从 Map 里取。吃透这张 Map,就掌握了 IoC 和 DI 的本质。
本讲不引入任何数据库驱动,不涉及 Web 容器,仅围绕 Spring IoC 容器本身展开。这是整个 Spring JDBC 体系的认知基石。
二、极简依赖 · 纯 IoC 容器
本讲只依赖 spring-context,没有 Web 容器,没有数据库驱动,是最干净的纯 IoC 容器环境。
| 依赖 | 作用 |
|---|---|
spring-context | Spring 核心 IoC 容器,Bean 管理 |
logback-classic | 日志输出,查看运行结果 |
lombok | 简化 POJO 的 getter / setter / 构造方法 |
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>[3.3.0,4.0.0)</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
三、核心代码演示
3.1 配置类 · 两种 Bean 注册方式
@Configuration 标记配置类,@ComponentScan 指定包扫描路径。@Bean 方法手动注册 Bean,方法名作为 Map 的 key。
@Configuration // 标记为配置类
@ComponentScan("com.gzz") // 指定包扫描路径
public class AppConfig {
@Bean // 手动注册,方法名 "user1" 作为 Map 的 key
public User user1() {
return new User("user1", 1);
}
@Bean // 手动注册,方法名 "user2" 作为 Map 的 key
public User user2() {
return new User("user2", 2);
}
}
3.2 实体类
User 是简单 POJO,用 Lombok 简化代码:
@Getter
@Setter
@AllArgsConstructor
public class User {
private String name;
private int gender; // 1=男,2=女
}
3.3 DAO 组件 · 包扫描自动注册
@Repository 将 UserDao 自动注册进容器,@Slf4j 支持日志打印:
@Slf4j
@Repository // 标记为数据访问层组件,自动注册进 Map
public class UserDao {
public User save(User user) {
log.info("save方法中的user={}", user);
return user;
}
}
3.4 启动类 · 自身也要成为 Bean
启动类有两个关键点:
- 自身必须标注
@Component,否则容器不会管理它,@Autowired和@EventListener全部失效。 main方法创建AnnotationConfigApplicationContext,传入配置类,触发容器启动。
@Slf4j
@Component // 关键:让 Application 自身也被容器管理
public class Application {
public static void main(String[] arg) {
new AnnotationConfigApplicationContext(AppConfig.class);
}
@Autowired
@Qualifier("user1") // 按名称注入 → Map.get("user1")
private User user1;
@Autowired
@Qualifier("user2") // 按名称注入 → Map.get("user2")
private User user2;
@Autowired // 按类型注入 UserDao
private UserDao dao;
3.5 事件监听 · 容器启动后自动执行
@EventListener 监听 ContextRefreshedEvent,等容器完全刷新后再执行测试方法,此时所有 Bean 已就绪:
@EventListener(ContextRefreshedEvent.class)
public void run() {
log.info("user1={}", user1);
log.info("user2={}", user2);
User user3 = dao.save(user1);
log.info("user3={}", user3);
log.info("dao={}", dao);
log.info("user1.getName()={}, user1.getGender()={}", user1.getName(), user1.getGender());
log.info("user2.getName()={}, user2.getGender()={}", user2.getName(), user2.getGender());
}
}
3.6 日志配置 · logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d[%F:%L][%p]:[%t]%m%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>
四、运行结果
2026-06-26 18:54:37,278[Application.java:31][INFO]:[main]user1.getName()=user1,user1.getGender()=1
2026-06-26 18:54:37,280[Application.java:32][INFO]:[main]user2.getName()=user2,user2.getGender()=2
2026-06-26 18:54:37,280[Application.java:33][INFO]:[main]user1=com.gzz.user.User@7e5c856f
2026-06-26 18:54:37,280[Application.java:34][INFO]:[main]user2=com.gzz.user.User@413f69cc
2026-06-26 18:54:37,280[UserDao.java:12][INFO]:[main]save方法中的user=com.gzz.user.User@7e5c856f
2026-06-26 18:54:37,280[Application.java:36][INFO]:[main]user3=com.gzz.user.User@7e5c856f
2026-06-26 18:54:37,280[Application.java:37][INFO]:[main]dao=com.gzz.user.UserDao@1f53a5dc
user1 和 user3 内存地址相同(@7e5c856f),证明 dao.save(user1) 返回的就是容器中的同一个单例对象。
五、本讲核心总结
5.1 Spring 容器本质
| 操作 | 注解 / 方法 | 本质 |
|---|---|---|
| 注册 Bean | @Bean / @Repository | map.put(key, 实例) |
| 注入依赖 | @Autowired + @Qualifier | map.get(key) |
| 包扫描 | @ComponentScan | 自动抓取组件并注册 |
| 启动类管理 | @Component | 自身也存入 Map |
5.2 容器结构示意
Spring 容器 = 大 Map
│
├── "user1" → User@7e5c856f
├── "user2" → User@413f69cc
├── "userDao" → UserDao@1f53a5dc
└── "application" → Application@...
5.3 易错点
| 容易踩的坑 | 后果 |
|---|---|
启动类忘了加 @Component | 事件监听失效,@Autowired 不生效 |
| 包扫描路径写错 | 组件注册不上,注入报错 |
多个同类型 Bean 不加 @Qualifier | 按类型注入冲突,容器启动失败 |
六、下一讲预告
第 3 讲将进入 Spring JDBC 最小案例,把 DataSource 和 JdbcTemplate 注册进容器,真正用 Spring 方式操作数据库。
更多推荐

所有评论(0)