当前位置: 首页 > news >正文

Spring框架03(上):Spring 框架开发程序的方式:从零搭建一个原生 JDBC + Druid 的 Spring 项目(纯配置文件形式)

一、前言

前面我们已经了解了 Spring IoC 容器、Bean 基础配置、依赖注入核心思想。

本篇承接上文,聚焦实际开发。在学习 Spring 的过程中,最经典的入门案例,就是搭建一个基于原生 JDBC和Druid 连接池的项目,来理解 Spring 最核心的 依赖注入(DI) 思想。本文就带你从零开始,完整实现这个案例。

二、Spring 开发核心思想回顾

Spring 核心两大核心:

  • IoC 控制反转: 对象不再由开发者手动 new 创建,全部交给 Spring 容器统一管理、实例化。

  • DI 依赖注入: 对象之间的依赖关系(属性、集合、引用类型),由 Spring 自动装配赋值,彻底解耦。

基于这两个思想,我们所有 Spring 程序,都遵循 「拆分代码 → 编写 Bean → XML 配置 → 容器加载 → 获取对象使用」 这套固定开发流程。

三、Spring 开发标准流程

1. 开发五步走

  • 引入 Spring 核心依赖;

  • 编写业务实体类、业务层、持久层 Java 代码;

  • 编写 Spring XML 配置文件,注册 Bean、完成依赖注入;

  • 加载 Spring 核心配置文件,创建 IoC 容器;

  • 从容器中获取 Bean 对象,调用方法完成业务逻辑。

2. 关键亮点

  • 业务代码只负责业务逻辑,不负责对象创建;

  • 所有对象、参数、集合数据,全部在配置文件中统一管理;

  • 支持多配置文件拆分,适配大型项目分层开发。

四、项目目标与技术栈

1. 目标

  • 不使用 MyBatis 等 ORM 框架,直接使用原生 JDBC 操作数据库。

  • 使用 Druid 作为数据库连接池。

  • 利用 Spring 的依赖注入,将连接池注入 DAO,再将 DAO 注入 Service,实现业务逻辑。

  • 体会 Spring 如何帮我们解耦对象依赖关系。

2. 技术栈

  • Maven:项目构建与依赖管理

  • Spring 5.x:核心容器、依赖注入

  • Druid:阿里开源的高性能数据库连接池

  • MySQL 8.x:关系型数据库

  • JUnit 4:单元测试

五、实战案例

5.1 创建 Maven 工程并引入依赖

(创建Maven项目不用勾选任何模板,直接next,包名为com.qcby,项目名为SpringLearning03)

首先,在 pom.xml 文件中,引入项目所需的所有依赖。

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qcby</groupId><artifactId>SpringLearning03</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>SpringLearning03 Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><!-- Spring 核心依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><!-- 日志依赖 --><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><!-- JUnit 单元测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- Druid 连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency><!-- MySQL 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency></dependencies><build><finalName>SpringLearning03</finalName><pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --><plugins><plugin><artifactId>maven-clean-plugin</artifactId><version>3.4.0</version></plugin><!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --><plugin><artifactId>maven-resources-plugin</artifactId><version>3.3.1</version></plugin><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.13.0</version></plugin><plugin><artifactId>maven-surefire-plugin</artifactId><version>3.3.0</version></plugin><plugin><artifactId>maven-war-plugin</artifactId><version>3.4.0</version></plugin><plugin><artifactId>maven-install-plugin</artifactId><version>3.1.2</version></plugin><plugin><artifactId>maven-deploy-plugin</artifactId><version>3.1.2</version></plugin></plugins></pluginManagement></build>
</project>

5.2 准备数据库与实体类

5.2.1 创建数据库与表

首先,在 MySQL 中创建数据库和表,并插入测试数据。

create database spring_db;
use spring_db;create table account(id int primary key auto_increment,name varchar(40),money double
) character set utf8 collate utf8_general_ci;insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);

5.2.2 编写 JavaBean 实体类

创建与数据库表对应的 Account 实体类。

image

package com.qcby.domain;import java.io.Serializable;public class Account implements Serializable {private static final long serialVersionUID = 7355810572012650248L;private Integer id;private String name;private Double money;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}

5.3 编写AccountDao接口和实现类

image

5.3.1 AccountDao接口

package com.qcby.dao;import com.qcby.domain.Account;
import java.util.List;public interface AccountDao {public List<Account> findAll();
}

5.3.2 AccountDao实现类

package com.qcby.dao;import com.qcby.domain.Account;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;public class AccountDaoImpl implements AccountDao {// 注入连接池对象private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}@Overridepublic List<Account> findAll() {List<Account> list = new ArrayList<>();Connection connection = null;PreparedStatement stmt = null;ResultSet rs = null;try {// 获取连接connection = dataSource.getConnection();// 编写sql语句String sql = "select * from account";// 预编译stmt = connection.prepareStatement(sql);// 查询rs = stmt.executeQuery();// 遍历,封装数据while (rs.next()){Account account = new Account();account.setId(rs.getInt("id"));account.setName(rs.getString("name"));account.setMoney(rs.getDouble("money"));list.add(account);}} catch (SQLException e) {e.printStackTrace();} finally {try {if (connection != null) connection.close();} catch (SQLException e) {e.printStackTrace();}try {if (stmt != null) stmt.close();} catch (SQLException e) {e.printStackTrace();}try {if (rs != null) rs.close();} catch (SQLException e) {e.printStackTrace();}}return list;}
}

5.4 编写 Service 层代码

image

5.4.1 编写 Service 接口

package com.qcby.service;import com.qcby.domain.Account;
import java.util.List;public interface AccountService {// 方法签名:返回值+方法名+参数,必须和实现类完全一致List<Account> findAll();
}

5.4.2 编写 Service 实现类

同样,Service 也通过 setter 方法接收 DAO 的注入。

package com.qcby.service;import com.qcby.dao.AccountDao;
import com.qcby.domain.Account;
import java.util.List;public class AccountServiceImpl implements AccountService {// 依赖注入private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}// 必须和接口里的方法签名完全一致,@Override 才生效@Overridepublic List<Account> findAll() {return accountDao.findAll();}
}

5.5 编写 Spring 核心配置文件

在 resources 目录下创建 applicationContext.xml,这是整个项目的灵魂。


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 配置连接池 --><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/spring_db?useSSL=false&amp;serverTimezone=UTC&amp;allowPublicKeyRetrieval=true"/><property name="username" value="root"/><property name="password" value="2020"/><!-- 连接超时设置,连不上就直接报错,不会一直卡住 --><property name="maxWait" value="3000"/><property name="timeBetweenEvictionRunsMillis" value="60000"/></bean><!-- 管理 DAO Bean --><bean id="accountDao" class="com.qcby.dao.AccountDaoImpl"><property name="dataSource" ref="dataSource"/></bean><!-- 管理 Service Bean --><bean id="accountService" class="com.qcby.service.AccountServiceImpl"><property name="accountDao" ref="accountDao"/></bean></beans>

这里的依赖关系非常清晰:

  • Spring 创建 dataSource(连接池)对象。

  • Spring 创建 accountDao 对象,并把 dataSource 注入给它。

  • Spring 创建 accountService 对象,并把 accountDao 注入给它。

5.5 编写测试程序

最后,编写 JUnit 测试类,验证整个流程是否通畅。

image

package com.qcbyjy.test;import com.qcbyjy.domain.Account;
import com.qcbyjy.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;public class Demo1 {@Testpublic void run1(){// 1. 加载 Spring 配置文件,创建工厂ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 从工厂中获取 Service 对象AccountService accountService = (AccountService) ac.getBean("accountService");// 3. 调用 Service 方法List<Account> list = accountService.findAll();// 4. 打印结果for (Account account : list) {System.out.println(account);}}
}

运行结果:

image

核心思想:控制反转(IoC)与依赖注入(DI)

  • 传统方式:Service 中 new AccountDaoImpl(),DAO 中 new DruidDataSource(),代码高度耦合。

  • Spring 方式:我们只定义接口和 setter 方法,对象的创建和依赖关系的维护,全部交给 Spring 容器来管理。

这种方式带来了巨大的好处:

  • 解耦:业务代码只依赖接口,不关心具体实现和对象创建。

  • 易扩展:如果要更换连接池(比如用 HikariCP),只需修改 XML 配置,无需修改任何 Java 代码。

  • 易测试:可以轻松地注入 Mock 对象,进行单元测试。

1. DAO 层(数据库访问层)

  • 接口:AccountDao
  • 实现类:AccountDaoImpl

2. Service 层(业务逻辑层)

  • 接口:AccountService
  • 实现类:AccountServiceImpl
Demo1(测试类)↓ 依赖
AccountService(接口)↓ 由 Spring 注入实现类
AccountServiceImpl(实现类)↓ 依赖
AccountDao(接口)↓ 由 Spring 注入实现类
AccountDaoImpl(实现类)↓ 操作
数据库
  • Service 层:依赖 AccountDao 接口,不直接依赖 AccountDaoImpl

  • 测试类:依赖 AccountService 接口,不直接依赖 AccountServiceImpl

这就是 Spring 「解耦」的核心体现:上层只认接口,不认具体实现类。

http://www.jsqmd.com/news/733184/

相关文章:

  • 关于华夏百川中频激光治疗仪相关负面报道的正式说明 - 野榜精选
  • 不只是看源码:用JD-GUI插件在IDEA里直接反编译依赖jar包
  • [开源] OpenTalking:整合 LLM、流式 TTS 与 WebRTC 的实时数字人编排框架
  • 保姆级教程:在YOLOv8中手把手替换BiFPN,并添加P2层提升小目标检测效果
  • 比亚迪DiLink 4.0车机Root实战:从固件提取到Magisk修补的保姆级避坑指南
  • 告别‘一病一药’:用PromptIR这个‘万能提示’模块,一个模型搞定图片去雾、去雨、去噪
  • 别再只用CBC了!聊聊OpenSSL AES ECB模式那些容易被忽略的坑(附C++实战代码)
  • 从Slack反推设计瓶颈:一个真实案例带你玩转Vivado Path Report
  • 保姆级教程:手把手教你将YOLOv8-Seg模型从PyTorch移植到C++推理引擎(附完整代码)
  • 从一次Samba挂载失败,聊聊Linux网络文件系统(CIFS/SMB)的版本兼容性与安全策略
  • 有效睡眠的本质的庖丁解牛
  • 从图像滤镜到推荐算法:Hadamard积和Kronecker积在AI项目里的‘隐藏’用法与性能调优
  • TVBoxOSC:打造你的全能电视盒子播放器终极指南
  • 2026年3月优秀的打包机企业口碑推荐,全自动打包机/手提式电动打包机/缠绕膜/彩色缠绕膜,打包机制造商有哪些 - 品牌推荐师
  • 麒麟系统桌面文件误删:数安寻搭建数据“重生”桥梁
  • 利用Taotoken模型广场为不同文本处理任务选择合适的模型
  • 3分钟学会:用stl-thumb为STL文件生成精美缩略图
  • 《OpenClaw本地知识库优化:从导入到优先调用指南》
  • 别再空谈4R了!用Notion或飞书搭建你的第一个客户关系管理看板(附模板)
  • Translumo:打破语言壁垒的智能屏幕翻译神器
  • 【学习笔记】Grader交互
  • 终极指南:如何用TMSpeech实现Windows本地实时语音转文字
  • Dify 2026多模态集成权威拆解:基于23家头部客户POC数据的延迟/吞吐/准确率三维基准测试报告(含可复现benchmark脚本)
  • 别再到处找Modbus主机库了!一个头文件搞定STM32CubeMX下的RTU主站通信
  • 微信同款存储引擎MMKV实战:从mmap原理到Protobuf编码,一次搞懂高性能背后的秘密
  • 告别弹窗卡顿!Android BottomSheetBehavior 性能优化与避坑实战(附完整代码)
  • 长期使用Taotoken服务感受到的API调用稳定性与技术支持响应
  • 告别激活烦恼:KMS_VL_ALL_AIO如何用一行命令解决Windows和Office激活难题
  • python papermill
  • 3步让小爱音箱变身AI语音助手:MiGPT完整指南