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

背景

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

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

1.Injector

/**
 * @author Jason Song(song_s@ctrip.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(song_s@ctrip.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 目录下的类。内容填写的类必须实现文件名中的类,否则报错。
  • 文件内容具体如下图所示:
    SPI加载内容

流程图

时序图
若有错请留言,谢谢啦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
npm warn deprecated apollo-server-core@3.12.0: the `apollo-server-core` package has been deprecated. 这个警告意味着`apollo-server-core`包已被废弃。废弃意味着它将在将来的版本中不再被维护或更新。当我们在使用npm安装或更新项目依赖时,如果看到这个警告,就意味着有一些问题需要解决。 在本例中,警告涉及到`apollo-server-core` 版本3.12.0。这个包是Apollo的一个核心组件,用于构建GraphQL服务器。然而,该版本已被废弃,这意味着它存在一些问题或者有更好的替代选择。 废弃的软件包存在一些风险,因为它们不再得到维护或更新。这可能导致安全漏洞、缺陷或不兼容问题。所以,我们需要尽快寻找替代方案。 为了解决这个问题,我们可以查看`apollo-server-core`的文档或发布信息,以了解更多关于废弃的原因和可能的替代包的信息。通常,`apollo-server-core`的维护者会提供一些指导或建议,告诉我们如何迁移到更新的版本或其他相关的包。 我们可以使用命令`npm outdated`来检查是否有更近的版本可用,并尝试更新到最新的兼容版本。如果没有直接的替代包,我们可以考虑使用类似的库或搜索其他的GraphQL服务器解决方案。 总之,当我们看到npm的警告废弃某个软件包时,我们需要密切关注相关的文档和发布信息,寻找替代方案,并尽快做出调整以避免可能带来的问题或风险。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值