范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

阿里一面说一说JavaSpringDubbo三者SPI机制的原理和区别

  今天来跟大家聊一聊Java、Spring、Dubbo三者SPI机制的原理和区别。
  其实我之前写过一篇类似的文章,但是这篇文章主要是剖析dubbo的SPI机制的源码,中间只是简单地介绍了一下Java、Spring的SPI机制,并没有进行深入,所以本篇就来深入聊一聊这三者的原理和区别。什么是SPI
  SPI全称为Service Provider Interface,是一种动态替换发现的机制,一种解耦非常优秀的思想,SPI可以很灵活的让接口和实现分离,让api提供者只提供接口,第三方来实现,然后可以使用配置文件的方式来实现替换或者扩展,在框架中比较常见,提高框架的可扩展性。
  简单来说SPI是一种非常优秀的设计思想,它的核心就是解耦、方便扩展。Java SPI机制--ServiceLoader
  ServiceLoader是Java提供的一种简单的SPI机制的实现,Java的SPI实现约定了以下两件事:文件必须放在META-INF/services/目录底下文件名必须为接口的全限定名,内容为接口实现的全限定名
  这样就能够通过ServiceLoader加载到文件中接口的实现。来个demo
  第一步,需要一个接口以及他的实现类public interface LoadBalance { }  public class RandomLoadBalance implements LoadBalance{ } 复制代码
  第二步,在META-INF/services/目录创建一个文件名LoadBalance全限定名的文件,文件内容为RandomLoadBalance的全限定名
  测试类:public class ServiceLoaderDemo {      public static void main(String[] args) {         ServiceLoader loadBalanceServiceLoader = ServiceLoader.load(LoadBalance.class);         Iterator iterator = loadBalanceServiceLoader.iterator();         while (iterator.hasNext()) {             LoadBalance loadBalance = iterator.next();             System.out.println("获取到负载均衡策略:" + loadBalance);         }     }  } 复制代码
  测试结果:
  此时就成功获取到了实现。
  在实际的框架设计中,上面这段测试代码其实是框架作者写到框架内部的,而对于框架的使用者来说,要想自定义LoadBalance实现,嵌入到框架,仅仅只需要写接口的实现和spi文件即可。实现原理
  如下是ServiceLoader中一段核心代码
  首先获取一个fullName,其实就是META-INF/services/接口的全限定名
  然后通过ClassLoader获取到资源,其实就是接口的全限定名文件对应的资源,然后交给parse方法解析资源
  parse方法其实就是通过IO流读取文件的内容,这样就可以获取到接口的实现的全限定名
  再后面其实就是通过反射实例化对象,这里就不展示了。
  所以其实不难发现ServiceLoader实现原理比较简单,总结起来就是通过IO流读取META-INF/services/接口的全限定名文件的内容,然后反射实例化对象。优缺点
  由于Java的SPI机制实现的比较简单,所以他也有一些缺点。
  第一点就是浪费资源,虽然例子中只有一个实现类,但是实际情况下可能会有很多实现类,而Java的SPI会一股脑全进行实例化,但是这些实现了不一定都用得着,所以就会白白浪费资源。
  第二点就是无法对区分具体的实现,也就是这么多实现类,到底该用哪个实现呢?如果要判断具体使用哪个,只能依靠接口本身的设计,比如接口可以设计为一个策略接口,又或者接口可以设计带有优先级的,但是不论怎样设计,框架作者都得写代码进行判断。
  所以总得来说就是ServiceLoader无法做到按需加载或者按需获取某个具体的实现。使用场景
  虽然说ServiceLoader可能有些缺点,但是还是有使用场景的,比如说:不需要选择具体的实现,每个被加载的实现都需要被用到虽然需要选择具体的实现,但是可以通过对接口的设计来解决Spring SPI机制--SpringFactoriesLoader
  Spring我们都不陌生,他也提供了一种SPI的实现SpringFactoriesLoader。
  Spring的SPI机制的约定如下:配置文件必须在META-INF/目录下,文件名必须为spring.factories文件内容为键值对,一个键可以有多个值,只需要用逗号分割就行,同时键值都需要是类的全限定名,键和值可以没有任何类与类之间的关系,当然也可以有实现的关系。
  所以也可以看出,Spring的SPI机制跟Java的不论是文件名还是内容约定都不一样。来个demo
  在META-INF/目录下创建spring.factories文件,LoadBalance为键,RandomLoadBalance为值
  测试:public class SpringFactoriesLoaderDemo {      public static void main(String[] args) {         List loadBalances = SpringFactoriesLoader.loadFactories(LoadBalance.class, MyEnableAutoConfiguration.class.getClassLoader());         for (LoadBalance loadBalance : loadBalances) {             System.out.println("获取到LoadBalance对象:" + loadBalance);         }     }  } 复制代码
  运行结果:
  成功获取到了实现对象。核心原理
  如下是SpringFactoriesLoader中一段核心代码
  其实从这可以看出,跟Java实现的差不多,只不过读的是META-INF/目录下spring.factories文件内容,然后解析出来键值对。使用场景
  Spring的SPI机制在内部使用的非常多,尤其在SpringBoot中大量使用,SpringBoot启动过程中很多扩展点都是通过SPI机制来实现的,这里我举两个例子1、自动装配
  在SpringBoot3.0之前的版本,自动装配是通过SpringFactoriesLoader来加载的。
  但是SpringBoot3.0之后不再使用SpringFactoriesLoader,而是Spring重新从META-INF/spring/目录下的org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中读取了。
  至于如何读取的,其实猜也能猜到就跟上面SPI机制读取的方式大概差不多,就是文件路径和名称不一样。2、PropertySourceLoader的加载
  PropertySourceLoader是用来解析application配置文件的,它是一个接口
  SpringBoot默认提供了 PropertiesPropertySourceLoader 和 YamlPropertySourceLoader两个实现,就是对应properties和yaml文件格式的解析。
  SpringBoot在加载PropertySourceLoader时就用了SPI机制
  与Java SPI机制对比
  首先Spring的SPI机制对Java的SPI机制对进行了一些简化,Java的SPI每个接口都需要对应的文件,而Spring的SPI机制只需要一个spring.factories文件。
  其次是内容,Java的SPI机制文件内容必须为接口的实现类,而Spring的SPI并不要求键值对必须有什么关系,更加灵活。
  第三点就是Spring的SPI机制提供了获取类限定名的方法loadFactoryNames,而Java的SPI机制是没有的。通过这个方法获取到类限定名之后就可以将这些类注入到Spring容器中,用Spring容器加载这些Bean,而不仅仅是通过反射。
  但是Spring的SPI也同样没有实现获取指定某个指定实现类的功能,所以要想能够找到具体的某个实现类,还得依靠具体接口的设计。
  所以不知道你有没有发现,PropertySourceLoader它其实就是一个策略接口,注释也有说,所以当你的配置文件是properties格式的时候,他可以找到解析properties格式的PropertiesPropertySourceLoader对象来解析配置文件。Dubbo SPI机制--ExtensionLoader
  ExtensionLoader是dubbo的SPI机制的实现类。每一个接口都会有一个自己的ExtensionLoader实例对象,这点跟Java的SPI机制是一样的。
  同样地,Dubbo的SPI机制也做了以下几点约定:接口必须要加@SPI注解配置文件可以放在META-INF/services/、META-INF/dubbo/internal/ 、META-INF/dubbo/ 、META-INF/dubbo/external/这四个目录底下,文件名也是接口的全限定名内容为键值对,键为短名称(可以理解为spring中Bean的名称),值为实现类的全限定名先来个demo
  首先在LoadBalance接口上@SPI注解@SPI public interface LoadBalance {  } 复制代码
  然后,修改一下Java的SPI机制测试时配置文件内容,改为键值对,因为Dubbo的SPI机制也可以从META-INF/services/目录下读取文件,所以这里就没重写文件random=com.sanyou.spi.demo.RandomLoadBalance 复制代码
  测试类:public class ExtensionLoaderDemo {      public static void main(String[] args) {         ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(LoadBalance.class);         LoadBalance loadBalance = extensionLoader.getExtension("random");         System.out.println("获取到random键对应的实现类对象:" + loadBalance);     }  } 复制代码
  通过ExtensionLoader的getExtension方法,传入短名称,这样就可以精确地找到短名称对的实现类。
  所以从这可以看出Dubbo的SPI机制解决了前面提到的无法获取指定实现类的问题。
  测试结果:
  dubbo的SPI机制除了解决了无法获取指定实现类的问题,还提供了很多额外的功能,这些功能在dubbo内部用的非常多,接下来就来详细讲讲。dubbo核心机制1、自适应机制
  自适应,自适应扩展类的含义是说,基于参数,在运行时动态选择到具体的目标类,然后执行。
  每个接口有且只能有一个自适应类,通过ExtensionLoader的getAdaptiveExtension方法就可以获取到这个类的对象,这个对象可以根据运行时具体的参数找到目标实现类对象,然后调用目标对象的方法。
  举个例子,假设上面的LoadBalance有个自适应对象,那么获取到这个自适应对象之后,如果在运行期间传入了random这个key,那么这个自适应对象就会找到random这个key对应的实现类,调用那个实现类的方法,如果动态传入了其它的key,就路由到其它的实现类。
  自适应类有两种方式产生,第一种就是自己指定,在接口的实现类上加@Adaptive注解,那么这个这个实现类就是自适应实现类。@Adaptive public class RandomLoadBalance implements LoadBalance{ } 复制代码
  除了自己代码指定,还有一种就是dubbo会根据一些条件帮你动态生成一个自适应类,生成过程比较复杂,这里就不展开了。
  自适应机制在Dubbo中用的非常多,而且很多都是自动生成的,如果你不知道Dubbo的自适应机制,你在读源码的时候可能都不知道为什么代码可以走到那里…2、IOC和AOP
  一提到IOC和AOP,立马想到的都是Spring,但是IOC和AOP并不是Spring特有的概念,Dubbo也实现IOC和AOP的功能,但是是一个轻量级的。
  2.1、依赖注入
  Dubbo依赖注入是通过setter注入的方式,注入的对象默认就是上面提到的自适应的对象,在Spring环境下可以注入Spring Bean。public class RoundRobinLoadBalance implements LoadBalance {      private LoadBalance loadBalance;          public void setLoadBalance(LoadBalance loadBalance) {         this.loadBalance = loadBalance;     }  } 复制代码
  如上代码,RoundRobinLoadBalance中有一个setLoadBalance方法,参数LoadBalance,在创建RoundRobinLoadBalance的时候,在非Spring环境底下,Dubbo就会找到LoadBalance自适应对象然后通过反射注入。
  这种方式在Dubbo中也很常见,比如如下的一个场景
  RegistryProtocol中会注入一个Protocol,其实这个注入的Protocol就是一个自适应对象。
  2.2、接口回调
  Dubbo也提供了一些类似于Spring的一些接口的回调功能,比如说,如果你的类实现了Lifecycle接口,那么创建或者销毁的时候就会回调以下几个方法
  在dubbo3.x的某个版本之后,dubbo提供了更多接口回调,比如说ExtensionPostProcessor、ExtensionAccessorAware,命名跟Spring的非常相似,作用也差不多。
  2.3、自动包装
  自动包装其实就是aop的功能实现,对目标对象进行代理,并且这个aop功能在默认情况下就是开启的。
  在Dubbo中SPI接口的实现中,有一种特殊的类,被称为Wrapper类,这个类的作用就是来实现AOP的。
  判断Wrapper类的唯一标准就是这个类中必须要有这么一个构造参数,这个构造方法的参数只有一个,并且参数类型就是接口的类型,如下代码:public class RoundRobinLoadBalance implements LoadBalance {      private final LoadBalance loadBalance;      public RoundRobinLoadBalance(LoadBalance loadBalance) {         this.loadBalance = loadBalance;     }  } 复制代码
  此时RoundRobinLoadBalance就是一个Wrapper类。
  当通过random获取RandomLoadBalance目标对象时,那么默认情况下就会对RandomLoadBalance进行包装,真正获取到的其实是RoundRobinLoadBalance对象,RoundRobinLoadBalance内部引用的对象是RandomLoadBalance。
  测试一下
  在配置文件中加入roundrobin=com.sanyou.spi.demo.RoundRobinLoadBalance 复制代码
  测试结果
  从结果可以看出,虽然指定了random,但是实际获取到的是RoundRobinLoadBalance,而RoundRobinLoadBalance内部引用了RandomLoadBalance。
  如果有很多的包装类,那么就会形成一个责任链条,一个套一个。
  所以dubbo的aop跟spring的aop实现是不一样的,spring的aop底层是基于动态代理来的,而dubbo的aop其实算是静态代理,dubbo会帮你自动组装这个代理,形成一条责任链。
  到这其实我们已经知道,dubbo的spi接口的实现类已经有两种类型了:自适应类Wrapper类
  除了这两种类型,其实还有一种,叫做默认类,就是@SPI注解的值对应的实现类,比如@SPI("random") public interface LoadBalance {  } 复制代码
  此时random这个key对应的实现类就是默认实现,通过getDefaultExtension这个方法就可以获取到默认实现对象。3、自动激活
  所谓的自动激活,就是根据你的入参,动态地选择一批实现类返回给你。
  自动激活的实现类上需要加上Activate注解,这里就又学习了一种实现类的分类。@Activate public interface RandomLoadBalance {  } 复制代码
  此时RandomLoadBalance就属于可以被自动激活的类。
  获取自动激活类的方法是getActivateExtension,所以根据这个方法的入参,可以动态选择一批实现类。
  自动激活这个机制在Dubbo一个核心的使用场景就是Filter过滤器链中。
  Filter是dubbo中的一个扩展点,可以在请求发起前或者是响应获取之后就行拦截,作用有点像Spring MVC中的HandlerInterceptor。
  Filter的一些实现类
  如上Filter有很多实现,所以为了能够区分Filter的实现是作用于provider的还是consumer端,所以就可以用自动激活的机制来根据入参来动态选择一批Filter实现。
  比如说ConsumerContextFilter这个Filter就作用于Consumer端。
  ConsumerContextFilter
  总结
  通过以上分析可以看出,实现SPI机制的核心原理就是通过IO流读取指定文件的内容,然后解析,最后加入一些自己的特性。
  最后总的来说,Java的SPI实现的比较简单,并没有什么其它功能;Spring得益于自身的ioc和aop的功能,所以也没有实现太复杂的SPI机制,仅仅是对Java做了一点简化和优化;但是dubbo的SPI机制为了满足自身框架的使用要求,实现的功能就比较多,不仅将ioc和aop的功能集成到SPI机制中,还提供注入自适应和自动激活等功能。
  原文链接:https://juejin.cn/post/7210601680554246202

66岁大妈相亲遇67岁三婚大爷,大妈你是不是生理有问题?孙大爷,67岁,婚姻经历可谓传奇的很,这一生竟然有过三段婚姻经历,而且每一次都是对方出g,这让大爷也很无奈,明明他很珍惜,希望和每一任都能好好过日子,一直到老。可最终,都是对方离自孩子感染流感病毒,全家乱套啦!家长百问百答据天津发布微信公众号2月20日消息,天津市北辰区华辰学校小学部和中学部有个别班级因学生发烧而暂停线下教学,停课时间为2月20日和21日。目前已证实发烧是因甲型流感引起。拒绝孩子错误用眼!快来看看你踩雷了吗?之前跟大家分享过,一年前我家娃视力检查双眼视力0。6,通过近一年的防控回归到了1。2,现在仔细想想娃视力为啥下降了?我觉得我们平时挺注意护眼的啊!问了很多眼镜宝宝的家长,再对照我家孕程接近尾声,孕期最后一月,如何保证顺利分娩孕期最后一个月,应该注意什么不知不觉中到了预产期了!想着和宝宝见面的日子一天天临近,很多准妈妈对此既翘首以盼又有些不知所措,可能还有这样那样的顾虑,为了能够顺顺利利和宝宝见面,在孕带着孩子来看书!济南书市青少年阅读成主角大众网海报新闻记者刘帅济南报道2月26日上午,第35届北京图书订货会分会场暨首届济南书市开幕第二天,又恰逢周日,很多家长带着孩子来看书购书。孩子早就惦记着来书市逛逛,今天正好是周日李铁案再爆猛料!35岁中国男足队长疑涉案,陈戌源拔出萝卜带出泥最近李铁和陈戌源的案件发酵得越来越厉害,背后接二连三牵出各种人员。首先是李铁案发生之后,后来到一月份包括陈永亮和刘奕这两个足协的高层都接连被带走问话,而且涉嫌到严重违法违纪。除此之为什么推荐你吃吃亚麻籽?首先看亚麻籽的重要组成成分吧!吃亚麻籽,主要是为了获取亚麻籽里的亚麻酸欧米伽3家族,它是人体必需脂肪酸,在体内最终会转化为DHA,它主要存在于大脑中。老师说多吃亚麻籽做学霸憨笑其实不要惧怕高碳水,这十二种高碳水化合物可以帮助你健康享受碳水多年来,碳水化合物的名声很差。人们经常将它们与体重增加2型糖尿病和各种其他健康状况联系起来。确实,高糖和精制谷物的加工食品通常缺乏重要的维生素和矿物质。然而,许多营养丰富富含纤维的外媒称国企停用四大审计!若属实除了信息安全外,或为脱钩做准备四大会计师事务所中国业务引关注近日,有外媒援引消息人士的信息报道称,中国对国有企业提出指导性意见,要求逐渐停止使用四大国际会计师事务所,合同到期后不再续签。如有境外附属公司的,则附科学一周(20230224)论文导读编译冯维维Science,24Feb2023,Volume379Issue6634科学2023年2月24日,第379卷6634期生物物理和大气物理学BiophysicsAtmosp华润携金沙景芝金种子亮相海南确立啤白融合发展之路2月23日,2022酒业创新与投资大会第二阶段会议酒与投资论坛首次聚齐海通国际中泰证券信达证券华创证券中金公司招商证券国泰君安等多位二级市场知名分析师,用数据说话,为酒业投下坚定的
一双袜子,余生的痛2018年11月21日,那是悲痛的一天!这一天,亲爱的爸爸走了,永远地离开了我们。晚上1151分,当姐夫急匆匆地敲门时,母亲迫不及待地打开门姐夫一个箭步,走向父亲的卧室父亲微微的朝没有四肢却活得这般精彩,他是如何活出不设限的人生?倘若你因一次意外,突然没有了四肢,你将如何面对?相信大多数的我们,肯定是绝望,沮丧,痛不欲生,为何我这样倒霉?为什么这样的悲剧会发生在我身上?我的人生还有什么希望?甚至干脆一了百了夜读最后一朵凌霄花主播读经典,陪您说晚安。大家好,我是禹城融媒主播陈帅。今晚与您分享的文章是丁立梅的最后一朵凌霄花。最后一朵凌霄花,开在我每天上下班必经的路旁。季节已深秋了,原先满墙头闹纷纷的凌霄花诈骗者定当是鼠辈我们不是提倡人人平等吗?那我怎么还要去歧视诈骗者,这不就是有意识区别对待他人。不是这样的,我并不是因为给诈骗者套上一个称号而鄙视他们,而是我发自心底地瞧不起他们。我认为我要尊重他们人来世间的意义,有人来享福,有人来应劫贫民为了解决温饱努力向上爬,是迫不得已,到了富裕阶层后不生邪念,只是为了长久富贵,可以理解,毕竟要经历脱掉几层皮,脱胎换骨后才能攀爬到富贵阶层。与其说贫民向上爬是有能力,更佩服富二倪萍的坎坷人生,和她一生爱过的5个重要男人撰文无忧酒编辑1号唠嗑员倪萍是一个成功的女人,但是她的人生也充满了坎坷。2014年,倪萍做客超级访谈时说了这么一句话如果有下辈子,我既不要爹娘,也不要孩子,更不要家庭。是对生活的和哈尔滨巨贪张明杰落马记涉案金额近3。5亿,女儿是知名歌手前言相信大家都听过歌曲我的歌声里吧,它是著名歌手曲婉婷的作品。曲婉婷独特的嗓音演唱出的歌曲别有一番风味,她发表的多张专辑都深受大家喜爱。不过她是一名歌手的同时还有着另一个身份,就是网友分享被绿得最惨的经历!自己的孩子竟然是姐夫的01hr网友分享被绿得最惨的经历还以为自己得了大便宜而沾沾自喜02hr活阎王猪我难道不会疼的吗?03hr应该看下精神科不要为逃学找借口04hr会不会这就是变成土豪的原因?那不是跟着长期摆烂后,我是如何恢复学习能力的欢迎来到林小北的屋子,点击右上方关注,持续为你分享自我提升成长干货自媒体写作内容。写在前面你们曾经历过这样一段时期吗?每天睁眼醒来,某音某博某信来回切换,一天下来什么事也没有做成,学会爱自己,才有能力爱别人好好爱自己,好好爱值得的人。发现自己的美如果你身上有某些特质和魅力特别吸引人,那么,这种魅力也应该让你更爱自己。你可以为自己踏实的工作态度而自豪,可以为自己善于发现美好认真生活而骄听说的孩子这篇文章不知能不能写完整,也许支离破碎,但也想记下来。因为几个月过去,自己内心逐渐平静,或许能记述这件事,可之前想过几句开头,却都无法接着想下去。思绪还是会不由自主卷入巨大心痛悲哀