博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Dubbo与spi扩展
阅读量:3926 次
发布时间:2019-05-23

本文共 7267 字,大约阅读时间需要 24 分钟。

首先了解一下JDK的SPI

  1. spi 全称为(Service Provider Interface),是JDK内置的一种服务提供机制。
  2. 这个是针对厂商或者插件的。
  3. 一般来说对于未知的实现或者对扩展开放的系统,通常会把一些东西抽象出来,抽象的各个模块往往有很多不同的实现方案,例如:日志模块、xml解析模块、jdbc模块等。
实例
package com.tan.spi.example;public interface People {
void eat(String food);}
package com.tan.spi.example;public class Tom implements People{
@Override public void eat(String food) {
System.out.print("Tom 在吃"+food); }}
package com.tan.spi.example;import java.util.Iterator;import java.util.ServiceLoader;public class Main {
public static void main(String[] args) {
System.out.print("llll"); ServiceLoader
load = ServiceLoader.load(People.class); Iterator
iterator = load.iterator(); while (iterator.hasNext()) {
People next = iterator.next(); next.eat("蛋糕"); } }}

src下面必须新建META-INF/services/接口全限定路径

在这里插入图片描述
结果截图
在这里插入图片描述
这里我们只是了解一下,具体还可以自行百度

Dubbo 扩展SPI机制

Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。Dubbo SPI 的相关逻辑被封装在了

ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。

上面我们已经实验了java的spi,现在我们来看一下dubbo自定义实现的spi,这里有一个问题,dubbo为什么不用jdk的spi机制 而要自己定义一个???

  1. jdk标准的spi会一次性实例化扩展点上面的所有实现,如果所有扩展实现会很耗时,没有用到的也进行加载会造成资源浪费
  2. 增加都扩展点ioc和aop的支持,一个扩展点可以直接setter注入其他扩展点

约定

dubbo spi 存储路径 META-INF/dubbo/internal 文件名为接口全路径名,每一个spi定义的格式为扩展名=具体类目

目地

获取一个实现类的对象

途径

  • getExtensionLoader(Class type) 为该接口new 一个ExtensionLoader,然后缓存起来

  • getAdaptiveExtension()

    获取一个扩展装饰类对象,这个类有一个规则,如果没有一个adaptive注解,就会动态创建一个装饰类

  • getExtension(String name)获取一个实现类的对象

具体实现

首先看一下getExtensionLoader的源码,从 org.apache.dubbo.container.Container进去

private static final ExtensionLoader
loader = ExtensionLoader.getExtensionLoader(Container.class);
public static 
ExtensionLoader
getExtensionLoader(Class
type) {
if (type == null) throw new IllegalArgumentException("Extension type == null"); if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); } if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } // 这里是从一个ConcurrentMap中取,如果没有会将new 一个ExtensionLoader进去 ExtensionLoader
loader = (ExtensionLoader
) EXTENSION_LOADERS.get(type); if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader
(type)); loader = (ExtensionLoader
) EXTENSION_LOADERS.get(type); } return loader; }

在看一下getAdaptiveExtension

这是adaptive注解

/** * Provide helpful information for {@link ExtensionLoader} to inject dependency extension instance. * * @see ExtensionLoader * @see URL */ // ElementType.TYPE 代表该注解只能注解在类,包上面@Documented@Retention(RetentionPolicy.RUNTIME)@Target({
ElementType.TYPE, ElementType.METHOD})public @interface Adaptive {
String[] value() default {
};}

需要注意的是,adaptive注解和方法上是存在一定区别的:

注解在类上:代表人工实现编码,即实现了一个装饰类(设计模式中的装饰模式),比如ExtensionFactory
注解在方法上,代表动态的生成和编译一个动态的adaptive,例如Protoco$Adaptive
首先从**org.apache.dubbo.config.spring.schema.DubboNamespaceHandler(dubbo命名空间)**进去,看一下seviceBean.class

@Override    public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); }

在这里插入图片描述serviceBean实现了ServiceConfig,我们看一下这个方法,

在这里插入图片描述发现里面的getAdaptiveExtension()方法

public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get(); if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get(); if (instance == null) {
try {
instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) {
createAdaptiveInstanceError = t; throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t); } } } } else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } } return (T) instance; }

主要看一下createAdaptiveExtension方法,

在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

private void loadDirectory(Map
> extensionClasses, String dir, String type) {
String fileName = dir + type; try {
Enumeration
urls; ClassLoader classLoader = findClassLoader(); if (classLoader != null) {
urls = classLoader.getResources(fileName); } else {
urls = ClassLoader.getSystemResources(fileName); } if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement(); loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) {
logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } }
private void loadDirectory(Map
> extensionClasses, String dir, String type) {
// 拼装路径 String fileName = dir + type; try {
Enumeration
urls; ClassLoader classLoader = findClassLoader(); if (classLoader != null) {
urls = classLoader.getResources(fileName); } else {
urls = ClassLoader.getSystemResources(fileName); } if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement(); loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) {
logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } }

转载地址:http://lnkgn.baihongyu.com/

你可能感兴趣的文章
“既然计划没有变化快,那制订计划还有个卵用啊!”
查看>>
C#实现网页加载后将页面截取成长图片
查看>>
C# 在自定义的控制台输出重定向类中整合调用方信息
查看>>
【gRPC】ProtoBuf 语言快速学习指南
查看>>
C# 9 新特性 —— 补充篇
查看>>
Asp.Net Core使用Skywalking实现分布式链路追踪
查看>>
浅谈CLR基础知识
查看>>
Xamarin使XRPC实现接口/委托远程调用
查看>>
如何成功搞垮一个团队?
查看>>
.NET开源5年了,这些宝藏你还没get?
查看>>
【日常排雷】 .Net core 生产环境appsetting读取失败
查看>>
从内存中释放Selenium chromedriver.exe
查看>>
如何在 C# 中使用 MSMQ
查看>>
小试elsa
查看>>
巧用 Lazy 解决.NET Core中的循环依赖关系
查看>>
微前端架构在容器平台的应用
查看>>
C# 中的 null 包容运算符 “!” —— 概念、由来、用法和注意事项
查看>>
仓储模式到底是不是反模式?
查看>>
【One by One系列】IdentityServer4(一)OAuth2.0与OpenID Connect 1.0
查看>>
为什么人和人的差距这么大?
查看>>