JDBC的驱动是如何加载的
注:本文出处:http://www.cnblogs.com/jiaoyiping/ 转载请保留出处
JDBC定义了一套接口,数据库产品的提供商会实现这些接口来提供自己的数据库驱动程序,这是个很好的面向接口编程的实例,想要替换数据库的时候只需要替换驱动程序就可以了(这里暂不考虑不同数据库之间的数据类型和SQL语法的差异)
那么针对具体的一款数据库(以PostgreSQL为例)是如何初始化的呢?
我们在使用原生的JDBC的时候都会写以下的代码:
Class.forName("org.postgresql.Driver");
Collection conn = DriverManager.getCollection("URL","username","password");
这两行代码就做了什么工作呢?
驱动又是如何加载的呢?我们知道,Class.forName()会导致类的初始化(
1.根据传入的类的完全限定名加载相应的class文件,
2.验证字节码并为类的静态域分配存储空间,
3.为静态属性设置值、执行静态代码块儿等 )
驱动的注册就是在静态代码块儿中执行的,以PostgreSQL9.3的Driver为例:
public class Driver implements java.sql.Driver
{ // make these public so they can be used in setLogLevel below public static final int DEBUG = 2;
public static final int INFO = 1;
public static final int OFF = 0; private static final Logger logger = new Logger();
private static boolean logLevelSet = false;
private static Timer cancelTimer=null; static
{
try
{
// moved the registerDriver from the constructor to here
// because some clients call the driver themselves (I know, as
// my early jdbc work did - and that was based on other examples).
// Placing it here, means that the driver is registered once only.
java.sql.DriverManager.registerDriver(new Driver());
}
catch (SQLException e)
{
e.printStackTrace();
}
}
DriverManager的Register方法:
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException { registerDriver(driver, null);
}
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException { /* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
} println("registerDriver: " + driver); }
DriverManager.getCollection()方法会遍历已经注册到系统中的驱动,调用驱动中相应的方法来得到真正的数据库连接。
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
} if(url == null) {
throw new SQLException("The url cannot be null", "08001");
} println("DriverManager.getConnection(\"" + url + "\")"); // Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null; for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
} } else {
println(" skipping: " + aDriver.getClass().getName());
} } // if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
} println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
} }
自JDBC4.0开始,Class.forName("");可以省略掉了,因为在DriverManager的静态代码块儿里会寻找 jdbc.drivers 这个系统变量,找到相应的驱动程序并使用Class.forName()来加载它
详细代码如下:
/**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
JDBC的驱动是如何加载的的更多相关文章
- Kernel启动时 驱动是如何加载的module_init,加载的次序如何;略见本文
Init.h中有相关initcall的启动次序,在system.map中可看出具体的__initcall指针的前后次序 #define pure_initcall(fn) __define_initc ...
- Linux 设备驱动的固件加载【转】
转自:http://blog.csdn.net/zqixiao_09/article/details/51106663 版权声明:本文为博主原创文章,未经博主允许不得转载. 作为一个驱动作者, 你可能 ...
- Linux驱动之内核加载模块过程分析
Linux内核支持动态的加载模块运行:比如insmod first_drv.ko,这样就可以将模块加载到内核所在空间供应用程序调用.现在简单描述下insmod first_drv.ko的过程 1.in ...
- 如何单独编译Linux内核源码中的驱动为可加载模块?
答: 分为两步: 1. 配置某个驱动为模块(如: CONFIG_RTC_XXX=m) 2. 指定路径并编译, 如编译drivers/rtc中的驱动 make SUBDIRS=drivers/rtc m ...
- JDBC源码分析(加载过程)
public static void main(String[] args) { String url = "jdbc:mysql://172.16.27.11:3306/jdbcT ...
- 如何在真实串口驱动还未加载的情况下调试uboot?
1. 先找出真实串口是什么型号 1.1 怎么找?笔者提供两种方案: 方案一: 若当前的板子支持dm,从uboot的dts找串口节点对应的compatible属性 方案二: 从linux内核的dts找串 ...
- 分享知识-快乐自己:Maven 无法加载 Oracle 数据库驱动源
由于Oracle授权问题,Maven3不提供Oracle JDBC driver,为了在Maven项目中应用Oracle JDBC driver,必须手动添加到本地仓库. 手动添加到本地仓库需要本地有 ...
- openwrt系统之字符设备驱动软件包加载、测试程序加载
.首先将软件包(如mydrv)放到ubuntu虚拟机openwrt/trunk/package/kernel/目录下 .回到openwrt/trunk/目录下,make menuconfig进行配置, ...
- linux 保留内核中sas驱动的加载导致crash问题
[root@localhost ~]# uname -a Linux localhost.localdomain -.el7.x86_64 问题描述,在crash的时候,小内核因为分配中断号失败而触发 ...
随机推荐
- LNMP架构介绍 MySQL安装 PHP安装 Nginx介绍
- php无限分类二
1.读取数据数据 /** * 所有分类数据 * */ public function categoryData(){ $order = 'id,sort asc'; $res = $this-> ...
- CorelDRAW中关于锁定与解锁对象的操作
在编辑复制的图形时,有时为了避免对象受到操作的影响,可以使用“锁定与解锁对象”功能键对已经编辑好的对象进行锁定.被锁定的对象将不能进行任何编辑操作,本教程将详解CorelDRAW中关于锁定与解锁对象的 ...
- 详解BarTender选项大小调整模式
BarTender大小调整模式是DotCode码制独有的符号体系特殊选项.DotCode 符号可能在形状上有所不同,包括从接近正方形的点阵到细长的色带,而“大小调整模式”选项通过指定点阵的配置来确定 ...
- jquery ajax 请求中 中文汉字的 转码问题
1.汉字参数直接跟在请求连接的后面,这样需要使用encodeURIComponent(fileName)或者encodeURI(fileName)转码两次 后台使用URLDecoder.decode( ...
- NHibernate 集合映射深入 (第五篇) <set>,<list>,<map>,<bag>
一.集合外键 在NHibernate中,典型的用于映射集合类的元素有<set>,<list>,<map>,<bag>,<array>,< ...
- Window系统、主函数和窗体函数这三者之间的关系
理解Window系统.主窗体.窗体函数这三者之间的关系,对于编写Windows程序十分重要. 主函数和窗体函数都是由Windows系统来调用的函数.仅仅只是主函数是程序启动之后.系统首先调用的函数: ...
- phonegap入门–3 Android phonegap 自定义插件DEMO
一.环境要求: 首先需要建立phonegap android 工程,请参考:http://www.cnblogs.com/zhujinguo/p/4369883.html 二.建立java类 ...
- [转]mac osx 下的apt-get,yum的代替工具 ----homebrew
原文地址:http://blog.csdn.net/tsxw24/article/details/15500517 linux下有很方便的包管理器如:apt-get.yum,mac下也有类似的工具:H ...
- Python easyGUI 文件对比 覆盖保存
#在35-3的基础上进行优化,当用户点击ok按钮的时候,对打开的文件进行检查是否修改.# 如果修改过,则提示覆盖保存.放弃保存.另存为并实现相应的功能 1 import easygui as g im ...