JDBC必须用反射加载驱动,因为Java规定Driver实现类需通过Class.forName()触发静态代码块完成向DriverManager的注册,这是JDBC规范强制要求的初始化方式;反射支持多厂商驱动(如MySQL 8.x用com.mysql.cj.jdbc.Driver、Oracle用oracle.jdbc.driver.OracleDriver)统一入口,切换数据库只需改配置,无需修改源码。

反射让JDBC驱动类能在运行时按需加载,不依赖编译期绑定;连接池则复用Connection对象,避免反复创建销毁开销。二者配合,既解耦了数据库类型,又提升了并发访问效率。
为什么JDBC必须用反射加载驱动
Java规定:Driver实现类在首次被Class.forName()加载时,会自动执行其静态代码块,完成向DriverManager的注册。这是JDBC规范强制要求的初始化方式。
- MySQL 5.x驱动类是com.mysql.jdbc.Driver,8.x改为com.mysql.cj.jdbc.Driver——若硬编码,换版本就得改源码
- Oracle、PostgreSQL等厂商驱动类名完全不同,靠反射才能统一入口
- 配置文件中只写
jdbc.driver=com.mysql.cj.jdbc.Driver,程序读取后通过Class.forName动态加载,切换数据库只需改配置
反射如何参与连接池初始化
主流连接池(如HikariCP、Druid、C3P0)在构建数据源时,都需要显式指定driver-class-name。这个字符串最终仍要经由反射转为Class对象,再用于验证和实例化。
- HikariCP内部调用
Class.forName(driverClassName)校验类是否存在,失败则抛出异常 - Druid在
initFromProperties()阶段解析配置后,同样用反射加载驱动类,并缓存Class引用 - 即使使用Spring Boot的
spring.datasource.driver-class-name,底层仍是通过反射触发驱动注册
配置注入的实际落地方式
真正解耦的关键不在“是否用反射”,而在于“配置从哪来、何时加载、如何生效”。常见可靠做法:
- 把driver、url、username、password全放在
application.properties或config.properties里,启动时由工具类(如DbUtil)用Props或ResourceBundle读取 - 在static代码块中完成
Class.forName(props.get("jdbc.driver")),确保类加载即注册,早于任何getConnection调用 - 连接池配置(如maxPoolSize、connection-timeout)也走同一套配置体系,避免硬编码与配置分散
注意反射失效的典型场景
不是所有反射调用都成功。以下情况会导致Class.forName失败或驱动未注册:
- JAR包未进classpath:Maven依赖scope写成
test,或IDE未刷新依赖 - 类名拼写错误:比如把
cj漏掉写成com.mysql.jdbc.Driver(MySQL 8+已废弃) - 模块系统限制:Java 9+模块化后,若未在
module-info.java中声明requires java.sql,反射可能受限 - 驱动jar与JVM版本不兼容:例如用JDK 17运行仅支持JDK 8的旧版mysql-connector-java 5.1.x










