<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JDK SPI 、Spring SPI、Dubbo SPI機制

          共 49030字,需瀏覽 99分鐘

           ·

          2021-08-06 06:10

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          JDK SPI機制

          SPI(Service Provider Interface),是一種將服務(wù)接口與服務(wù)實現(xiàn)分離以達到解耦可拔插、大大提升了程序可擴展性的機制。

          約定(我覺得稱之為規(guī)范更合適):

          1. 制定統(tǒng)一的規(guī)范(比如 java.sql.Driver)

          2. 服務(wù)提供商提供這個規(guī)范具體的實現(xiàn),在自己jar包的META-INF/services/目錄里創(chuàng)建一個以服務(wù)接口命名的文件,內(nèi)容是實現(xiàn)類的全命名(比如:com.mysql.jdbc.Driver)。

          3. 平臺引入外部模塊的時候,就能通過該jar包META-INF/services/目錄下的配置文件找到該規(guī)范具體的實現(xiàn)類名,然后裝載實例化,完成該模塊的注入。

          這個機制最大的優(yōu)點就是無須在代碼里指定,進而避免了代碼污染,實現(xiàn)了模塊的可拔插。

           JDK SPI的一個典型案例就是 java.sql.Driver 了

           

           

           我們最熟悉的代碼

          // 加載驅(qū)動
          Class.forName("com.mysql.jdbc.Driver");
          // 獲取連接
          Connection connection = DriverManager.getConnection("url""user""password");

          我們進入DriverManager類,里面有個靜態(tài)代碼塊,這部分的內(nèi)容會先執(zhí)行。

          static {
              loadInitialDrivers();
              println("JDBC DriverManager initialized");
          }

          加載并初始化驅(qū)動

          private static void loadInitialDrivers() {
              // ...

              // 如果驅(qū)動被打包作為服務(wù)提供者,則加載它。
              AccessController.doPrivileged(new PrivilegedAction<Void>() {
                  public Void run() {
                      // 1. load
                      ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                      // 2. 獲取Loader的迭代器
                      Iterator<Driver> driversIterator = loadedDrivers.iterator();
                      try{
                          // 3. 調(diào)用next方法
                          while(driversIterator.hasNext()) {
                              driversIterator.next();
                          }
                      } catch(Throwable t) {
                      // Do nothing
                      }
                      return null;
                  }
              });
              // ...
          }

          看 ServiceLoader#load方法

          public static <S> ServiceLoader<S> load(Class<S> service) {
              // 獲取類加載器
              ClassLoader cl = Thread.currentThread().getContextClassLoader();
              // load
              return ServiceLoader.load(service, cl);
          }

          public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader){
              // new 一個 ServiceLoader,參數(shù)為服務(wù)接口Class和類加載器
              return new ServiceLoader<>(service, loader);
          }

          可以看出,上面主要是獲取類加載器并新建ServiceLoader的過程,沒有加載實現(xiàn)類的動作。現(xiàn)在看:ServiceLoader#iterator 方法

          public Iterator<S> iterator() {
              // 這里是個Iterator的匿名內(nèi)部類,重寫了一些方法
              return new Iterator<S>() {

                  // 已存在的提供者
                  Iterator<Map.Entry<String,S>> knownProviders
                      = providers.entrySet().iterator();

                  public boolean hasNext() {
                      // 先檢查緩存
                      if (knownProviders.hasNext())
                          return true;
                      // 緩存沒有,走 java.util.ServiceLoader.LazyIterator#hasNext 方法
                      return lookupIterator.hasNext();
                  }

                  public S next() {
                      // 同樣先檢查緩存
                      if (knownProviders.hasNext())
                          return knownProviders.next().getValue();
                      // 緩存沒有,走 java.util.ServiceLoader.LazyIterator#next 方法
                      return lookupIterator.next();
                  }

                  public void remove() {
                      throw new UnsupportedOperationException();
                  }

              };
          }

          看 ServiceLoader.LazyIterator#hasNext 方法

          // java.util.ServiceLoader.LazyIterator#hasNext
          public boolean hasNext() {
              if (acc == null) {
                  return hasNextService();
              } else {
                  PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                      public Boolean run() { return hasNextService(); }
                  };
                  return AccessController.doPrivileged(action, acc);
              }
          }
          private boolean hasNextService() {
              if (nextName != null) {
                  return true;
              }
              if (configs == null) {
                  try {
                      // 獲取全路徑:META-INF/services/java.sql.Driver
                      String fullName = PREFIX + service.getName();
                      // 加載資源
                      if (loader == null)
                          configs = ClassLoader.getSystemResources(fullName);
                      else
                          configs = loader.getResources(fullName);
                  } catch (IOException x) {
                      fail(service, "Error locating configuration files", x);
                  }
              }
              // 這里負責解析前面加載的配置信息
              while ((pending == null) || !pending.hasNext()) {
                  if (!configs.hasMoreElements()) {
                      return false;
                  }
                  // 解析的返回值是一個 Iterator<String> 類型,其中的String代表文件里配置的實現(xiàn)類全限定名,比如:com.mysql.jdbc.Driver
                  pending = parse(service, configs.nextElement());
              }
              // 把nextName初始化
              nextName = pending.next();
              return true;
          }

          看 ServiceLoader.LazyIterator#next 方法

          // java.util.ServiceLoader.LazyIterator#next
          public S next() {
              if (acc == null) {
                  return nextService();
              } else {
                  PrivilegedAction<S> action = new PrivilegedAction<S>() {
                      public S run() { return nextService(); }
                  };
                  return AccessController.doPrivileged(action, acc);
              }
          }
          // 進入 java.util.ServiceLoader.LazyIterator#nextService 方法
          private S nextService() {
              if (!hasNextService())
                  throw new NoSuchElementException();
              // com.mysql.jdbc.Driver
              String cn = nextName;
              nextName = null;
              Class<?> c = null;
              try {
                  // 加載 com.mysql.jdbc.Driver Class
                  c = Class.forName(cn, false, loader);
              } catch (ClassNotFoundException x) {
                  fail(service,
                       "Provider " + cn + " not found");
              }
              if (!service.isAssignableFrom(c)) {
                  fail(service,
                       "Provider " + cn  + " not a subtype");
              }
              try {
                  // 實例化并轉(zhuǎn)換成 com.mysql.jdbc.Driver 對象
                  S p = service.cast(c.newInstance());
                  // 添加到緩存
                  providers.put(cn, p);
                  return p;
              } catch (Throwable x) {
                  fail(service,
                       "Provider " + cn + " could not be instantiated",
                       x);
              }
              throw new Error();          // This cannot happen
          }

          所以,DriverManager做了什么事:

          // 1. 獲取類加載器并創(chuàng)建ServiceLoader對象
          ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
          // 2. 創(chuàng)建迭代器并覆蓋hasNext和next方法
          Iterator<Driver> driversIterator = loadedDrivers.iterator();

          try{
              // 3. 這個方法主要是讀取配置文件,獲取其中實現(xiàn)類的全限定名。
              while(driversIterator.hasNext()) {
                  // 4. 這個方法主要是根據(jù)全限定名去生成一個實例
                  driversIterator.next();
              }
          } catch(Throwable t) {
          // Do nothing
          }

          另外你也能發(fā)現(xiàn),ServiceLoader只提供了遍歷的方式來獲取目標實現(xiàn)類,沒有提供按需加載的方法,這也是常說的不足之處。

          自己實現(xiàn)

          定義一個接口

          package com.demo.spi;

          public interface Funny {

              void deal();
          }

          寫一個實現(xiàn)類

          package com.demo.spi;

          public class FunnyImpl implements Funny {
              @Override
              public void deal() {
                  System.out.println("FunnyImpl");
              }
          }

          配置

          文件內(nèi)容

           執(zhí)行測試

          @Test
          public void test(){
              ServiceLoader<Funny> serviceLoader = ServiceLoader.load(Funny.class);
              serviceLoader.forEach(Funny::deal);
          }

          輸出

          Spring SPI機制

          對于Spring的SPI機制主要體現(xiàn)在SpringBoot中。我們知道SpringBoot的啟動包含new SpringApplication和執(zhí)行run方法兩個過程,new的時候有這么個邏輯:(getSpringFactoriesInstances)

          這個方法走到里面,無非 1. 加載類的全限定名列表。2. 根據(jù)類名通過反射實例化。

          重點在于:SpringFactoriesLoader.loadFactoryNames(type, classLoader) 

          public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
              // 獲取類的全限定名
              String factoryClassName = factoryClass.getName();
              // 1. 執(zhí)行l(wèi)oadSpringFactories,這里只傳入了類加載器,肯定是要獲取全部配置
              // 2. getOrDefault,獲取指定接口的實現(xiàn)類名稱列表,如果沒有則返回一個空列表
              return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
          }

          private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
              // 先檢查緩存
              MultiValueMap<String, String> result = cache.get(classLoader);
              if (result != null) {
                  return result;
              }
              try {
                  // 獲取類路徑下所有META-INF/spring.factories
                  Enumeration<URL> urls = (classLoader != null ?
                          classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                          ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
                  result = new LinkedMultiValueMap<>();
                  // 把加載的配置轉(zhuǎn)換成Map<String, List<String>>格式
                  while (urls.hasMoreElements()) {
                      URL url = urls.nextElement();
                      UrlResource resource = new UrlResource(url);
                      Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                      for (Map.Entry<?, ?> entry : properties.entrySet()) {
                          String factoryClassName = ((String) entry.getKey()).trim();
                          for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                              result.add(factoryClassName, factoryName.trim());
                          }
                      }
                  }
                  cache.put(classLoader, result);
                  return result;
              }
              catch (IOException ex) {
                  throw new IllegalArgumentException("Unable to load factories from location [" +
                          FACTORIES_RESOURCE_LOCATION + "]", ex);
              }
          }

          它還提供了實例化的方法:SpringFactoriesLoader.loadFactories(factoryClass, classLoader) 利用反射實現(xiàn),理解起來也不困難,不做解釋了

          對比ServiceLoader:

          1. 都是XXXLoader。命名格式都一樣。

          2. 一個是加載 META-INF/services/ 目錄下的配置;一個是加載 META-INF/spring.factories 固定文件的配置。思路都一樣。

          3. 兩個都是利用ClassLoader和ClassName來完成操作的。不同的是Java的ServiceLoader加載配置和實例化都是自己來實現(xiàn),并且不能按需加載;SpringFactoriesLoader既可以單獨加載配置然后按需實例化也可以實例化全部。

          如何實現(xiàn)一個Spring-Boot-Starter?

          先看一下SpringBoot的相關(guān)要點。

           

          這個是SpringBoot的 @SpringBootApplication 注解,里面還有一個 @EnableAutoConfiguration 注解,開啟自動配置的。

           

          可以發(fā)現(xiàn)這個自動配置注解在另一個工程,而這個工程里也有個spring.factories文件,如下圖:

           

          我們知道在SpringBoot的目錄下有個spring.factories文件,里面都是一些接口的具體實現(xiàn)類,可以由SpringFactoriesLoader加載。

          那么同樣的道理,spring-boot-autoconfigure模塊也能動態(tài)加載了。看一下其中的內(nèi)容:

           

          我們看到有個接口【org.springframework.boot.autoconfigure.EnableAutoConfiguration】,有N個配置類,都是自動配置相關(guān)的,那么我們可以猜一猜,是不是模仿一下這樣的配置,我們就可以絲滑進入了?

          為了證明這一點,我們看一下 dubbo-spring-boot-starter 的結(jié)構(gòu)

           

          你會發(fā)現(xiàn)starter項目沒有實現(xiàn)類,只有個pom文件。

           

          它的關(guān)鍵在于引入了一個 autoconfigure 依賴。

           

          這個里面配置了Spring的 spring.factories 其中只有一個配置

           

          看到這里你是不是感覺到了什么:

          1. 新建一個只有pom的starter工程,引入寫好的自動配置模塊。

          2. 自動配置模塊配置 spring.factories 文件,格式如上。

          3. 具體實現(xiàn)自動配置邏輯。

          這樣當SpringBoot啟動加載配置類的時候,就會把這些第三方的配置一起加載。

          所以我認為 Starter的作用就是在啟動之前把第三方的配置加載到容器中,具體表現(xiàn)就是無須用戶手動配置即可使用。【如果不準確望指正】

          具體實現(xiàn)一個starter

          兩個maven工程,目錄如下:

          custom-spring-boot-autoconfigure↓↓↓↓↓

          pom

          <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              <modelVersion>4.0.0</modelVersion>

              <groupId>com.demo</groupId>
              <artifactId>custom-spring-boot-autoconfigure</artifactId>
              <version>1.0-SNAPSHOT</version>

              <dependencies>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-autoconfigure</artifactId>
                      <version>2.1.6.RELEASE</version>
                  </dependency>
              </dependencies>

          </project>

          CustomAutoConfiguration

          import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;

          @Configuration
          @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
          public class CustomAutoConfiguration {

              @Bean
              public CustomConfig customConfig(){
                  System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!第三方自定義的配置");
                  return new CustomConfig();
              }
          }


          CustomConfig

          /**
           * 自定義配置
           */
          public class CustomConfig {

              private String value = "Default";
          }

          spring.factories

          org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
          com.demo.CustomAutoConfiguration

          一共四個文件

          custom-spring-boot-starter↓↓↓↓

          只有pom文件

          <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              <modelVersion>4.0.0</modelVersion>

              <groupId>com.demo</groupId>
              <artifactId>custom-spring-boot-starter</artifactId>
              <version>1.0-SNAPSHOT</version>

              <dependencies>
                  <dependency>
                      <groupId>com.demo</groupId>
                      <artifactId>custom-spring-boot-autoconfigure</artifactId>
                      <version>1.0-SNAPSHOT</version>
                  </dependency>

              </dependencies>

          </project>

          最后把這兩個工程install

           

          下面是新建一個SpringBoot項目,然后引入咱們自定義的starter依賴。

          <dependency>
              <groupId>com.demo</groupId>
              <artifactId>custom-spring-boot-starter</artifactId>
              <version>1.0-SNAPSHOT</version>
          </dependency>

          然后啟動就可以啦,可以看見,用戶引入依賴之后無需手動配置就可以使用第三方插件

           

          看下工程依賴

          Dubbo SPI機制

          上面介紹了兩種SPI,到Dubbo這里就不難理解了吧?

          思路都是處理類+約定配置 ,在Dubbo里,約定擴展配置在  META-INF/dubbo/internal/ 目錄下

          在Dubbo源碼里也可以發(fā)現(xiàn),核心類是 ExtensionLoader

          private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

          private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

          getExtensionLoader

          // 這個方法就是獲取一個特定類型的ExtensionLoader的實例
          public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
              if (type == null) {
                  throw new IllegalArgumentException("Extension type == null");
              }
              if (!type.isInterface()) {
                  throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
              }
              if (!withExtensionAnnotation(type)) {
                  throw new IllegalArgumentException("Extension type (" + type +
                          ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
              }
              // 先從緩存中取
              ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
              if (loader == null) {
                  // 緩存沒有則新建
                  EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
                  loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
              }
              return loader;
          }

          下面看getExtension方法

          public T getExtension(String name) {
              if (StringUtils.isEmpty(name)) {
                  throw new IllegalArgumentException("Extension name == null");
              }
              if ("true".equals(name)) {
                  // 獲取默認的擴展類
                  return getDefaultExtension();
              }
              // Holder用來持有目標對象
              final Holder<Object> holder = getOrCreateHolder(name);
              // 獲取實例
              Object instance = holder.get();
              // 沒有的話,雙重檢查并創(chuàng)建實例
              if (instance == null) {
                  synchronized (holder) {
                      instance = holder.get();
                      if (instance == null) {
                          instance = createExtension(name);
                          holder.set(instance);
                      }
                  }
              }
              return (T) instance;
          }

          看createExtension方法

          private T createExtension(String name) {
              // 1. 獲取擴展Class
              Class<?> clazz = getExtensionClasses().get(name);
              if (clazz == null) {
                  throw findException(name);
              }
              try {
                  // 先從緩存中獲取
                  T instance = (T) EXTENSION_INSTANCES.get(clazz);
                  if (instance == null) {
                      // 沒有則新建
                      EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                      instance = (T) EXTENSION_INSTANCES.get(clazz);
                  }
                  // 2. IOC注入依賴
                  injectExtension(instance);
                  Set<Class<?>> wrapperClasses = cachedWrapperClasses;
                  if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                      for (Class<?> wrapperClass : wrapperClasses) {
                          instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                      }
                  }
                  return instance;
              } catch (Throwable t) {
                  throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                          type + ") couldn't be instantiated: " + t.getMessage(), t);
              }
          }


          1. 獲取所有擴展類

          private Map<String, Class<?>> getExtensionClasses() {
              // 緩存 + 雙重檢查
              Map<String, Class<?>> classes = cachedClasses.get();
              if (classes == null) {
                  synchronized (cachedClasses) {
                      classes = cachedClasses.get();
                      if (classes == null) {
                          // 加載擴展類
                          classes = loadExtensionClasses();
                          cachedClasses.set(classes);
                      }
                  }
              }
              return classes;
          }

          // 對于loadDirectory,每個目錄有兩個執(zhí)行邏輯,因為目前要兼容alibaba老版本
          private Map<String, Class<?>> loadExtensionClasses() {
              cacheDefaultExtensionName();
              Map<String, Class<?>> extensionClasses = new HashMap<>();
              // 加載目錄:META-INF/dubbo/internal/
              loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
              loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache""com.alibaba"));
              // 加載目錄:META-INF/dubbo/
              loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
              loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache""com.alibaba"));
              // 加載目錄:META-INF/services/
              loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
              loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache""com.alibaba"));
              return extensionClasses;
          }

          private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
              // 文件路徑,比如 META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
              String fileName = dir + type;
              try {
                  Enumeration<java.net.URL> urls;
                  ClassLoader classLoader = findClassLoader();
                  // 加載文件
                  if (classLoader != null) {
                      urls = classLoader.getResources(fileName);
                  } else {
                      urls = ClassLoader.getSystemResources(fileName);
                  }
                  if (urls != null) {
                      while (urls.hasMoreElements()) {
                          java.net.URL resourceURL = urls.nextElement();
                          // 加載資源(讀取文件內(nèi)容)
                          loadResource(extensionClasses, classLoader, resourceURL);
                      }
                  }
              } catch (Throwable t) {
                  logger.error("Exception occurred when loading extension class (interface: " +
                          type + ", description file: " + fileName + ").", t);
              }
          }

          private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
              try {
                  try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                      String line;
                      // 按行讀取
                      while ((line = reader.readLine()) != null) {
                          // 判斷注釋
                          final int ci = line.indexOf('#');
                          if (ci >= 0) {
                              // 截取 # 之前的字符串,# 之后的內(nèi)容為注釋,需要忽略
                              line = line.substring(0, ci);
                          }
                          line = line.trim();
                          if (line.length() > 0) {
                              try {
                                  String name = null;
                                  // 按照 = 分割,前面是name,后面是類全限定名
                                  int i = line.indexOf('=');
                                  if (i > 0) {
                                      name = line.substring(0, i).trim();
                                      line = line.substring(i + 1).trim();
                                  }
                                  if (line.length() > 0) {
                                      // 加載Class
                                      loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                                  }
                              } catch (Throwable t) {
                                  IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                                  exceptions.put(line, e);
                              }
                          }
                      }
                  }
              } catch (Throwable t) {
                  logger.error("Exception occurred when loading extension class (interface: " +
                          type + ", class file: " + resourceURL + ") in " + resourceURL, t);
              }
          }

          private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
              if (!type.isAssignableFrom(clazz)) {
                  throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                          type + ", class line: " + clazz.getName() + "), class "
                          + clazz.getName() + " is not subtype of interface.");
              }
              // 檢測目標類上是否有 Adaptive 注解
              if (clazz.isAnnotationPresent(Adaptive.class)) {
                  // 設(shè)置 cachedAdaptiveClass緩存
                  cacheAdaptiveClass(clazz);
              // 檢測 clazz 是否是 Wrapper 類型
              } else if (isWrapperClass(clazz)) {
                  // 存儲 clazz 到 cachedWrapperClasses 緩存中
                  cacheWrapperClass(clazz);
              } else {
                  // 檢測 clazz 是否有默認的構(gòu)造方法,如果沒有,則拋出異常
                  clazz.getConstructor();
                  if (StringUtils.isEmpty(name)) {
                      // 如果 name 為空,則嘗試從 Extension 注解中獲取 name,或使用小寫的類名作為 name
                      name = findAnnotationName(clazz);
                      if (name.length() == 0) {
                          throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                      }
                  }

                  String[] names = NAME_SEPARATOR.split(name);
                  if (ArrayUtils.isNotEmpty(names)) {
                      // 存儲 name 到 Activate 注解對象的映射關(guān)系
                      cacheActivateClass(clazz, names[0]);
                      for (String n : names) {
                          // 存儲 Class 到名稱的映射關(guān)系
                          cacheName(clazz, n);
                          // 存儲名稱到 Class 的映射關(guān)系
                          saveInExtensionClass(extensionClasses, clazz, n);
                      }
                  }
              }
          }

          這部分有些注釋直接拿的官網(wǎng),總結(jié)下來這部分:

          1. 根據(jù)約定的路徑比如【META-INF/dubbo/internal/】加載文件URl。

          2. 讀取并解析每一個文件內(nèi)容,具體為按行讀取,去掉注釋,按等號分割為 name-class全限定名鍵值對。

          3. 根據(jù)類全限定名生成Class對象,根據(jù)Class對象的的特點進行相關(guān)的緩存以及name到Class對象的緩存。

          2. IOC注入依賴

          private T injectExtension(T instance) {
              try {
                  if (objectFactory != null) {
                      for (Method method : instance.getClass().getMethods()) {
                          // 這里判斷方法是否是setter方法,Dubbo目前只處理setter的IOC
                          if (isSetter(method)) {
                              // 如果標注了@DisableInject,則不進行注入
                              if (method.getAnnotation(DisableInject.class) != null) {
                                  continue;
                              }
                              // 獲取 setter 方法參數(shù)類型
                              Class<?> pt = method.getParameterTypes()[0];
                              // 基本類型跳過
                              if (ReflectUtils.isPrimitives(pt)) {
                                  continue;
                              }
                              try {
                                  // 獲取屬性名,比如 setName 方法對應(yīng)屬性名 name
                                  String property = getSetterProperty(method);
                                  // 從 ObjectFactory 中獲取依賴對象
                                  Object object = objectFactory.getExtension(pt, property);
                                  if (object != null) {
                                      // 通過反射調(diào)用 setter 方法設(shè)置object依賴
                                      method.invoke(instance, object);
                                  }
                              } catch (Exception e) {
                                  logger.error("Failed to inject via method " + method.getName()
                                          + " of interface " + type.getName() + ": " + e.getMessage(), e);
                              }
                          }
                      }
                  }
              } catch (Exception e) {
                  logger.error(e.getMessage(), e);
              }
              return instance;
          }

          這部分容易理解,核心部分利用反射實現(xiàn),其它前置處理做了一些校驗。

          而使用Dubbo SPI的話也是一樣的套路,擴展類實現(xiàn) + 配置 + ExtensionLoader


            作者 |  露娜妹

          來源 |  cnblogs.com/LUA123/p/12460869.html

          瀏覽 129
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  男女午夜激情福利视频 | 亚洲人人草 | 无码人妻久久一区二区三区蜜桃 | 中文字幕日韩无码一区 | 日韩一级免费视频 |