Class.forName加载数据库驱动深入探讨

最近写了个监控系统,需要从各个数据库中获取数据,为了可配行,各个数据库的信息(数据库类型,用户名,密码等)都存放在我们自己的数据库中,

暂时想到的方法就是直接用JDBC代码访问数据库,取数据,然后将数据库连接进行池化。

唉,以前都是J2EE开发,用spring配置使用,这次直接使用JDBC还不太习惯了,OK,不废话了

JDBC步骤:

1.加载数据驱动

 

 

Class.forName("com.mysql.jdbc.Driver");

 

  2. 得到数据库连接

 

 

DriverManager.getConnection(url, user, password);

 

  当时到这步就产生两个疑问:

1. 为什么加载了数据库驱动,就能得到数据库连接,看上面的两行代码,是看出来两者之间联系

2. 除了Class.forName()加载数据库驱动,貌似没有看到过其他加载数据库驱动方式,是否有其他方式?为什么不用其他方式?

OK,问题出来了,就看看问题

 

问题1:

后来结合数据库驱动源代码和JDBC规范了解到,每个数据库驱动,都必须在加载的时候,自己实现数据库驱动注册(注册?貌似前面观察者模式就有注册行为,但是数据库驱动却没有update行为,所以不存在通知)

mysql驱动源代码:

 

 

public class Driver extends NonRegisteringDriver
  implements java.sql.Driver
 {
   public Driver()
    throws SQLException
   {
   }
 
   static
   {
    try
    {
       DriverManager.registerDriver(new Driver());
     } catch (SQLException E) {
      throw new RuntimeException("Can't register driver!");
     }
   }
 }

 

 

DriverManager.registerDriver()代码如下:

 

 

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);
	
	/* 由于可以注册多个数据库驱动类型,这里更新下数据库驱动 */
	readDrivers = (java.util.Vector) writeDrivers.clone();

    }
 

 

 

 

这样在DriverManager.getConnection()的时候,就可以用到数据库驱动,得到连接了,代码如下

 

 

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));
    }



private static Connection getConnection(
	String url, java.util.Properties info, ClassLoader callerCL) throws SQLException {
	java.util.Vector drivers = null;
        /*
	 * 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.
	 */
	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 + "\")");
    
	if (!initialized) {
	    initialize();
	}

	synchronized (DriverManager.class){ 
            // 得到注册的驱动
	    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 ( 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;
		}
	    }
	}
    
	// 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");
    }
 

OK,到此问题的答案算是明白了

 

下面,问题2:

加载类的方式几种,目前我想到的就下面几种,不够以后在补充,呵呵:

1. Class.forName()

2. Class.forName().newInstance();

3. new

4.Class.class

5.Class.class.newInstance();

6.Thread.currentThread().getContextClassLoader().loadClass()

可以知道方法2和5都是有点多余,加载的类,还实例化了一个对象出来,可以Pass掉。而3跟前面一个问题一样,实例化了一个对象出来,多此一举,此对象还占据内存。

OK,还剩下三种方法1.4.6,先来分析1和6的区别

 

Class.forName()的源代码如下:

 

 

public static Class<?> forName(String className) 
                throws ClassNotFoundException {
        return forName0(className, true, ClassLoader.getCallerClassLoader());
    }

  调用此方法等效于:

 

  Class.forName(className, true, currentLoader)

第二次参数表示装载类的时候是否初始化该类, 即调用类的静态块的语句及初始化静态成员变量。

 

 

Thread.currentThread().getContextClassLoader().loadClass()的代码如下:

 

public Class<?> loadClass(String name) throws ClassNotFoundException {
	return loadClass(name, false);
    }
 

 

调用此方法等效于调用 loadClass(name,false)

 

 

 

protected synchronized Class<?> loadClass(String name, boolean resolve)
	throws ClassNotFoundException
    {
	// First, check if the class has already been loaded
	Class c = findLoadedClass(name);
	if (c == null) {
	    try {
		if (parent != null) {
		    c = parent.loadClass(name, false);
		} else {
		    c = findBootstrapClassOrNull(name);
		}
	    } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            if (c == null) {
	        // If still not found, then invoke findClass in order
	        // to find the class.
	        c = findClass(name);
	    }
	}
	if (resolve) {
	    resolveClass(c);
	}
	return c;
    }
 

 

 

 

此方法使用指定的 二进制名称来加载类。此方法的默认实现将按以下顺序搜索类:

 

  1. 调用 findLoadedClass(String) 来检查是否已经加载类。

  2. 在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。

  3. 调用 findClass(String) 方法查找类。

如果使用上述步骤找到类,并且 resolve 标志为真,则此方法将在得到的 Class 对象上调用 resolveClass(Class) 方法。

 

参数:
resolve - 如果该参数为 true,则分析这个类 

 

所以此方法没有初始化类,即没有加载类的静态代码,所以此方法不行

 

 

OK,下面只剩下方法1和4了,方法4加载类会获得编译检查,可以保证此类一定存在,所以无法做到运行时动态加载未知数据库驱动,并且Class.class前面还必须给一个变量赋值才行: Class cc = Class.class

所以加载数据库,大家看到的都是方法1。

好了,到这,收工,呵呵。。。。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值