注:本文出處: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); } } }