`

JDBC4.0 驱动加载过程

    博客分类:
  • J2SE
阅读更多

 

注意,jdbc4.0不再需要显示调用class.forName()注册驱动,而是自动调用驱动jar包下META-INF\services\java.sql.Driver文本中的类名称去注册,DriverManager是一个单例类。

参见如下简单的程序

String url ="jdbc:mysql://localhost:3306/jdbcana";       
String username = "root";  
String password = "root";  
Connection con = DriverManager.getConnection(url, username, password); 

 DriverManager.getConnection(url, username, password)会返回Connection,看源码:

    public static Connection getConnection(String url,   
        String user, String password) throws SQLException {  
            java.util.Properties info = new java.util.Properties();  
      
            // Gets the classloader of the code that called this method, may   
        // be null.  
        ClassLoader callerCL = DriverManager.getCallerClassLoader();  
      
        if (user != null) {  
            info.put("user", user);  
        }  
        if (password != null) {  
            info.put("password", password);  
        }  
      
            return (getConnection(url, info, callerCL));  
    }  

接下来调用getConnection(url, info, callerCL)方法,这里面会调用initialize()->loadInitialDrivers();方 法(如果使用了class.forName注册驱动,则initialize()在注册时已被调用,这里不会再执行)。

loadInitialDrivers()方法中会首先尝试从System.getproperty中获取设置的jdbc.drivers属性,没设置就不管了,代码如下:

private static void loadInitialDrivers() {
        String drivers;
	
        try {
	    drivers = (String) java.security.AccessController.doPrivileged(
		new sun.security.action.GetPropertyAction("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.
	
	 DriverService ds = new DriverService();

	 // Have all the privileges to get all the 
	 // implementation of java.sql.Driver
	 java.security.AccessController.doPrivileged(ds);		
	        
         println("DriverManager.initialize: jdbc.drivers = " + drivers);
        if (drivers == null) {
            return;
        }
        while (drivers.length() != 0) {
            int x = drivers.indexOf(':');
            String driver;
            if (x < 0) {
                driver = drivers;
                drivers = "";
            } else {
                driver = drivers.substring(0, x);
                drivers = drivers.substring(x+1);
            }
            if (driver.length() == 0) {
                continue;
            }
            try {
                println("DriverManager.Initialize: loading " + driver);
                Class.forName(driver, true,
			      ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }
 上面代码中DriverService是一个内部类,实现了java.security.PrivilegedAction接口,代码如下:
class DriverService implements java.security.PrivilegedAction {
        Iterator ps = null;
	public DriverService() {};
        public Object run() {

	// uncomment the followin line before mustang integration 	
        // Service s = Service.lookup(java.sql.Driver.class);
	// ps = s.iterator();

	ps = Service.providers(java.sql.Driver.class);

	try {
           while (ps.hasNext()) {
               ps.next();
           } // end while
	} catch(Throwable t) {
	    // Do nothing
	}
        return null;
    } //end run

} 
 

上面代码会调用Service.providers(Driver.class);方法,他其实是从String str = "META-INF/services/" + this.service.getName();中加载类,具体看代码:

private static class LazyIterator implements Iterator {
 
     Class service;
     ClassLoader loader;
     Enumeration configs = null;
     Iterator pending = null;
     Set returned = new TreeSet();
     String nextName = null;
 
     private LazyIterator(Class service, ClassLoader loader) {
         this.service = service;
         this.loader = loader;
     }
 
     public boolean hasNext() throws ServiceConfigurationError {
         if (nextName != null) {
         return true;
         }
         if (configs == null) {
         try {
             String fullName = prefix + service.getName();
             if (loader == null)
             configs = ClassLoader.getSystemResources(fullName);
             else
             configs = loader.getResources(fullName);
         } catch (IOException x) {
             fail(service, ": " + x);
         }
         }
         while ((pending == null) || !pending.hasNext()) {
         if (!configs.hasMoreElements()) {
             return false;
         }
         pending = parse(service, (URL)configs.nextElement(), returned);
         }
         nextName = (String)pending.next();
         return true;
     }
 
     public Object next() throws ServiceConfigurationError {
         if (!hasNext()) {
         throw new NoSuchElementException();
         }
         String cn = nextName;
         nextName = null;
         try {
         return Class.forName(cn, true, loader).newInstance();
         } catch (ClassNotFoundException x) {
         fail(service,
              "Provider " + cn + " not found");
         } catch (Exception x) {
         fail(service,
              "Provider " + cn + " could not be instantiated: " + x,
              x);
         }
         return null;    /* This cannot happen */
     }
 
     public void remove() {
         throw new UnsupportedOperationException();
     }
 
     }
 上面代码调用了return Class.forName(str, true, this.loader).newInstance();装载连接初始化并生产驱动类实例,即自动将驱动注册到DriverManager中的一个列表中。

 

下面做一个简单测试读取META-INF/services的驱动,代码如下:

Enumeration<URL> list = ClassLoader.getSystemResources("META-INF/services/" + Driver.class.getName());
         while (list.hasMoreElements()) {
             System.out.println(list.nextElement());
         }
 控制台输出:
jar:file:/usr/local/glassfish3/jdk7/jre/lib/resources.jar!/META-INF/services/java.sql.Driver
jar:file:/home/alexis/mysql-connector/mysql-connector-java-5.1.22-bin.jar!/META-INF/services/java.sql.Driver
 这两个jar文件一个是jdk自带的,另一个是我们自己加到环境变量里的mysql驱动,然后我们再看看这两个java.sql.Driver里的东西,就是驱动类名称了。

 

 下面来看看驱动被自动加载,是如何自动注册的,查看驱动类文件,会发现都有这样一段static代码块,如下:

static {
        try {
             java.sql.DriverManager.registerDriver(new Driver());
         } catch (SQLException E) {
             throw new RuntimeException("Can't register driver!");
         }
     }
 它调用了单例类DriverManager.registerDriver(Driver);向驱动管理器注册了驱动,代码如下:
public static synchronized void registerDriver(java.sql.Driver driver)
	throws SQLException {
	if (!initialized) {
	    initialize();
	}
      
	DriverInfo di = new DriverInfo();

	di.driver = driver;
	di.driverClass = driver.getClass();
	di.driverClassName = di.driverClass.getName();

	// Not Required -- drivers.addElement(di);

	writeDrivers.addElement(di); 
	println("registerDriver: " + di);
	
	/* update the read copy of drivers vector */
	readDrivers = (java.util.Vector) writeDrivers.clone();

    }
 从上面代码知道,驱动类被赋给值对象,值对象被存入一个同步列表中,再看initialize()后面的代码如下:
synchronized (DriverManager.class){ 
            // use the readcopy of drivers
	    drivers = readDrivers;  
        }

	// Walk through the loaded drivers attempting to make a connection.
	// Remember the first exception that gets raised so we can reraise it.
	SQLException reason = null;
	for (int i = 0; i < drivers.size(); i++) {
	    DriverInfo di = (DriverInfo)drivers.elementAt(i);
      
	    // If the caller does not have permission to load the driver then 
	    // skip it.
	    if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {
		println("    skipping: " + di);
		continue;
	    }
	    try {
		println("    trying " + di);
		Connection result = di.driver.connect(url, info);
		if (result != null) {
		    // Success!
		    println("getConnection returning " + di);
		    return (result);
		}
	    } catch (SQLException ex) {
		if (reason == null) {
		    reason = ex;
		}
	    }
	}
 初始化完成之后,从vector中拿出DriverInfo,然后取出driver,调用connect方法, 这个方法会解析url,如果符合当前驱动的规则,那么就用当前驱动,比如如果传入的是jdbc:mysql://localhost:3306 /jdbcana就可以解析出来是用mysql jdbc 驱动,其中getCallerClass(callerCL, di.driverClassName)代码如下:
private static Class getCallerClass(ClassLoader callerClassLoader, 
					String driverClassName) {
	Class callerC = null;

	try {
	    callerC = Class.forName(driverClassName, true, callerClassLoader);
	}
	catch (Exception ex) {
	    callerC = null;           // being very careful 
	}

	return callerC;
    }

 实际上是返回给定字符串名类或接口的Class对象使用给定的类加载器,检查是不是被当前加载器加载的类。

 

 曾经有一个面试题:同时注册多个驱动,问得到的connection是哪一个数据库的,为什么?

 看到这里答案已经解开,它是循环取出每个注册的驱动与URL匹配,只返回成功连接的那个。

 

最后总结一下流程:

1. 调用 getConnection()。

2. DriverManager 通过  SystemProerty jdbc.driver 获取数据库驱动类名

或者

通过ClassLoader.getSystemResources 去CLASSPATH里的类信息里查找 META-INF/services/java.sql.Driver 这个文件里获取数据库驱动名。

3. 通过找的的driver名对他们进行类加载。

4. Driver类在被加载的时候执行static语句块,将自己注册到DriverManager里去。

5. 注册完毕后 DriverManager 调用这些驱动的connect方法,将合适的Connection 返回给客户端。

分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    数据库驱动sqljdbc4.0

    1. 加载驱动:使用`Class.forName()`方法加载SQLJDBC4.0驱动。 ```java Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); ``` 2. 创建连接:通过`DriverManager.getConnection()`方法创建数据库连接...

    sqljdbc4.0.zip

    《SQL JDBC 4.0驱动:为开发者带来的便利与详细解析》 在Java开发中,数据库连接是不可或缺的一部分,而Microsoft SQL Server作为广泛使用的数据库系统,其对应的JDBC驱动是连接Java应用程序与SQL Server的关键。...

    sqljdbc4.0

    标题“sqljdbc4.0”指的是SQL Server JDBC驱动的一个特定版本,主要用于Java应用程序与Microsoft SQL Server数据库之间的连接。SQL JDBC是微软提供的一个开源驱动,它实现了Java Database Connectivity (JDBC) API,...

    数据库jdbc驱动加载过程

    JDBC 驱动加载过程详解 数据库 JDBC 驱动加载过程是指在 Java 应用程序中加载和注册数据库 JDBC 驱动程序的过程,以便于应用程序连接到数据库并进行数据交互。下面是对 JDBC 驱动加载过程的详细解释。 JDBC 驱动...

    sqljdbc4.0jar文件

    `sqljdbc4.0.jar` 文件是微软公司提供的一个Java数据库连接驱动,它允许Java应用程序通过JDBC(Java Database Connectivity)接口与SQL Server数据库进行通信。这个驱动是Microsoft SQL Server JDBC Driver的一个...

    JDBC4.0 API一览表

    JDBC4.0支持零配置驱动,意味着只需将驱动的JAR文件放入`CLASSPATH`,无需在`jdbc.drivers`系统属性中显式注册,系统就能自动加载驱动。 **5. 新的数据库元数据API** JDBC4.0增强了`DatabaseMetaData`接口,添加了...

    spring-jdbc4.0jar包

    这个"spring-jdbc4.0.jar"包是针对Spring框架4.0版本的JDBC支持组件,包含了一系列接口和类,用于简化数据库的访问过程。 首先,Spring JDBC的核心类`JdbcTemplate`是其主要功能的入口点。这个模板类提供了大量的...

    sqljdbc_4.0

    以下是关于SQLJDBC_4.0驱动的详细知识点: 1. JDBC接口:JDBC(Java Database Connectivity)是Java平台的标准API,允许Java程序与各种关系型数据库进行交互。SQLJDBC_4.0驱动是实现这一接口的微软特定版本,使得...

    JDBC驱动4.0

    但JDBC 4.0引入了服务提供者接口(SPI),使得驱动可以通过`META-INF/services/java.sql.Driver`文件自动注册,简化了初始化过程。 **Connection接口的增强** - `autoReconnectForPools`属性:为连接池提供自动重新...

    jdbc4.0 sql server连接包

    这个“jdbc4.0 sql server连接包”提供了针对SQL Server数据库的JDBC驱动,使得Java开发者能够方便地在SQL Server上执行SQL语句、管理数据和事务。 **sqljdbc4.jar与sqljdbc.jar** 这两个jar包都是由Microsoft官方...

    sqljdbc 3.0及4.0

    将这些JAR文件添加到Java项目的类路径中,就可以在代码中使用`Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver")`来加载驱动,然后通过`DriverManager.getConnection()`方法建立与SQL Server的连接。...

    sqljdbc4.0-4.2.zip

    5. 使用方法:在Java项目中,要使用SQLJDBC驱动,首先需要将对应的JAR文件(如sqljdbc4.0-4.2.zip中的文件)添加到项目的类路径中。然后,通过`Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver")`加载...

    sqljdbc4-4.0.jar

    sqljdbc4-4.0.jar驱动使得Java开发者能够利用这些特性,方便地执行SQL查询,执行存储过程,以及进行事务管理等操作。 在使用"sqljdbc4-4.0.jar"之前,开发者需要将其添加到项目的类路径中,这样Java程序才能找到并...

    sqljdbc_4.0.2206.100_chs

    "sqljdbc_4.0.2206.100_chs" 是一个针对中文用户的SQL Server JDBC驱动程序的版本号,它属于Microsoft SQL Server的Java数据库连接(JDBC)驱动程序。JDBC驱动是Java程序员用来与SQL Server进行交互的桥梁,允许Java...

    Microsoft JDBC Driver 4.0 for SQL Server

    1. `sqljdbc_4.0.2206.100_chs.exe` 是Windows环境下的安装程序,用户可以通过执行这个文件将驱动添加到系统路径,方便在Java应用中自动发现和加载。 2. `sqljdbc_4.0.2206.100_chs.tar.gz` 是适用于Linux和Unix等...

    maven 下载 sqljdbc4-4.0.jar

    1. 引入JDBC驱动:在Java代码中,我们需要通过`Class.forName()`方法加载驱动: ```java Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); ``` 2. 创建连接:使用`DriverManager.getConnection()`...

    JDBC Driver 4.0 for SQL Server

    在实际开发中,开发者需要下载并添加Microsoft JDBC Driver 4.0的jar文件到项目的类路径中,然后通过`Class.forName()`方法加载驱动,使用`DriverManager.getConnection()`创建数据库连接。此外,还需要了解如何编写...

    access的jdbc驱动

    使用JDBC驱动时,首先需要加载驱动,然后创建数据库连接,接着可以通过Statement或PreparedStatement对象执行SQL,最后关闭连接。对于Access数据库,需要特定的JDBC-ODBC桥接驱动或者由第三方提供的直接JDBC驱动。 ...

Global site tag (gtag.js) - Google Analytics