目录
二 为什么在JDBC中要用Class.forName,不能用ClassLoader.loadClass
三 Class.forName和ClassLoader.loadClass原理剖析
一 结论
Class.forName和ClassLoader.loadClass相同点
- 两者都实现了根据类的完全限定名将类加载到JVM中
- 两者都返回一个Class对象
- @exception ClassNotFoundException if the class cannot be located
Class.forName和ClassLoader.loadClass不同点
- 前者加载类的同时对类进行了初始化操作。(默认true)
- 后者只是将类加载到了JVM,并没有初始化
二 为什么在JDBC中要用Class.forName,不能用ClassLoader.loadClass
2.1 使用示例
// JDBC连接数据库
// 1. 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取连接
Connection conn = DriverManager.getConnection(url);
2.2 注册驱动原理
注册驱动是什么意思?是指在用JDBC连接数据库时,JDBC规范要求将Driver注册到它的DriverManager,因为SQL数据源有多种。看下
com.mysql.cj.jdbc.Driver类,该类在依赖包 mysql-connector-java-xxx.jar包。
可以看到Driver类里是通过在static静态代码块完成注册Driver实例到DriverManager的。
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
...
}
2.3 结论
因为ClassLoader.loadClass加载类到JVM没有对类初始化,所以不会执行Driver类里的static代码块,所以无法完成驱动注册;
Class.forName()加载类时默认完成类初始化,所以执行static代码块从而完成注册驱动。
所以,这就是为什么在JDBC中要用Class.forName,不能用ClassLoader.loadClass的原因。
三 Class.forName和ClassLoader.loadClass原理剖析
3.1 Class.forName()原理
Class.forName()在Class类里有2个重载方法。
我们在JDBC里使用的Class.forName("com.mysql.cj.jdbc.Driver")是直接使用的第一个方法,第二个参数是boolean initialize,默认是直接传的true。
@param initialize if {@code true} the class will be initialized ,即确定该class是否需要初始化。
两个重载方法底层都是调的forName0().
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
/.../
return forName0(name, initialize, loader, caller);
}
/** Called after security check for system loader access checks have been made. */
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
3.2 ClassLoader.loadClass原理
ClassLoader.loadClass在ClassLoader类也有2个重载方法。
ClassLoader的loadClass方法有个boolean resolve参数
@param resolve * If <tt>true</tt> then resolve the class ,即是否解析该类。
我们知道类加载过程包括了加载、验证、准备、解析、初始化五个阶段,所以这里显然没有到初始化阶段。
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先,检查类是否已经被加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}