為什麼說JDBC驅動類載入破壞了雙親委派機制
阿新 • • 發佈:2019-05-05
大家都知道jdk的類載入機制是雙親委派機制,當我們需要載入一個類的時候,比如如下幾種情況
- new一個類的物件
- 呼叫類的靜態成員(除了final常量)和靜態方法
- 使用java.lang.reflect包的方法對類進行反射呼叫
- 當虛擬機器啟動,java Demo01,則一定會初始化Demo01類,載入main方法所在的類
- 當初始化一個類,如果其父類沒有被初始化,則先初始化它父類
那麼看一段程式碼:
public static void main(String[] args) throws SQLException { String url = "jdbc:mysql://..."; String username = "root"; String password = "root"; Connection conn = null; try { conn = DriverManager.getConnection(url, username, password); PreparedStatement statement = conn.prepareStatement("select * from table where id = ?"); statement.setLong(1,615444000000000L); ResultSet set = statement.executeQuery(); while(set.next()){ System.out.println(set.getString("user_id")); } conn.close(); } catch (SQLException e) { e.printStackTrace(); } finally { if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
上面是一段標準的jdbc查詢程式碼,在載入java.sql.DriverManager類的時候,會執行靜態塊
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
主要是這兩行程式碼
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); //我們看到這裡使用了執行緒上下文載入器,為什麼這裡要這麼做??如果不這麼做會怎樣? public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }
下面是java.util.ServiceLoader$LazyIterator.nextService()方法
private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<?> c = null; try { c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); } try { S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen }
我們看到了Class.forName(cn, false, loader)的呼叫,如果前面沒有用執行緒上下文載入器(也就是loader),那麼這裡預設使用的是呼叫方的類載入器,如下所示:
/**
* Returns the {@code Class} object associated with the class or
* interface with the given string name. Invoking this method is
* equivalent to:
*
* <blockquote>
* {@code Class.forName(className, true, currentLoader)}
* </blockquote>
*
* where {@code currentLoader} denotes the defining class loader of
* the current class.
*
* <p> For example, the following code fragment returns the
* runtime {@code Class} descriptor for the class named
* {@code java.lang.Thread}:
*
* <blockquote>
* {@code Class t = Class.forName("java.lang.Thread")}
* </blockquote>
* <p>
* A call to {@code forName("X")} causes the class named
* {@code X} to be initialized.
*
* @param className the fully qualified name of the desired class.
* @return the {@code Class} object for the class with the
* specified name.
* @exception LinkageError if the linkage fails
* @exception ExceptionInInitializerError if the initialization provoked
* by this method fails
* @exception ClassNotFoundException if the class cannot be located
*/
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
那麼如果沒有使用執行緒上下文載入器,其實用的就是呼叫方 ServiceLoader 的載入器,而ServiceLoader在java.util下面,是無法載入com.mysql.jdbc包下的類。 我不認為這是對雙