建设官方网站需要注意什么南京市网站seo整站优化
1.简介
SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口 加载实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。下面,我们先来了解一下 Java SPI 与 Dubbo SPI 的使用方法,然后再来分析 Dubbo SPI 的源码。
2.SPI 示例
2.1 Java SPI 示例
前面简单介绍了 SPI 机制的原理,本节通过一个示例来演示 JAVA SPI 的使用方法。首先,我们定义一个接口,名称为 Robot。
public interface Robot {void sayHello();
}
接下来定义两个实现类,分别为擎天柱 OptimusPrime 和大黄蜂 Bumblebee。
public class OptimusPrime implements Robot {@Overridepublic void sayHello() {System.out.println("Hello, I am Optimus Prime.");}
}public class Bumblebee implements Robot {@Overridepublic void sayHello() {System.out.println("Hello, I am Bumblebee.");}
}
接下来 META-INF/services 文件夹下创建一个文件,名称为 Robot 的全限定名 com.tianxiaobo.spi.Robot。文件内容为实现类的全限定的类名,如下:
com.tianxiaobo.spi.OptimusPrime
com.tianxiaobo.spi.Bumblebee
做好了所需的准备工作,接下来编写代码进行测试。
public class JavaSPITest {@Testpublic void sayHello() throws Exception {ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);System.out.println("Java SPI");serviceLoader.forEach(Robot::sayHello);}
}
最后来看一下测试结果,如下:
从测试结果可以看出,我们的两个实现类被成功的加载,并输出了相应的内容。关于 Java SPI 的演示先到这,接下来演示 Dubbo SPI。
2.2 Dubbo SPI 示例
Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 的实现类配置放置在 META-INF/dubbo 路径下,下面来看一下配置内容。
optimusPrime = com.tianxiaobo.spi.OptimusPrime
bumblebee = com.tianxiaobo.spi.Bumblebee
与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样我们就可以按需加载指定的实现类了。另外,在测试 Dubbo SPI 时,需要在 Robot 接口上标注 @SPI 注解。下面来演示一下 Dubbo SPI 的使用方式:
public class DubboSPITest {@Testpublic void sayHello() throws Exception {ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);Robot optimusPrime = extensionLoader.getExtension("optimusPrime");optimusPrime.sayHello();Robot bumblebee = extensionLoader.getExtension("bumblebee");bumblebee.sayHello();}
}
测试结果如下:
演示完 Dubbo SPI,下面来看看 Dubbo SPI 对 Java SPI 做了哪些改进,以下内容引用至 Dubbo 官方文档。
- JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
- 增加了对扩展点 IOC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
在以上改进项中,第一个改进项比较好理解。第二个改进项没有进行验证,就不多说了。第三个改进项是增加了对 IOC 和 AOP 的支持,这是什么意思呢?这里简单解释一下,Dubbo SPI 加载完拓展实例后,会通过该实例的 setter 方法解析出实例依赖项的名称。比如通过 setProtocol 方法名,可知道目标实例依赖 Protocal。知道了具体的依赖,接下来即可到 IOC 容器中寻找或生成一个依赖对象,并通过 setter 方法将依赖注入到目标实例中。说完 Dubbo IOC,接下来说说 Dubbo AOP。Dubbo AOP 是指使用 Wrapper 类(可自定义实现)对拓展对象进行包装,Wrapper 类中包含了一些自定义逻辑,这些逻辑可在目标方法前行前后被执行,类似 AOP。Dubbo AOP 实现的很简单,其实就是个代理模式。这个官方文档中有所说明,大家有兴趣可以查阅一下。
关于 Dubbo SPI 的演示,以及与 Java SPI 的对比就先这么多,接下来加入源码分析阶段。