接上一篇JDBC解析:http://blog.csdn.net/x_iya/article/details/70670342
一个简单的Demo:
package cn.bjut.test;
import java.sql.*;
import java.io.PrintWriter;
/**
* Created by N3verL4nd on 2017/4/18.
*/
public class jdbc {
public static void main(String[] args) {
String driverClass = "com.mysql.jdbc.Driver";
String jdbcUrl = "jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8&useSSL=true";
String user = "root";
String password = "lgh123";
String sql = "SELECT * FROM persons";
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
/*
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
*/
try {
conn = DriverManager.getConnection(jdbcUrl, user, password);
System.out.println(conn);
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getInt(1)+ " " + rs.getString(2) + " " + rs.getInt(3));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
到底需不需要添加加载驱动的代码:
try { Class.forName(driverClass); } catch (ClassNotFoundException e) { e.printStackTrace(); }我们先来看一下输出。为了方便查看,执行SQL语句的给注释掉了。
不加:
DriverManager.getConnection("jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8&useSSL=true")
trying com.mysql.jdbc.Driver
getConnection returning com.mysql.jdbc.Driver
com.mysql.jdbc.JDBC4Connection@5ef04b5
加:
DriverManager.getConnection("jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8&useSSL=true")
trying com.mysql.fabric.jdbc.FabricMySQLDriver
trying com.mysql.jdbc.Driver
getConnection returning com.mysql.jdbc.Driver
com.mysql.jdbc.JDBC4Connection@5ef04b5
如上所示,加和不加Class.forName()我们都获得到了JDBC Connection对象。
加载驱动的代码导致了以上输出顺序的不同。
分析:
conn = DriverManager.getConnection(url, username, password);导致DriverManager的静态代码块的执行。
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); } } }
System.getProperty("jdbc.drivers")需要配合java -Djdbc.drivers="driver1:driver2"使用。
所以核心的代码只有:
AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { //通过读取META-INF/services的java.sql.Driver来加载驱动 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();//也就是执行new Driver(); } } catch(Throwable t) { // Do nothing } return null; } });
查看DriverManager的API:
DriverManager 类的方法 getConnection 和 getDrivers 已经得到提高以支持 Java Standard Edition Service Provider 机制。 JDBC 4.0 Drivers 必须包括 META-INF/services/java.sql.Driver 文件。此文件包含 java.sql.Driver 的 JDBC 驱动程序实现的名称。例如,要加载 my.sql.Driver 类,META-INF/services/java.sql.Driver 文件需要包含下面的条目:
my.sql.Driver
应用程序不再需要使用 Class.forName() 显式地加载 JDBC 驱动程序。当前使用 Class.forName() 加载 JDBC 驱动程序的现有程序将在不作修改的情况下继续工作。
也就是说Service Provider(java.util.ServiceLoader)机制会自动加载java.sql.Driver文件中的驱动程序。
是不是有点IOC的味道了?
通过如下调试:
我们发现ServiceLoader创建了com.mysql.driver.Driver对象,也导致了Driver静态代码块的执行:
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
再做个测试:
当我们把该文件(java.sql.Driver)删除掉(mysql.jar)
不使用SPI机制,则需要我们使用Class.forName来加载驱动。
而最开始的输出差异,我认为是数据库驱动多次装入导致的。class.forName装载一次驱动,SPI机制装载一次驱动。
// List of registered JDBC drivers private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
SPI介绍:
http://www.cnblogs.com/javaee6/p/3714719.html
http://blog.csdn.net/hintcnuie/article/details/37922089
http://blog.csdn.net/kokojhuang/article/details/8273303
http://singleant.iteye.com/blog/1497259
http://blog.csdn.net/fenglibing/article/details/7083071