JDK的spi和dubbo的spi

  今天在学习dubbo的时候了解到dubbo的spi是模仿了JDK的spi,之前没有听说过spi这个东西,所以特地学习记录一下。SPI的全名为Service Provider Interface。我们在系统开发过程中不同模块之间一般都是用接口进行通信的。接口一般都有多个实现类。如果在模块里编码的时候涉及到了具体的实现类,则后续需要修改实现类或者新增实现类的时候就要修改原来的模块编码,这样就违反了开闭原则(对扩展开发,对修改关闭。避免新增功能的时候修改原来的代码对原有的功能造成新的bug。)不利于代码的维护。JDK的SPI实现了为接口自动寻找实现类的功能。spi有点类似于ioc思想,将接口与实现类的装配转移到代码之外,用一个配置文件就能实现。

 spi的使用方式:当服务提供者提供一个接口的多个实现的时候,一般会在META-INF/services/ 下面建立一个与接口全路径同名的文件,在文件里配置接口具体的实现类。当外部调用模块的时候的时候就能实例化对应的实现类。


mysql的代码里就用到了spi

 

  jdk用来实现serviceloader来实现接口实现类的加载。


ServiceLoader实现了Iterable接口,所以当在一个文件里配置多个实现类的时候,会加载一组实现类进来。

定义一个command接口,建立一个配置文件,并在里面配置2个实现类。




加载的时候2个类都能加载进来


为什么dubbo不直接用jdk的spi机制,而是自己模仿实现了一个spi机制呢?

jdk的spi会在一次实例化所有实现,可能会比较耗时,而且有些可能用不到的实现类也会实例化,浪费资源而且没有选择。另外dubbo的spi增加了对扩展点IOC和AOP的支持,一个扩展点可以直接setter注入其他扩展点。这是jdk spi不支持的。



dubbo的spi文件定义在META-INF/dubbo/internal/路径下面和jdk的spi类似。但是dubbo的spi文件里面格式和jdkSPI有点不一样,格式是扩展名=全路径的类名,比如

threadlocal=com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory

lru=com.alibaba.dubbo.cache.support.lru.LruCacheFactory

jcache=com.alibaba.dubbo.cache.support.jcache.JCacheFactory

扩展名的作用是用来实现选择性的加载实现类。



dubbo的spi使用ExtensionLoader来实现。

 

//这个方法用户获取一个接口的ExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    if (type == null)
        throw new IllegalArgumentException("Extension type == null");
    if(!type.isInterface()) {
  1. 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!"); } //从map从获取 ExtensionLoader< T> loader = (ExtensionLoader< T>) EXTENSION_LOADERS.get(type); if (loader == null) { //map中没有的时候会创建并缓存到map里面 EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader< T>(type)); loader = (ExtensionLoader< T>) EXTENSION_LOADERS.get(type); } return loader; }
 //这个方法通过扩展名获取一个实现类
public T getExtension(String name) {
   if (name == null || name.length() == 0)
       throw new IllegalArgumentException("Extension name == null");
   if ("true".equals(name)) {
       return getDefaultExtension();
   }
   Holder<Object> holder = cachedInstances.get(name);
   if (holder == null) {
       //创建一个holder放到map里面 putIfAbsent保证了只有一个能放进去
       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.set(instance);
            }
        }
   }
   return (T) instance;
}
private ExtensionLoader(Class<?> type) {
    this.type = type;
    //objectFactory的作用是为dubbo的IOC提供对象
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}



//获取一个扩展装饰类的对象 如果类没有用Adaptive注解就动态创建一个装饰类
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;
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值