源码级深度理解 Java SPI( 八 )


源码级深度理解 Java SPI

文章插图
RedisProperties 中维护了 Redis 连接所需要的关键属性,只要在 yml 或 properties 配置文件中,指定 spring.redis 开头的属性,都会被自动装载到 RedisProperties 实例中 。
源码级深度理解 Java SPI

文章插图
通过以上分析,已经一步步解读出 Spring Boot 自动装载的原理 。
五、SPI 应用案例之 DubboDubbo 并未使用 Java SPI,而是自己封装了一套新的 SPI 机制 。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下,配置内容形式如下:
optimusPrime = org.apache.spi.OptimusPrimebumblebee = org.apache.spi.Bumblebee与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样可以按需加载指定的实现类 。Dubbo SPI 除了支持按需加载接口实现类,还增加了 IOC 和 AOP 等特性 。
5.1 ExtensionLoader 入口Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,可以加载指定的实现类 。
ExtensionLoader 的 getExtension 方法是其入口方法,其源码如下:
public T getExtension(String name) {if (name == null || name.length() == 0)throw new IllegalArgumentException("Extension name == null");if ("true".equals(name)) {// 获取默认的拓展实现类return getDefaultExtension();}// Holder,顾名思义,用于持有目标对象Holder<Object> holder = cachedInstances.get(name);if (holder == null) {cachedInstances.putIfAbsent(name, new Holder<Object>());holder = cachedInstances.get(name);}Object instance = holder.get();// 双重检查if (instance == null) {synchronized (holder) {instance = holder.get();if (instance == null) {// 创建拓展实例instance = createExtension(name);// 设置实例到 holder 中holder.set(instance);}}}return (T) instance;}可以看出,这个方法的作用就是:首先检查缓存,缓存未命中则调用 createExtension 方法创建拓展对象 。那么,createExtension 是如何创建拓展对象的呢,其源码如下:
private T createExtension(String name) {// 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表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);}// 向实例中注入依赖injectExtension(instance);Set<Class<?>> wrapperClasses = cachedWrapperClasses;if (wrapperClasses != null && !wrapperClasses.isEmpty()) {// 循环创建 Wrapper 实例for (Class<?> wrapperClass : wrapperClasses) {// 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例 。// 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));}}return instance;} catch (Throwable t) {throw new IllegalStateException("...");}}createExtension 方法的的工作步骤可以归纳为:
  1. 通过 getExtensionClasses 获取所有的拓展类
  2. 通过反射创建拓展对象
  3. 向拓展对象中注入依赖
  4. 将拓展对象包裹在相应的 Wrapper 对象中
以上步骤中,第一个步骤是加载拓展类的关键,第三和第四个步骤是 Dubbo IOC 与 AOP 的具体实现 。
5.2 获取所有的拓展类Dubbo 在通过名称获取拓展类之前,首先需要根据配置文件解析出拓展项名称到拓展类的映射关系表(Map<名称, 拓展类>),之后再根据拓展项名称从映射关系表中取出相应的拓展类即可 。相关过程的代码分析如下:

经验总结扩展阅读