2019独角兽企业重金招聘Python工程师标准>>>
大家都知道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 loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
//我们看到这里使用了线程上下文加载器,为什么这里要这么做??如果不这么做会怎样?
public static ServiceLoader load(Class 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:
*
*
* {@code Class.forName(className, true, currentLoader)}
*
*
* where {@code currentLoader} denotes the defining class loader of
* the current class.
*
* For example, the following code fragment returns the
* runtime {@code Class} descriptor for the class named
* {@code java.lang.Thread}:
*
*
* {@code Class t = Class.forName("java.lang.Thread")}
*
*
* 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包下的类。 我不认为这是对双亲委派的破坏。