成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Dubbo的SPI實(shí)現(xiàn)以及與JDK實(shí)現(xiàn)的區(qū)別

開(kāi)發(fā) 開(kāi)發(fā)工具
在 Java 里, 為了規(guī)范開(kāi)發(fā),制定了大量的「規(guī)范」與「標(biāo)準(zhǔn)」,這些上層的內(nèi)容,大多是以接口的形式提供出來(lái)。那這些接口最終實(shí)現(xiàn)是誰(shuí)呢,在哪里呢?

在 Java 里, 為了規(guī)范開(kāi)發(fā),制定了大量的「規(guī)范」與「標(biāo)準(zhǔn)」,這些上層的內(nèi)容,大多是以接口的形式提供出來(lái)。那這些接口最終實(shí)現(xiàn)是誰(shuí)呢,在哪里呢?

[[235687]]

規(guī)范并不關(guān)心這個(gè)。

所謂規(guī)范,是指定了一系列內(nèi)容,來(lái)指導(dǎo)我們的開(kāi)發(fā)實(shí)現(xiàn)。比如 Servlet規(guī)范對(duì)于 Servlet 的行為做了說(shuō)明,具體實(shí)現(xiàn)時(shí),可以是 Tomcat,可以是Jetty 等等。

再比如 Java 的 JDBC 規(guī)范,規(guī)定了 Driver 提供者需要實(shí)現(xiàn)的內(nèi)容,但具體是 Oracle,或者M(jìn)ySQL 都可以支持。關(guān)于JDBC 可以看之前一篇文章(沒(méi)想到你是這樣的 JDBC)。在之前我們可以通過(guò) Class.forName來(lái)進(jìn)行Driver 具體實(shí)現(xiàn)類(lèi)的加載。從JDK1.6開(kāi)始,官方提供了一個(gè)名為 「SPI」 的機(jī)制,來(lái)更方便快捷的進(jìn)行對(duì)應(yīng)實(shí)現(xiàn)類(lèi)的加載,不需要我們關(guān)心。我們所需要做的,只需要將包含實(shí)現(xiàn)類(lèi)的 JAR 文件放到 classpath中即可。

正好最近讀了一些Dubbo的源碼,其中有 Dubbo 的不同于JDK的另一種 SPI實(shí)現(xiàn)。所以這篇我們來(lái)看 Dubbo 的 「SPI」實(shí)現(xiàn)以及與 JDK 實(shí)現(xiàn)的區(qū)別。

首先,什么是 SPI 呢?

SPI(Service Provider Interfaces), 可以理解成一個(gè)交給第三方實(shí)現(xiàn)的API。JDK文檔這樣描述

A service is a well-known set of interfaces and (usually abstract) classes. A service provider is a specific implementation of a service.

在Java 中使用到SPI的這些地方:

  • JDBC
  • JNDI
  • Java XML Processing API
  • Locael
  • NIO Channel Provider
  • ……

通過(guò)這種SPI 的實(shí)現(xiàn)形式,我們的應(yīng)用仿佛有了可插拔的能力。

我們之前的文章Tomcat 中 的可插拔以及 SCI 的實(shí)現(xiàn)原理 里,也分析了容器中是如何做到可插拔的。

JDK中的SPI 是怎樣實(shí)現(xiàn)的呢?

在JDK中包含一個(gè)SPI最核心的類(lèi):ServiceLoader,在需要加載Provider類(lèi)的時(shí)候,我們所要做的是:

  1. ServiceLoader.load(Provider.class); 

在JDK中規(guī)范了 Service Provider的路徑,所有 Provider必須在JAR文件的META-INF/services目錄下包含一個(gè)文件,文件名就是我們要實(shí)現(xiàn)的Service的名稱(chēng)全路徑。比如我們熟悉的JDBC 的MySQL實(shí)現(xiàn), 在mysql-connector中,就有這樣一個(gè)文件

META-INF/services/java.sql.Driver

這些provider是什么時(shí)候加載的呢?

由于Provider 的加載和初始化是Lazy的實(shí)現(xiàn),所以需要的時(shí)候,可以遍歷Provider 的 Iterator,按需要加載,已經(jīng)加載的會(huì)存放到緩存中。

但有些實(shí)現(xiàn)不想Lazy,就直接在 ServiceLoader 的load執(zhí)行之后直接把所有的實(shí)現(xiàn)都加載和初始化了,比如這次說(shuō)的JDBC,所以這里在Tomcat里有個(gè)處理內(nèi)存泄漏的,可以查看之前的文章(Tomcat與內(nèi)存泄露處理)

繼續(xù)說(shuō)回具體的加載時(shí)機(jī)。我們一般在Spring 的配置中會(huì)增加一個(gè)datasource,這個(gè)數(shù)據(jù)源一般會(huì)在啟動(dòng)時(shí)做為一個(gè)Bean被初始化,此時(shí)數(shù)據(jù)源中配置的driver會(huì)被設(shè)置。

這些內(nèi)容傳入Bean中,會(huì)調(diào)用DriverManager的初始化

  1. static { 
  2.    loadInitialDrivers(); 
  3.     println("JDBC DriverManager initialized"); 
  4.  
  5. loadInitialDrivers 執(zhí)行的的時(shí)候,除了ServiceLoader.load外,還進(jìn)行了初始化 
  6.  
  7. ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); 
  8. Iterator<Driver> driversIterator = loadedDrivers.iterator(); 
  9. try{ 
  10.     while(driversIterator.hasNext()) { 
  11.         driversIterator.next(); 
  12.     } 
  13. } catch(Throwable t) { 
  14. // Do nothing 
  15. return null; 

我們?cè)賮?lái)看 Dubbo 的SPI實(shí)現(xiàn)方式。如果你能看下 Dubbo 的源碼就會(huì)發(fā)現(xiàn),實(shí)現(xiàn)時(shí)并沒(méi)有使用 JDK 的SPI,而是自已設(shè)計(jì)了一種。

我們以Main class啟動(dòng)來(lái)看看具體的實(shí)現(xiàn)。

我們從使用的入口處來(lái)看,***步傳入一個(gè)接口, 然后再傳入期待的實(shí)現(xiàn)的名稱(chēng)

  1. SpringContainer container = (SpringContainer) ExtensionLoader.getExtensionLoader(Container.class).getExtension("spring"); 

這里傳入的是Container.class, 期待的實(shí)現(xiàn)是spring。

  1.  // synchronized in getExtensionClasses 
  2.      private Map<String, Class<?>> loadExtensionClasses() { 
  3.          final SPI defaultAnnotation = type.getAnnotation(SPI.class); 
  4.          if (defaultAnnotation != null) { 
  5.              String value = defaultAnnotation.value(); 
  6.              if ((valuevalue = value.trim()).length() > 0) { 
  7.                  String[] names = NAME_SEPARATOR.split(value); 
  8.                  if (names.length > 1) { 
  9.                      throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() 
  10.                             + ": " + Arrays.toString(names)); 
  11.                 } 
  12.                 if (names.length == 1) cachedDefaultName = names[0]; 
  13.             } 
  14.         } 
  15.  
  16.         Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); 
  17.         loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); 
  18.         loadDirectory(extensionClasses, DUBBO_DIRECTORY); 
  19.         loadDirectory(extensionClasses, SERVICES_DIRECTORY); 
  20.         return extensionClasses; 
  21.     } 

共從三個(gè)地方加載擴(kuò)展的class

  • DUBBO_INTERNAL_DIRECTORY META-INF/dubbo/internal/
  • DUBBO_DIRECTORY META-INF/dubbo/
  • SERVICES_DIRECTORY META-INF/services/

  1.  private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) { 
  2.          String fileName = dir + type.getName(); 
  3.          try { 
  4.              Enumeration<java.net.URL> urls; 
  5.              ClassLoader classLoader = findClassLoader(); 
  6.              if (classLoader != null) { 
  7.                  urls = classLoader.getResources(fileName); 
  8.              } else { 
  9.                  urls = ClassLoader.getSystemResources(fileName); 
  10.             } 
  11.             if (urls != null) { 
  12.                 while (urls.hasMoreElements()) { 
  13.                     java.net.URL resourceURL = urls.nextElement();   
  14.                     loadResource(extensionClasses, classLoader, resourceURL); 
  15.                 } 
  16.             } 
  17.         } catch (Throwable t) { 
  18.             logger.error("Exception when load extension class(interface: " + 
  19.                     type + ", description file: " + fileName + ").", t); 
  20.         } 
  21.     } 

這里通過(guò)classLoader,尋找符合傳入的特定名稱(chēng)的文件,java.net.URL resourceURL = urls.nextElement();

此時(shí)會(huì)得到一個(gè)包含該文件的URLPath, 再通過(guò)loadResource,將資源加載

此時(shí)得到的文件內(nèi)容是

  1. spring=com.alibaba.dubbo.container.spring.SpringContainer 

再進(jìn)一步,將等號(hào)后面的class加載,即可完成。

loadClass時(shí),并不是直接通過(guò)類(lèi)似Class.forName等形式加載,而是下面這個(gè)樣子:

  1. private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { 
  2.          if (!type.isAssignableFrom(clazz)) { 
  3.              throw new IllegalStateException("Error when load extension class(interface: " + 
  4.                      type + ", class line: " + clazz.getName() + "), class " 
  5.                      + clazz.getName() + "is not subtype of interface."); 
  6.          } 
  7.          if (clazz.isAnnotationPresent(Adaptive.class)) { 
  8.              if (cachedAdaptiveClass == null) { 
  9.                  cachedAdaptiveClass = clazz
  10.             } else if (!cachedAdaptiveClass.equals(clazz)) { 
  11.                 throw new IllegalStateException("More than 1 adaptive class found: " 
  12.                         + cachedAdaptiveClass.getClass().getName() 
  13.                         + ", " + clazz.getClass().getName()); 
  14.             } 
  15.         } else if (isWrapperClass(clazz)) { 
  16.             Set<Class<?>> wrappers = cachedWrapperClasses
  17.             if (wrappers == null) { 
  18.                 cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); 
  19.                 wrappers = cachedWrapperClasses
  20.             } 
  21.             wrappers.add(clazz); 
  22.         } else { 
  23.             clazz.getConstructor(); 
  24.             if (name == null || name.length() == 0) { 
  25.                 name = findAnnotationName(clazz); 
  26.                 if (name.length() == 0) { 
  27.                     throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); 
  28.                 } 
  29.             } 
  30.             String[] names = NAME_SEPARATOR.split(name); 
  31.             if (names != null && names.length > 0) { 
  32.                 Activate activate = clazz.getAnnotation(Activate.class); 
  33.                 if (activate != null) { 
  34.                     cachedActivates.put(names[0], activate); 
  35.                 } 
  36.                 for (String n : names) { 
  37.                     if (!cachedNames.containsKey(clazz)) { 
  38.                         cachedNames.put(clazz, n); 
  39.                     } 
  40.                     Class<?> c = extensionClasses.get(n); 
  41.                     if (c == null) { 
  42.                         extensionClasses.put(n, clazz); 
  43.                     } else if (c != clazz) { 
  44.                         throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName()); 
  45.                     } 
  46.                 } 
  47.             } 
  48.         } 
  49.     } 

加載之后,需要對(duì)class進(jìn)行初始化,此時(shí)直接newInstance一個(gè),再通過(guò)反射注入的方式將對(duì)應(yīng)的屬性設(shè)置進(jìn)去。

  1. private T createExtension(String name) { 
  2.          Class<?> clazz = getExtensionClasses().get(name); 
  3.          if (clazz == null) { 
  4.              throw findException(name); 
  5.          } 
  6.          try { 
  7.              T instance = (T) EXTENSION_INSTANCES.get(clazz); 
  8.              if (instance == null) { 
  9.                  EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); 
  10.                 instance = (T) EXTENSION_INSTANCES.get(clazz); 
  11.             } 
  12.             injectExtension(instance); 
  13.             Set<Class<?>> wrapperClasses = cachedWrapperClasses
  14.             if (wrapperClasses != null && !wrapperClasses.isEmpty()) { 
  15.                 for (Class<?> wrapperClass : wrapperClasses) { 
  16.                     instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); 
  17.                 } 
  18.             } 
  19.             return instance; 
  20.         } catch (Throwable t) { 
  21.             throw new IllegalStateException("Extension instance(name: " + name + ", class: " + 
  22.                     type + ")  could not be instantiated: " + t.getMessage(), t); 
  23.         } 
  24.     } 
  1. private T injectExtension(T instance) { 
  2.         try { 
  3.             if (objectFactory != null) { 
  4.                 for (Method method : instance.getClass().getMethods()) { 
  5.                     if (method.getName().startsWith("set") 
  6.                             && method.getParameterTypes().length == 1 
  7.                             && Modifier.isPublic(method.getModifiers())) { 
  8.                         Class<?> pt = method.getParameterTypes()[0]; 
  9.                         try { 
  10.                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; 
  11.                            Object object = objectFactory.getExtension(pt, property); 
  12.                            if (object != null) { 
  13.                                method.invoke(instance, object); 
  14.                            } 
  15.                        } catch (Exception e) { 
  16.                            logger.error("fail to inject via method " + method.getName() 
  17.                                    + " of interface " + type.getName() + ": " + e.getMessage(), e); 
  18.                        } 
  19.                    } 
  20.                } 
  21.            } 
  22.        } catch (Exception e) { 
  23.            logger.error(e.getMessage(), e); 
  24.        } 
  25.        return instance; 
  26.    } 

通過(guò)上面的描述我們看到,JDK 與 Dubbo的 SPI 實(shí)現(xiàn)上,雖然都是從JAR中加載對(duì)應(yīng)的擴(kuò)展,但還是有些明顯的區(qū)別,比如:Dubbo 支持更多的加載路徑,同時(shí),并不是通過(guò)Iterator的形式,而是直接通過(guò)名稱(chēng)來(lái)定位具體的Provider,按需要加載,效率更高,同時(shí)支持Provider以類(lèi)似IOC的形式提供等等。

【本文為51CTO專(zhuān)欄作者“侯樹(shù)成”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過(guò)作者微信公眾號(hào)『Tomcat那些事兒』獲取授權(quán)】

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來(lái)源: 51CTO專(zhuān)欄
相關(guān)推薦

2025-05-20 05:53:07

DubboSPI機(jī)制

2024-10-29 08:34:55

SPI機(jī)制接口

2023-08-28 10:42:25

DubboSPIJava

2021-06-30 10:32:33

反射多態(tài)Java

2009-07-09 13:44:12

JDK JRE

2016-11-21 11:59:19

排序算法Objective-C

2020-06-30 15:35:36

JavaSPI代碼

2021-09-10 08:31:19

DubboSPI框架

2022-12-16 12:07:06

2025-03-04 09:02:25

JavaSPI機(jī)制

2025-02-27 00:32:35

2009-02-01 10:10:00

Java資格認(rèn)證JDK1.4JDK1.6

2025-05-08 09:22:14

2012-07-19 09:32:09

2019-09-10 09:58:19

Dubbo負(fù)載均衡Hash

2020-09-29 06:45:49

JDK

2009-08-05 15:37:50

什么是RESTRESTful的實(shí)現(xiàn)

2021-06-18 11:17:36

URL數(shù)據(jù)庫(kù)MySQL

2023-03-01 10:37:51

2017-04-27 21:00:33

Android滑動(dòng)分析
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 狠狠综合网 | 亚洲 成人 在线 | 亚洲国产一区二区三区 | 欧美一区两区 | 亚洲视频免费在线看 | 男女羞羞视频在线免费观看 | 日韩精品在线免费观看视频 | 日韩欧美亚洲一区 | 国产蜜臀97一区二区三区 | 欧美一区二区三区在线 | 色狠狠一区 | 这里只有精品99re | 成人午夜激情 | 精品国产一区二区三区久久狼黑人 | 嫩草网 | 精品免费视频一区二区 | 日日夜精品视频 | 久久精品一区 | 日韩影院在线 | 日韩免费激情视频 | 欧美激情网站 | 久久久久久久一区二区三区 | av男人的天堂在线 | 成人三级视频 | 亚洲福利| 天天搞天天操 | 91天堂网| 日韩一区二区三区视频 | 特黄一级 | 亚洲视频一区在线观看 | 免费一级片 | 国产激情视频在线观看 | 国产999精品久久久 日本视频一区二区三区 | 欧美黄色大片在线观看 | 亚洲第一中文字幕 | 日韩一区二区三区视频在线观看 | 精品视频网 | 成人影院在线视频 | 欧美日韩综合视频 | 日韩成人免费视频 | 国产一区二区三区四区在线观看 |