5.3 Java类加载机制
Java的类加载机制称作双亲委派机制,要明白JDBC为什么破坏双亲委派机制就需要明白双亲委派机制是什么,其工作原理如下,启动类加载器BootStrap只加载rt.jar,也就是jdk提供的相关java部分,扩展类加载器只加载java lib/ext扩展目录下的jar包,而用户类加载器App加载用户编写的代码所在目录(classpath)。
Java加载类的整个过程是自顶向下的,当上级类加载器发现要加载的东西不属于他的范围,就让下级类加载器去加载,这样依次类推。
5.3.1 JDBC为什么要破坏双亲委派机制
通过上边便已初见端倪,因为JDBC的DriverManager是由Bootstrap类加载器去加载的,调用的方法也是JDBC的方法,但是真正的驱动JDBC里根本没有,所以在这样的机制下Bootstrap类加载器不会向下委培自然也就加载不到了,所以要让用户程序类加载器来加载真正的驱动就需要打破这种机制。
5.3.2 如何实现打破双亲委派机制
获取连接需要使用DriverManager的方法,该类在使用的时候会初始化执行loadInitialDrivers()方法来获取真正的驱动。
- 使用DriverManager初始化驱动
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
- 初始化驱动loadInitialDrivers()方法
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;
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
//加载驱动,
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> 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;
}
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);
}
}
}
- load方法
public static <S> ServiceLoader<S> load(Class<S> service) {
//获取当前线程的ClassLoader(用户线程则是AppClassLoader)
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
- 设置classLoader
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
//开发者用户线程classLoader
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
5.3.3 DriverManager中的操作方法实例
获取数据库连接最后调用了getConnection(url, info, Reflection.getCallerClass()),传入了一个反射获取到的当前调用类,后边会用的这个调用类的classLoader。
@CallerSensitive
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
在DriverManager中有一个很重要的方法判断该驱动是否能被加载。
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
boolean result = false;
if(driver != null) {
Class<?> aClass = null;
try {
//该classLoader能否加载到该类,加载到就返回
aClass = Class.forName(driver.getClass().getName(), true, classLoader);
} catch (Exception ex) {
result = false;
}
//加载到该类再和驱动类比较是否同一个类,是则返回true
result = ( aClass == driver.getClass() ) ? true : false;
}
return result;
}