Apollo-Client 使用SPI和Guice創建加載實例對象

背景

Guide:https://www.jianshu.com/p/7fba7b43146a

SPI:https://www.jianshu.com/p/46b42f7f593c

1.Injector

/**
 * @author Jason Song([email protected])
 */
public interface Injector {

  /**
   * Returns the appropriate instance for the given injection type
   */
  <T> T getInstance(Class<T> clazz);

  /**
   * Returns the appropriate instance for the given injection type and name
   */
  <T> T getInstance(Class<T> clazz, String name);
}
  • Injector接口定義了 guice獲取實例的抽象方法。

1.1 DefaultInjector

該類是Inject的的實現類,初始化了Guice容器的過程。

/**
 * Guice injector
 * @author Jason Song([email protected])
 */
public class DefaultInjector implements Injector {
  private com.google.inject.Injector m_injector;

  public DefaultInjector() {
    try {
      // 實例化 Injector 對象
      m_injector = Guice.createInjector(new ApolloModule());
    } catch (Throwable ex) {
      ApolloConfigException exception = new ApolloConfigException("Unable to initialize Guice Injector!", ex);
      Tracer.logError(exception);
      throw exception;
    }
  }


  @Override
  public <T> T getInstance(Class<T> clazz) {
    try {
      //通過類型獲取實例對象
      return m_injector.getInstance(clazz);
    } catch (Throwable ex) {
      Tracer.logError(ex);
      throw new ApolloConfigException(
          String.format("Unable to load instance for %s!", clazz.getName()), ex);
    }
  }

  @Override
  public <T> T getInstance(Class<T> clazz, String name) {
    //該方法直接返回null,Guice不支持通過類型和類名查找實例。
    //Guice does not support get instance by type and name
    return null;
  }

  private static class ApolloModule extends AbstractModule {
    @Override
    protected void configure() {
      bind(ConfigManager.class).to(DefaultConfigManager.class).in(Singleton.class);
      bind(ConfigFactoryManager.class).to(DefaultConfigFactoryManager.class).in(Singleton.class);
      bind(ConfigRegistry.class).to(DefaultConfigRegistry.class).in(Singleton.class);
      bind(ConfigFactory.class).to(DefaultConfigFactory.class).in(Singleton.class);
      bind(ConfigUtil.class).in(Singleton.class);
      bind(HttpUtil.class).in(Singleton.class);
      bind(ConfigServiceLocator.class).in(Singleton.class);
      bind(RemoteConfigLongPollService.class).in(Singleton.class);
      bind(YamlParser.class).in(Singleton.class);
    }
  }
}
  • Guice不支持通過類型和對象名獲取實例。
  • 靜態內部類ApolloModule繼承com.google.inject.AbstractModule重寫configure方法,綁定指定類。

2. ApolloInjector

2.1 getInjector

#getInjector獲取Injector實例,即爲了獲取DefaultInjector,從而獲取Guice容器內的已綁定的實例對象。

 private static volatile Injector s_injector;
  private static final Object lock = new Object();

  /**
   * 獲取 Injector 實例
   * @return
   */
  private static Injector getInjector() {
    if (s_injector == null) {
      synchronized (lock) {
        if (s_injector == null) {
          try {
          	//創建Injector實例
            s_injector = ServiceBootstrap.loadFirst(Injector.class);
          } catch (Throwable ex) {
            ApolloConfigException exception = new ApolloConfigException("Unable to initialize Apollo Injector!", ex);
            Tracer.logError(exception);
            throw exception;
          }
        }
      }
    }

    return s_injector;
  }
  • 這裏獲取Injector是線程安全的,懶漢模式創建實例對象。ServiceBootstrap#loadFirst是通過SPI創建指定類型的對象,下面會詳細說明。

2.2 getInstance

通過Injector獲取Guice容器內綁定的類。

 public static <T> T getInstance(Class<T> clazz) {
    try {
      return getInjector().getInstance(clazz);
    } catch (Throwable ex) {
      Tracer.logError(ex);
      throw new ApolloConfigException(String.format("Unable to load instance for type %s!", clazz.getName()), ex);
    }
  }

  public static <T> T getInstance(Class<T> clazz, String name) {
    try {
      //目前只支持Guice獲取指定類型對象,但Guice不支持通過類型和名字獲取
      return getInjector().getInstance(clazz, name);
    } catch (Throwable ex) {
      Tracer.logError(ex);
      throw new ApolloConfigException(
          String.format("Unable to load instance for type %s and name %s !", clazz.getName(), name), ex);
    }
  }
  • Guice只能通過 Injector指定類型 創建/獲取 綁定的類,不支持通過類型和命令獲取。

3.ServiceBootstrap

封裝了SPI 通用的操作方法

public class ServiceBootstrap {

  /**
   * 返回指定類型的第一個實例
   *
   * @param clazz
   * @param <S>
   * @return
   */
  public static <S> S loadFirst(Class<S> clazz) {
    //獲取所有實例
    Iterator<S> iterator = loadAll(clazz);
    // 目錄/META-INF/services/{clazzName} 下沒有找到該類型的文件則拋出 IllegalStateException異常
    if (!iterator.hasNext()) {
      throw new IllegalStateException(String.format(
          "No implementation defined in /META-INF/services/%s, please check whether the file exists and has the right implementation class!",
          clazz.getName()));
    }
    //返回第一個實例
    return iterator.next();
  }

  /**
   * 指定類型創建配置文件中所有的實例
   *
   * @param clazz
   * @param <S>
   * @return
   */
  public static <S> Iterator<S> loadAll(Class<S> clazz) {
    // 加載 /META-INF/services/{clazzName} 文件內填寫的所有類並創建對象
    ServiceLoader<S> loader = ServiceLoader.load(clazz);
    // 獲取 /META-INF/services/{clazzName} 填寫的所有類並返回對應的集合
    return loader.iterator();
  }

  /**
   * 返回order排序後的對象集合
   *
   * @param clazz
   * @param <S>
   * @return
   */
  public static <S extends Ordered> List<S> loadAllOrdered(Class<S> clazz) {
    Iterator<S> iterator = loadAll(clazz);
    // 目錄/META-INF/services/{clazzName} 下沒有找到該類型的文件則拋出 IllegalStateException異常
    if (!iterator.hasNext()) {
      throw new IllegalStateException(String.format(
          "No implementation defined in /META-INF/services/%s, please check whether the file exists and has the right implementation class!",
          clazz.getName()));
    }

    List<S> candidates = Lists.newArrayList(iterator);
    Collections.sort(candidates, new Comparator<S>() {
      @Override
      public int compare(S o1, S o2) {
        // the smaller order has higher priority
        // 順序越小優先級越高
        return Integer.compare(o1.getOrder(), o2.getOrder());
      }
    });

    return candidates;
  }

  /**
   * 返回 優先級越高的 實例(Order最小)
   * @param clazz
   * @param <S>
   * @return
   */
  public static <S extends Ordered> S loadPrimary(Class<S> clazz) {
    List<S> candidates = loadAllOrdered(clazz);

    return candidates.get(0);
  }
}
  • ServiceLoader#load加載的是 /META-INF/services/%s 目錄下的類。內容填寫的類必須實現文件名中的類,否則報錯。
  • 文件內容具體如下圖所示:
    SPI加載內容

流程圖

時序圖
若有錯請留言,謝謝啦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章