欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Apollo-Client 使用SPI和Guice创建加载实例对象

程序员文章站 2022-04-17 17:25:49
...

背景

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

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

1.Injector

/**
 * @author Jason Song(aaa@qq.com)
 */
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(aaa@qq.com)
 */
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 目录下的类。内容填写的类必须实现文件名中的类,否则报错。
  • 文件内容具体如下图所示:
    Apollo-Client 使用SPI和Guice创建加载实例对象

流程图

Apollo-Client 使用SPI和Guice创建加载实例对象
若有错请留言,谢谢啦!

相关标签: Apollo