serviceloader通过扫描meta-inf/services/java.sql.driver文件自动加载数据库驱动,要求接口为java.sql.driver、路径严格匹配、每行仅一个无空格类名;drivermanager在静态块中调用serviceloader.load(driver.class)初始化并依次验证acceptsurl,首个返回true的驱动生效。

ServiceLoader 是 JDBC 实现“零硬编码驱动加载”的底层支柱。它让 java.sql.Driver 这个标准接口和 MySQL、Oracle、PostgreSQL 等厂商的具体实现彻底分离——你写代码时完全不用 import 任何一个驱动类,也不用调 Class.forName(),DriverManager 就能自动找到并注册可用驱动。
它怎么做到不写驱动类名也能连上数据库?
靠的是约定大于配置的资源路径机制:
- JDBC 规范定义了服务接口
java.sql.Driver - 每个数据库驱动 jar 包里,必须在
META-INF/services/java.sql.Driver文件中,写入自己实现类的全限定名(如com.mysql.cj.jdbc.Driver) - DriverManager 内部调用
ServiceLoader.load(Driver.class),按这个固定路径去 classpath 下扫描所有匹配文件 - 读出类名后,用当前线程上下文类加载器反射构造实例,并完成注册
为什么说这是真正的解耦?
对比两种写法就清楚了:
-
硬编码方式:你得在代码里写
Class.forName("com.mysql.cj.jdbc.Driver"),一旦换数据库,改代码、重新编译、发版 -
ServiceLoader 方式:只依赖
java.sql.*标准 API;换库只需替换 jar 包 + 确保其 META-INF/services 文件存在且正确;应用代码一行不动 - 多个驱动共存时,DriverManager 会全部加载,根据 URL 协议(
jdbc:mysql://或jdbc:oracle://)自动路由到对应实现
常见踩坑点,直接影响驱动是否生效
不是放个 jar 就行,路径、格式、类加载器都必须对:
- 文件位置必须是
resources/META-INF/services/java.sql.Driver(不是 src/main/java 下,也不是用斜杠分隔的包路径) - 文件名大小写敏感:Linux 环境下写成
Java.Sql.Driver或java/sql/Driver都会失败 - 文件内容每行一个实现类,不能有空格、制表符、BOM 头(Windows 记事本易悄悄加 BOM)
- Web 应用中,若 Tomcat 重置了线程上下文类加载器,而驱动 jar 在 WEB-INF/lib 下,ServiceLoader 可能找不到它——需显式传入正确的 ClassLoader
它和 Spring 的 @Autowired 本质不同
ServiceLoader 不是 Spring 特有的,它是 JVM 层原生能力:
- 不依赖任何容器,纯 JDK 提供,Java 6 起就存在
- 不做依赖注入、不管理 Bean 生命周期、不支持 AOP 或作用域
- 只做一件事:按约定路径发现类 → 反射构造 → 返回迭代器;其余逻辑(比如 Driver 如何注册进 DriverManager)由各实现自己在静态块或初始化时完成
- Spring Boot 的
spring.factories是受 ServiceLoader 启发的增强版,但机制独立,两者可并存











