SPI原理-线程上下文类加载器

灰太狼 2023-03-03 14:29 27阅读 0赞

最近在看spring源码时,发现了一个方法ClassUtils.getDefaultClassLoader(),先看一下源码

  1. public static ClassLoader getDefaultClassLoader(){
  2. ClassLoader cl = null;
  3. try {
  4. cl = Thread.currentThread().getContextClassLoader();
  5. }catch (Throwable ex){
  6. // Cannot access thread context ClassLoader - falling back...
  7. }
  8. if(cl == null){
  9. // No thread context class loader -> use class loader of this class.
  10. cl = ClassUtils.class.getClassLoader();
  11. if(cl == null){
  12. // getClassLoader() returning null indicates the bootstrap ClassLoader
  13. try {
  14. cl = ClassLoader.getSystemClassLoader();
  15. }catch (Throwable ex){
  16. // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
  17. }
  18. }
  19. }
  20. return cl;
  21. }

看到这里我感觉有点儿好奇,为什么这里要先从Thread.currentThread().getContextClassLoader()这样来获取类加载器呢,而不是直接使用下面的方式来获取类加载器。Thread.currentThread().getContextClassLoader()获取的是线程上下文的类加载器。这个也是spi的原理。

  1. spi打破了类加载器的双亲委派模型,底层就是使用的是线程上下文类加载器。
  2. 例如jdbc就是这样,我们知道接口是jdk提供的,具体实现由厂商来实现,那么父类加载器如何加载应用类加载的类呢。看一下源码。
  3. private static void loadInitialDrivers() {
  4. String drivers;
  5. try {
  6. drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
  7. public String run() {
  8. return System.getProperty("jdbc.drivers");
  9. }
  10. });
  11. } catch (Exception ex) {
  12. drivers = null;
  13. }
  14. // If the driver is packaged as a Service Provider, load it.
  15. // Get all the drivers through the classloader
  16. // exposed as a java.sql.Driver.class service.
  17. // ServiceLoader.load() replaces the sun.misc.Providers()
  18. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  19. public Void run() {
  20. ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
  21. Iterator<Driver> driversIterator = loadedDrivers.iterator();
  22. /* Load these drivers, so that they can be instantiated.
  23. * It may be the case that the driver class may not be there
  24. * i.e. there may be a packaged driver with the service class
  25. * as implementation of java.sql.Driver but the actual class
  26. * may be missing. In that case a java.util.ServiceConfigurationError
  27. * will be thrown at runtime by the VM trying to locate
  28. * and load the service.
  29. *
  30. * Adding a try catch block to catch those runtime errors
  31. * if driver not available in classpath but it's
  32. * packaged as service and that service is there in classpath.
  33. */
  34. try{
  35. while(driversIterator.hasNext()) {
  36. driversIterator.next();
  37. }
  38. } catch(Throwable t) {
  39. // Do nothing
  40. }
  41. return null;
  42. }
  43. });
  44. println("DriverManager.initialize: jdbc.drivers = " + drivers);
  45. if (drivers == null || drivers.equals("")) {
  46. return;
  47. }
  48. String[] driversList = drivers.split(":");
  49. println("number of Drivers:" + driversList.length);
  50. for (String aDriver : driversList) {
  51. try {
  52. println("DriverManager.Initialize: loading " + aDriver);
  53. Class.forName(aDriver, true,
  54. ClassLoader.getSystemClassLoader());
  55. } catch (Exception ex) {
  56. println("DriverManager.Initialize: load failed: " + ex);
  57. }
  58. }
  59. }

核心方法就是ServiceLoader.load(Driver.class);然后点进去看具体的实现

  1. public static <S> ServiceLoader<S> load(Class<S> service) {
  2. ClassLoader cl = Thread.currentThread().getContextClassLoader();
  3. return ServiceLoader.load(service, cl);
  4. }

可以看到,其实就是通过线程上下文类加载器来实现的。

发表评论

表情:
评论列表 (有 0 条评论,27人围观)

还没有评论,来说两句吧...

相关阅读