前一篇地址:
先上代码
static String driverName = "com.mysql.jdbc.Driver"; static String url = "jdbc:mysql://127.0.0.1:3306/mysql"; static String username = "root"; static String password = ""; @Test public void getConnection1() { DriverManager.setLogWriter(new PrintWriter(System.out)); try { // 1、加载驱动,不加载驱动依然正常可以连接 // Class.forName(driverName); // 2、获取connection Connection conn = DriverManager.getConnection(url, username, password); // 3、依然可以获取链接 System.out.println(conn); // 查看已经加载的driver Enumerationdrivers = DriverManager.getDrivers(); System.out.println("------加载的diver--------"); while(drivers.hasMoreElements()) { System.out.println(drivers.nextElement().getClass().getName()); } } catch (Exception e) { e.printStackTrace(); } }
上面代码,没有手动加载驱动,但是依然可以获取连接
控制台输出信息
DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql") trying com.mysql.jdbc.DrivergetConnection returning com.mysql.jdbc.Drivercom.mysql.jdbc.JDBC4Connection@72d8c235------加载的diver--------com.mysql.jdbc.Drivercom.mysql.fabric.jdbc.FabricMySQLDriver
从输出可以看到,DriverManager自动加载了com.mysql.jdbc.Driver和com.mysql.fabric.jdbc.FabricMySQLDriver这两个驱动,这也可以说明DriverManager可以管理多个驱动。
下面我们来看一下DriverManager的源码:
public class DriverManager { static { // 类加载时候就进行了加载数据库操作 loadInitialDrivers(); println("JDBC DriverManager initialized"); } //其他代码省略 private static void loadInitialDrivers() { String drivers; try { // AccessController.doPrivileged这个方法可以看做临时扩大该类的权限 // 读取系统jdbc.drivers的配置 drivers = AccessController.doPrivileged(new PrivilegedAction() { public String run() { return System.getProperty("jdbc.drivers"); } }); } catch (Exception ex) { drivers = null; } AccessController.doPrivileged(new PrivilegedAction () { public Void run() { // 扫描 java.sql.Driver的实现类,并加载 // 加载就按照前一篇文章一样进行驱动注册 ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class); Iterator driversIterator = loadedDrivers.iterator(); 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; } // 加载System中的jdbc.drivers参数指定的驱动,可以是多个驱动,以“:”分割 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); } } } }
加载mysql和oracle驱动,分别连接到mysql和oracle数据库
static String MYSQL_DRIVERNAME = "com.mysql.jdbc.Driver"; static String MYSQL_URL = "jdbc:mysql://127.0.0.1:3306/mysql"; static String MYSQL_USERNAME = "root"; static String MYSQL_PASSWORD = ""; static String ORALE_DRIVERNAME = "oracle.jdbc.driver.OracleDriver"; static String ORALE_URL = "jdbc:oracle:thin:@10.211.55.6:1521:ORCL"; static String ORALE_USERNAME = "system"; static String ORALE_PASSWORD = "orcl"; @Test public void getConnection1() { // 输出DriverManager 的日志信息到控制台 DriverManager.setLogWriter(new PrintWriter(System.out)); // 获取mysql连接 try { // 1、加载驱动 Class.forName(MYSQL_DRIVERNAME); // 2、获取connection Connection conn = DriverManager.getConnection(MYSQL_URL, MYSQL_USERNAME, MYSQL_PASSWORD); System.out.println(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } // 获取oracle连接 try { // 1、加载驱动 Class.forName(ORALE_DRIVERNAME); // 2、获取connection Connection conn = DriverManager.getConnection(ORALE_URL, ORALE_USERNAME, ORALE_PASSWORD); System.out.println(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } }
执行,控制台输出
DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql") trying com.mysql.jdbc.DrivergetConnection returning com.mysql.jdbc.Drivercom.mysql.jdbc.JDBC4Connection@22b3428eDriverManager.getConnection("jdbc:oracle:thin:@10.211.55.6:1521:ORCL") trying com.mysql.jdbc.Driver trying com.mysql.fabric.jdbc.FabricMySQLDriver trying oracle.jdbc.OracleDrivergetConnection returning oracle.jdbc.OracleDriveroracle.jdbc.driver.T4CConnection@28d51032
上述代码DriverManager同时连接oracle和mysql,换句话说,DriverManager同时管理着oracle和mysql两个驱动。那我们会产生疑问DriverManager到底是如果多个管理驱动的,怎么样根据我们的连接配置信息(url,密码...)获取到对应的连接?
驱动的管理或者说记录是封装成DriverInfo后放到DriverInfoCopyOnWriteArrayList,可以看做是一个线程安全的ArrayList。
private final static CopyOnWriteArrayListregisteredDrivers = new CopyOnWriteArrayList ();
当我们要获取连接的时候,再遍历registeredDrivers这个列表,然后使用列表中的驱动尝试连接,当获取到连接以后就停止遍历,然后返回connection
DriverManager的核心连接代码
private static Connection getConnection( String url, java.util.Properties info, Class caller) throws SQLException { // 省略。。。 println("DriverManager.getConnection(\"" + url + "\")"); SQLException reason = null; // 遍历驱动注册列表 for(DriverInfo aDriver : registeredDrivers) { // 判断是否能使用该驱动 if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); // 尝试连接 /** * 其实java.sql.Driver接口定义了一个方法判断驱动能不能接受url的连接, * boolean acceptsURL(String url) throws SQLException; * 这里其实先使下面代码进行判断 * if (!aDriver.driver.acceptsURL(url)) { * continue; * } * 但是没有使用上面代码进行判断,我猜可能的原因是: * 1、java官方并不要求实现acceptsURL()方法,我们平常 * 使用也很早用到这个方法。 * 2、存在一种这样的情况:某个数据库厂商协议进行升级 * 了,但是为了兼容旧的协议还是允许连接, * acceptsURL(旧协议的url)方法,返回false, * 起到一个提示的作用。 */ Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); // 没有报异常并且 connection 不为空,则返回connection 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"); }}