SPI原理-线程上下文类加载器
最近在看spring源码时,发现了一个方法ClassUtils.getDefaultClassLoader(),先看一下源码
public static ClassLoader getDefaultClassLoader(){
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}catch (Throwable ex){
// Cannot access thread context ClassLoader - falling back...
}
if(cl == null){
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if(cl == null){
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
}catch (Throwable ex){
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
看到这里我感觉有点儿好奇,为什么这里要先从Thread.currentThread().getContextClassLoader()这样来获取类加载器呢,而不是直接使用下面的方式来获取类加载器。Thread.currentThread().getContextClassLoader()获取的是线程上下文的类加载器。这个也是spi的原理。
spi打破了类加载器的双亲委派模型,底层就是使用的是线程上下文类加载器。
例如jdbc就是这样,我们知道接口是jdk提供的,具体实现由厂商来实现,那么父类加载器如何加载应用类加载的类呢。看一下源码。
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;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
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);
}
}
}
核心方法就是ServiceLoader.load(Driver.class);然后点进去看具体的实现
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
可以看到,其实就是通过线程上下文类加载器来实现的。
还没有评论,来说两句吧...