ExtensionLoader是Dubbo中很有特色的一个设计,它的作用是为框架提供各种组件的扩展点,可以在应用运行时来决定使用哪个组件。对扩展点组件的描述是通过注解的方式实现的,包括3个主要的注解:
- SPI
- Adaptive
- Activate
我们先从注解的含义来理解ExtensionLoader
SPI
SPI是一个很早就存在的概念,它是预先定义的一组接口,并允许各个厂商(使用方)有自己的实现,通过配置的方式在运行时决定使用哪种实现。我们可以看一下Dubbo中具有SPI标记的接口有哪些
- CacheFactory
- Compiler
- ExtensionFactory
- LoggerAdapter
- Serialization
- StatusChecker
- DataStore
- ThreadPool
- Container
- PageHandler
- MonitorFactory
- RegistryFactory
- ChannelHandler
- Codec
- Codec2
- Dispatcher
- Transporter
- Exchanger
- HttpBinder
- Networker
- TelnetHandler
- ZookeeperTransporter
- ExporterListener
- Filter
- InvokerListener
- Protocol
- ProxyFactory
- Cluster
- ConfiguratorFactory
- LoadBalance
- Merger
- RouterFactory
- RuleConverter
- ClassNameGenerator
- Validation
当我们需这些接口的实现类时,就可以通过ExtensionLoader的方法获取
其中第一个方法是拿到一个具体的实现类,第二个方法是拿到一组被激活的实现类。
Adaptive
这个词无论作为注解名还是get方法名都比较令人迷惑。Adaptive的本意是适配的,之所以这样命名是因为我们从getAdaptiveExtension()获取到的对象并不是某个真正的实现类,而是对实现类进行封装之后的一个适配器。当我们调用这个适配器的方法时,它会间接地去调用具体实现类的方法。而适配器中正包含了该选取何种实现方式的逻辑。
适配器类的定义有两种来源:
- 静态代码形式的默认适配器。这些类会被Adaptive注解修饰,且一个接口只能有一个这样的静态适配器。这种形式仅应用于一些特殊的接口,如:AdaptiveCompiler、AdaptiveExtensionFactory这两个适配器,ExtensionLoader需要依赖它们来工作,所以使用了这种特殊的构建方式。
- 动态代码适配器。实际上其余的接口都是使用动态适配器,ExtensionLoader根据接口定义动态生成一段适配器代码,并构建这个动态类的实例。这个时候接口中的一些方法具有Adaptive标记,它提供了一些用于查找具体Extension的key,如果这些方法中有URL类型的参数,则会依次在url中查找这些key对应的value,再以此为name确定要使用的Extension。如果没有从url中找到该参数,则会使用SPI注解中的默认值name进行构建。
Transporter
下面以Transporter接口为例进行说明
bind方法具有Adaptive标记,所以在生成动态Adapter类的时候,该方法会根据运行时的参数决定使用那个具体的实现类,以下是部分动态类的代码
在适配器的方法中,首先要确定具体实现类的类型,也就是extName。当方法中有URL类型的参数时,这个配置会从url参数中获取。Adaptive注解中的两个key即是在url查找的key,分别为server和transporter。当两个key都不存在时,使用SPI注解中得默认值netty。在确定了extName后,就可以从事先加载好的实例中获取extension对象,最终将方法的调用传递给extension的对应方法。
全部extName与实现类的对应关系配置在dubbo jar包中MET-INF/dubbo/internal下,Transporter对应的配置文件为
Activate
Activate标记适用于另外一种动态配置
一个接口有多个实现类,Activate标记在每个实现类的type上,并注明“何时被激活”的条件,如group和key的信息,以及在所有被激活实现类中的排序信息。通过ExtensionLoader的getActivateExtension可以获取到被激活实现类构成的List。下面以最重要的Filter接口进行说明
接口本身不再有Activate注解,而是在它的实现类上面,如TimeoutFilter
只有在group为provider时才会生效
MonitorFilter在provider和consumer两端都会生效。这里group、key等条件是通过getActivateExtension的方法参数确定的,group通常是预先设定好的,key可以直接指定,也可以动态由url参数决定。当url中包含特定的key(value可以是任意值)时,对应的实例就会被激活。
ExtensionLoader本身还有很多复杂的实现细节,这里就不再介绍了。感兴趣的读者可以翻阅源码深入研究一下。