专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

从实现到原理,聊聊Java中的SPI动态扩展

  1、简介
  SPI的全称是ServiceProviderInterface,翻译过来就是服务提供者的接口,它所实现的其实是一种服务的发现机制。
  这么说起来可能还是有点不好理解,我举个例子来类比一下。
  在spring项目中,写service层代码前,会约定俗成的会添加一个接口层。然后通过spring中的依赖注入,可以借助Autowired等方式注入这个接口的实现类的实例对象,之后对于service的调用一般也基于接口操作。
  简单形容就是这样的:
  如图所示,接口、实现类都是由服务提供方提供,我们可以把controller看作服务调用者,调用方只管调用接口就可以了。
  虽然也有声音认为,大部分情况下service只有一个实现类,接口层显得有些多余。但是在《HeadFirstDesignPatterns》这本书中,大佬们还是建议过:
  Programtoaninterface,notanimplementation。
  没错,就是常说的要面向接口编程。至于好处,也不外乎是降低耦合度、方便日后扩展、提高了代码的灵活性和可维护性等等。
  在上面这个例子里,这个接口层和其中的方法我们可以称之为API,而我们要讨论的SPI和它相比,有类似也有差异,还是先看图:
  简单来说,就是服务的调用方定义一个接口规范,可以由不同的服务提供者实现。并且,调用方能够通过某种机制来发现服务提供方,并通过接口调用它的能力。
  通过对比,我们可以看出它们虽然都有着接口这一层面,但还是有很大的不同:
  API中的接口是服务提供者给服务调用者的一个功能列表,而SPI中更多强调的是,服务调用者对服务实现的一种约束,服务提供者根据这种约束实现的服务,可以被服务调用者发现。
  说白了,Java中的SPI实现的就是,你按我的接口规范实现服务,我就能通过某种机制为这个接口寻找到这个服务。
  这么说起来可能还有些抽象,下面我们举一个例子,类比具体描述一下这个过程。2、定义接口
  说起智能家居系统,大家现在都比较熟悉了,只要是相同品牌下的产品,连上wifi就能够通过手机app控制了,非常方便。
  虽然产品不断更新换代,型号更新层出不穷,但是同种家电在app上操作起来,功能一般都是一样的。就拿空调来说,我们在app上操作起来一般也就三个主要功能:开关,选模式,调节温度。
  假设我现在在客厅、卧室、书房安装了3款不同型号的空调,并把它们都接入到了我app中,那么之后的操作都是相同的几个按键,简单粗暴。
  思考一下,无论是开关还是调温,都是通过app去调用设备的接口罢了,那么如果不同型号的空调各写各的接口,后端app在开发的时候光对接接口都麻烦的要死。
  解决方法也很简单,我先定义一套接口规范,不管你以后什么型号的空调,都按我的规范来实现接口。以后只要我能发现你的设备,那么都可以按相同的方法来调用接口。
  那么下面就先来定义这么一套接口规范,如果你以后想要接入智能家居系统,那么就要遵循这个规范来开发接口。
  新建一个项目作为标准,就叫airconditionstandard好了,然后创建一个接口。除了3个操作以外,我们再添加一个获取空调型号的方法。publicinterfaceIAircondition{获取型号StringgetType();开关voidturnOnOff();调节温度voidadjustTemperature(inttemperature);模式变更voidchangeModel(intmodelId);}
  这个接口后面要给服务的实现方来使用,用maven把它打成jar包:mvncleaninstall
  之后服务提供者在项目中就可以引入这个jar包了,有了这套规范,就保证了产品后期不管怎么更新换代,都能接入到系统来。3、服务实现
  制定并发布完规则后,挂式空调作为第一个服务提供者就来了,新建一个项目airconditionhangingtype,并引入刚才打好的jar包:dependencygroupIdcom。cn。hydragroupIdairconditionstandardartifactIdversion1。0SNAPSHOTversiondependency
  创建服务类,并实现前面定义的接口:publicclassHangingTypeAirconditionimplementsIAircondition{publicStringgetType(){returnHangingType;}publicvoidturnOnOff(){System。out。println(挂式空调开关);}publicvoidadjustTemperature(inti){System。out。println(挂式空调调节温度);}publicvoidchangeModel(inti){System。out。println(挂式空调更换模式);}}
  在项目的resources的目录下,创建METAINFservices目录,然后以前面定义的接口名com。cn。hydra。IAircondition创建文件,并在文件中写入实现类的全限定名。com。cn。hydra。HangingTypeAircondition
  整个项目结构非常简单:
  这样,一个服务方的简单实现就搞定了,用maven打成jar包,之后就可以提供给调用方使用了。
  同理,我们可以再创建一个立式空调的项目airconditionverticaltype,也只创建一个服务类:publicclassVerticalTypeAirconditionimplementsIAircondition{publicStringgetType(){returnVerticalType;}publicvoidturnOnOff(){System。out。println(立式空调开关);}publicvoidadjustTemperature(inti){System。out。println(立式空调调节温度);}publicvoidchangeModel(inti){System。out。println(立式空调更换模式);}}
  还是按上面的命名规则,创建一个配置文件:com。cn。hydra。VerticalTypeAircondition
  同样,打成jar包就完事了,至于服务调用者如何去发现和调用这两个服务,下面详细再说。4、服务发现
  现在两个服务提供方都实现了接口,下面关键的一步就是服务发现,这一步java中的spi发现机制已经帮我们实现好了。
  创建一个新项目airconditionapp,引入上面打好的两个jar包。dependenciesdependencygroupIdcom。cn。hydragroupIdairconditionhangingtypeartifactIdversion1。0SNAPSHOTversiondependencydependencygroupIdcom。cn。hydragroupIdairconditionverticaltypeartifactIdversion1。0SNAPSHOTversiondependencydependencies
  按照上面的说法,虽然每个服务提供者对于接口都有不同的实现,但是作为调用者来说,它并不需要关心具体的实现类,我们要做的是通过接口来调用服务提供者实现的方法。
  下面,就是关键的服务发现环节,我们写一个方法,根据型号去调用对应空调的开关方法。publicclassAirconditionApp{publicstaticvoidmain(String〔〕args){newAirconditionApp()。turnOn(VerticalType);}publicvoidturnOn(Stringtype){ServiceLoaderIAirconditionloadServiceLoader。load(IAircondition。class);for(IAirconditioniAircondition:load){System。out。println(检测到:iAircondition。getClass()。getSimpleName());if(type。equals(iAircondition。getType())){iAircondition。turnOnOff();}}}}
  测试结果:
  可以看到,测试过程中,通过定义的接口IAircondition发现了两个实现类,并通过参数,调用了特定实现类的某个方法。整段代码中没有出现过具体的服务实现类,操作都是通过接口调用。5、原理
  了解了spi的工作流程,我们再来看看它的实现,其实最关键的就是上面代码中出现的ServiceLoader这个类。
  上面的示例代码中,对于ServiceLoader的load()方法的结果,我们用for循环进行了遍历,这一点我们看一下源码就能明白,因为ServiceLoader实现了Iterable这一接口,而整个服务发现的核心,就在它的iterator()方法中。
  注意这里面有两个关键的东西,找一下在源码中定义的地方:
  注释写的非常明白,providers就是一个缓存,在迭代器中如果先从这里面进行查找,如果里面有就继续往下找,没有了的话就用这个懒加载的lookupIterator查找。
  那么就简单了,接着往下看LazyIterator,看看它里面的hasNext()和next()两个方法是怎么实现的。
  这个acc是一个安全管理器,在前面通过System。getSecurityManager()判断并赋值,debug看一下这里都是null,所以直接看hasNextService()和nextService()方法就可以了。
  在hasNextService()方法中,会取出接口取出实现类的类名放到nextName中:
  接下来,在nextService()方法中,则会先加载这个实现类,然后实例化对象,最终放入缓存中去。
  在迭代器的迭代过程中,会完成所有实现类的实例化,其实归根结底,还是基于java反射去实现的。6、应用
  要说spi的实际应用,大家最常见的应该就是日志框架slf4j了,它利用spi实现了插槽式接入其他具体的日志框架。
  说白了,slf4j本身就是个日志门面,并不提供具体的实现,需要绑定其他具体实现才能真正的引入日志功能。
  例如我们可使用log4j2作为具体的绑定器,只需要在pom中引入slf4jlog4j12,就可以使用具体功能。dependencygroupIdorg。slf4jgroupIdslf4japiartifactIdversion2。0。3versiondependencydependencygroupIdorg。slf4jgroupIdslf4jlog4j12artifactIdversion2。0。3versiondependency
  引入项目后,点开它的jar包看一下具体结构:
  有没有发现一个彩蛋,先说为什么我们pom中引入的明明是slf4jlog4j12,实际上引入的是slf4jreload4j?翻一下官网的文档:
  大意就是在2015年和2022年,log4j1。x就已经宣布endoflife终止了,原因也不难猜,估计是因为频繁爆出的漏洞。在那之后,slf4jlog4j在构建阶段就会自动重定向到slf4jreload4j了,并且官方也强烈建议使用slf4jreload4j作为替代。
  再回头看一下jar包的METAINF。services里面,通过spi注入了Reload4jServiceProvider这个实现类,它实现了SLF4JServiceProvider这一接口,在它的初始化方法initialize()中,会完成初始化等工作,后续可以继续获取到LoggerFactory和Logger等具体日志对象。7、总结
  Java中的SPI提供了一种比较特别的服务发现和调用机制,通过接口灵活的将服务调用与服务提供者分离,用于提供给第三方实现扩展时还是很方便的。但是也有缺点,比方说一旦加载一个接口,就会把所有实现类都加载进来,可能会加载到不需要的冗余服务。不过站在整体角度上,还是给我们提供了一种非常不错的框架扩展、集成的思路。

李玉成带85岁马玉琴逛动物园,冲鸵鸟大吵大叫,网友没个老人样时代在不停的发展,人们的审美和思想观念也有所改变!从前打死也不能接受的姐弟恋爷孙恋,现在也已经习以为常了,看得出来人们的思想真的不一样了。马玉琴和李玉成是很典型的姐弟恋,两人相差了菏投建设集团党支部加强意识形态工作筑牢思想防线大众网海报新闻记者赵晨光通讯员张晓莉张莹菏泽报道为进一步落实意识形态工作责任制,加强思想宣传舆论工作,防范化解意识形态领域重大风险隐患,近日,山东菏投建设投资集团有限公司(以下简称挖掘生产现场的智慧,从不起眼的小事开始原标题在生产现场发现问题,你一言我一语地献计献策,班组里的一个个小创新小改进就是这样做成的(引题)挖掘生产现场的智慧,从不起眼的小事开始(主题)工人日报中工网记者蒋菡阅读提示在生产合资理财公司遇产品发行窘境受波动市场冲击更大呼吁同业先守正再创新21世纪经济报道记者周炎炎唐曜华上海深圳报道目前全国开业的合资理财公司一共有四家,2020年9月,东方汇理资产管理公司与中银理财合资的汇华理财正式开业,成为首家中外联姻理财公司20无尽苍凉,曾经的计划生育先进县如今满是老人,或许这只是缩影不知道你们是否还记得,当年的计划生育当年的口号江苏省南通市如东县,是与上海一江之隔的东部沿海发达地区南通市的一个普通县。她曾是中国计划生育学习示范县,受到人民日报的表扬。如东计划生台选举杀出一匹黑马,王建煊称,当选就和大陆立即启动统一谈判台湾地区选举时,杀出一匹黑马,老将王建煊称,当选后就和大陆立即启动统一谈判。2024年台湾地区选举成为热门话题,而参选的人都是在岛内有一定分量的人,而在这些人中,有一位84岁高龄的热评两会丨新消费点亮新生活戴上一副虚拟现实眼镜,在独特视角下观赏古城的前世今生。陕西西安推出的VR沉浸式空中览古城项目,给游客带来震撼而美好的体验。随着新一代数字技术和可穿戴设备在消费场景中大展身手,也为旅李途纯凌娅没收李途纯价值不菲的私人物品至今片纸未退还凌娅没收李途纯价值不菲的私人物品至今片纸未退还用事实说话用证据作证作者李途纯近日,株洲市公安局原党委委员副局长,现任株洲市公安局二级高级警长凌娅涉嫌严重违纪违法,主动投案,目前正接苏婷很牛,可看完唐琼案例密训200问,发现这才是二建机电天花板二建机电专业一直以来热度不减,竞争压力很大,然而通关率却不见增长,关键就在于大部分考生没有掌握案例实操题,它在历年的分值占比非常高,若是没有一定的施工经验,很难理解并灵活运用,复习坑孩子的旧习俗,你占几条?2023育儿季当今社会很多父母忙于工作,孩子就由老人来照顾,虽然老人育儿经验丰富,但很多旧的观念习俗很难跟上现代的步伐。为了避免老人带孩子出现的一些问题,我们来探讨一下有哪些摒弃那妇产要闻围产医学部产三科丁新主任团队为50岁高龄产妇圆梦50岁的张女士,得知自己怀孕的消息,开心极了。这个由胚胎移植得来的试管婴儿实属不易,也是她的第一个宝宝。在北京妇产医院围产医学部产三科丁新主任团队手术室及其他相关科室医护人员的全力
iPhone解锁新姿势!BodyID!苹果正研发身体解锁?解锁新姿势。近日,苹果新的技术专利表明,该公司正在研发一种新的识别功能,这是继TouchID和FaceID后出现的新一种解锁设备方式BodyID。图片来源于网络雷总,什么时候能把小米外置SD卡还给我我手里的这部手机,是几年前花1800元,在小米商城买的小米8。那时候,小米正热推小米9,小米8作为不多的库存,打了折,作为穷人的我,不失时机地下了单。之前,几乎所有的手机,都有外置通俗易懂03决策树DecisionTree视频详解03机器学习决策树DecisionTree,小白入门决策树决策树(decisiontree)是一个树结构(可以是二叉树或非二叉树)。其每个非叶节点表示一个特征属性上的测试,Web3日报0304Web3日报0304SilverGateBank暂停交易平台网络服务KOL传做市商Citadel将助Silvergate摆脱流动性危机法庭文件Silvergate须向BlockFi苹果nfc门禁卡教程简介NFC门禁卡是一种先进的无线远程识别技术,可以用于门禁系统,可以使用苹果手机替代传统的门禁卡。在本文中,我们将介绍使用苹果手机作为NFC门禁卡的步骤,介绍相关资料,并总结一下制2023年初换机,追求顶级屏幕,这4款手机最佳如果您喜欢,可以点击上面的关注二字。后续会为您提供更多有价值的内容。今天分享,2023年初换机,追求顶级屏幕,这4款手机最佳。第一款红米K60Pro参考价格3899元(12256G以智造智!浪潮打造中国领先的服务器智能柔性生产基地3月2日,位于浪潮产业园的智能工厂里一片繁忙景象。各种智能机器人各司其职,为工厂的高效率运转提供保障。不久后,一批服务器将从这条生产线下线,用于能源交通金融等重点领域。这个聪明的工一个穷人想要翻身,一定不要自暴自弃,特别是想赚300万的人一个穷人想要翻身,一定不要自暴自弃,特别是想要赚300万的人,如果你一味地思想松散性格软弱内心躁动,你是不可能创业当老板的,相对的,你只会在卑微的底层,沦为一颗最廉价的棋子,一辈子平度市旧店镇有个赤土岘村,该村曾属掖县,后来划归平度本文继续带大家领略胶东乡村的风土人情,这次要介绍的地方名叫赤土岘,位于平度市旧店镇的辖区内。(赤土岘村的位置示意)赤土岘,是一个地理位置相对特殊的村庄。这里毗邻平度和莱州的交界线,行走嘉陵故道,遇见徐家坪位于陕西汉中市略阳县城以北18公里处的徐家坪镇,长江最大支流嘉陵江和中国第一条电气化铁路宝成铁路穿境而过,这里不仅是汉三颂之一郙阁颂故里,还是红二方面军曾经战斗过的地方。图说徐家坪仙居恩施,人间仙境,土司皇宫恩施土司城在中国湖北省,有一个地方,人们常称之为秘密之地。是美国有线电视新闻网评选的中国最美人间仙境,是中国国家地理评选的中国最美的地方,是列入世界遗产名录的世界文化遗产旅游目的地。这里的风
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网