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

还不会JavaSPI机制?

  什么是SPI1。背景
  在面向对象的设计原则中,一般推荐模块之间基于接口编程,通常情况下调用方模块是不会感知到被调用方模块的内部具体实现。一旦代码里面涉及具体实现类,就违反了开闭原则。如果需要替换一种实现,就需要修改代码。
  为了实现在模块装配的时候不用在程序里面动态指明,这就需要一种服务发现机制。JavaSPI就是提供了这样一个机制:为某个接口寻找服务实现的机制。这有点类似IOC的思想,将装配的控制权移交到了程序之外。
  SPI英文为ServiceProviderInterface字面意思就是:服务提供者的接口,我的理解是:专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。
  SPI将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。2。使用场景
  很多框架都使用了Java的SPI机制,比如:数据库加载驱动,日志接口,以及dubbo的扩展实现等等。3。SPI和API有啥区别
  说到SPI就不得不说一下API了,从广义上来说它们都属于接口,而且很容易混淆。下面先用一张图说明一下:
  一般模块之间都是通过通过接口进行通讯,那我们在服务调用方和服务实现方(也称服务提供者)之间引入一个接口。
  当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是API,这种接口和实现都是放在实现方的。
  当接口存在于调用方这边时,就是SPI,由接口调用方确定接口规则,然后由不同的厂商去根据这个规则对这个接口进行实现,从而提供服务,举个通俗易懂的例子:公司H是一家科技公司,新设计了一款芯片,然后现在需要量产了,而市面上有好几家芯片制造业公司,这个时候,只要H公司指定好了这芯片生产的标准(定义好了接口标准),那么这些合作的芯片公司(服务提供者)就按照标准交付自家特色的芯片(提供不同方案的实现,但是给出来的结果是一样的)。
  实战演示
  Spring框架提供的日志服务SLF4J其实只是一个日志门面(接口),但是SLF4J的具体实现可以有几种,比如:Logback、Log4j、Log4j2等等,而且还可以切换,在切换日志具体实现的时候我们是不需要更改项目代码的,只需要在Maven依赖里面修改一些pom依赖就好了。
  这就是依赖SPI机制实现的,那我们接下来就实现一个简易版本的日志框架。1。ServiceProviderInterface
  新建一个Java项目serviceproviderinterface目录结构如下:。ideasrcMETAINForgspiserviceLogger。javaLoggerService。javaMain。javaMyServicesLoader。java
  新建Logger接口,这个就是SPI,服务提供者接口,后面的服务提供者就要针对这个接口进行实现。packageorg。spi。service;publicinterfaceLogger{voidinfo(Stringmsg);voiddebug(Stringmsg);}
  接下来就是LoggerService类,这个主要是为服务使用者(调用方)提供特定功能的。如果存在疑惑的话可以先往后面继续看。packageorg。spi。service;importjava。util。ArrayList;importjava。util。List;importjava。util。ServiceLoader;publicclassLoggerService{privatestaticfinalLoggerServiceSERVICEnewLoggerService();privatefinalLoggerlogger;privatefinalListLoggerloggerList;privateLoggerService(){ServiceLoaderLoggerloaderServiceLoader。load(Logger。class);ListLoggerlistnewArrayList();for(Loggerlog:loader){list。add(log);}LoggerList是所有ServiceProviderloggerListlist;if(!list。isEmpty()){Logger只取一个loggerlist。get(0);}else{loggernull;}}publicstaticLoggerServicegetService(){returnSERVICE;}publicvoidinfo(Stringmsg){if(loggernull){System。out。println(info中没有发现Logger服务提供者);}else{logger。info(msg);}}publicvoiddebug(Stringmsg){if(loggerList。isEmpty()){System。out。println(debug中没有发现Logger服务提供者);}loggerList。forEach(loglog。debug(msg));}}
  新建Main类(服务使用者,调用方),启动程序查看结果。packageorg。spi。service;publicclassMain{publicstaticvoidmain(String〔〕args){LoggerServiceserviceLoggerService。getService();service。info(HelloSPI);service。debug(HelloSPI);}}
  程序结果:
  info中没有发现Logger服务提供者
  debug中没有发现Logger服务提供者
  将整个程序直接打包成jar包,可以直接通过IDEA将项目打包成一个jar包。
  2。ServiceProvider
  接下来新建一个项目用来实现Logger接口
  新建项目serviceprovider目录结构如下:。idealibserviceproviderinterface。jarsrcMETAINFservicesorg。spi。service。LoggerorgspiproviderLogback。java
  新建Logback类packageorg。spi。provider;importorg。spi。service。Logger;publicclassLogbackimplementsLogger{Overridepublicvoidinfo(Stringmsg){System。out。println(Logbackinfo的输出:msg);}Overridepublicvoiddebug(Stringmsg){System。out。println(Logbackdebug的输出:msg);}}
  将serviceproviderinterface的jar导入项目中。新建lib目录,然后将jar包拷贝过来,再添加到项目中。
  再点击OK。
  接下来就可以在项目中导入jar包里面的一些类和方法了,就像JDK工具类导包一样的。
  实现Logger接口,在src目录下新建METAINFservices文件夹,然后新建文件org。spi。service。Logger(SPI的全类名),文件里面的内容是:org。spi。provider。Logback(Logback的全类名,即SPI的实现类的包名类名)。
  这是JDKSPI机制ServiceLoader约定好的标准
  接下来同样将serviceprovider项目打包成jar包,这个jar包就是服务提供方的实现。通常我们导入maven的pom依赖就有点类似这种,只不过我们现在没有将这个jar包发布到maven公共仓库中,所以在需要使用的地方只能手动的添加到项目中。3。效果展示
  接下来再回到serviceproviderinterface项目。
  导入serviceproviderjar包,重新运行Main方法。运行结果如下:
  Logbackinfo的输出:HelloSPI
  Logbackdebug的输出:HelloSPI
  说明导入jar包中的实现类生效了。
  通过使用SPI机制,可以看出服务(LoggerService)和服务提供者两者之间的耦合度非常低,如果需要替换一种实现(将Logback换成另外一种实现),只需要换一个jar包即可。这不就是SLF4J原理吗?
  如果某一天需求变更了,此时需要将日志输出到消息队列,或者做一些别的操作,这个时候完全不需要更改Logback的实现,只需要新增一个服务实现(serviceprovider)可以通过在本项目里面新增实现也可以从外部引入新的服务实现jar包。我们可以在服务(LoggerService)中选择一个具体的服务实现(serviceprovider)来完成我们需要的操作。
  loggerList。forEach(loglog。debug(msg));
  或者
  loggerList。get(1)。debug(msg);
  loggerList。get(2)。debug(msg);
  这里需要先理解一点:ServiceLoader在加载具体的服务实现的时候会去扫描所有包下src目录的METAINFservices的内容,然后通过反射去生成对应的对象,保存在一个list列表里面,所以可以通过迭代或者遍历的方式得到你需要的那个服务实现。
  3。ServiceLoader
  想要使用Java的SPI机制是需要依赖ServiceLoader来实现的,那么我们接下来看看ServiceLoader具体是怎么做的:
  ServiceLoader是JDK提供的一个工具类,位于packagejava。util;包下。Afacilitytoloadimplementationsofaservice。
  这是JDK官方给的注释:一种加载服务实现的工具。
  再往下看,我们发现这个类是一个final类型的,所以是不可被继承修改,同时它实现了Iterable接口。之所以实现了迭代器,是为了方便后续我们能够通过迭代的方式得到对应的服务实现。publicfinalclassServiceLoaderSimplementsIterableS{xxx。。。}
  可以看到一个熟悉的常量定义:
  privatestaticfinalStringPREFIXMETAINFservices;
  下面是load方法:可以发现load方法支持两种重载后的入参;publicstaticSServiceLoaderSload(ClassSservice){ClassLoaderclThread。currentThread()。getContextClassLoader();returnServiceLoader。load(service,cl);}publicstaticSServiceLoaderSload(ClassSservice,ClassLoaderloader){returnnewServiceLoader(service,loader);}privateServiceLoader(ClassSsvc,ClassLoadercl){serviceObjects。requireNonNull(svc,Serviceinterfacecannotbenull);loader(clnull)?ClassLoader。getSystemClassLoader():cl;acc(System。getSecurityManager()!null)?AccessController。getContext():null;reload();}publicvoidreload(){providers。clear();lookupIteratornewLazyIterator(service,loader);}
  根据代码的调用顺序,在reload()方法中是通过一个内部类LazyIterator实现的。先继续往下面看。
  ServiceLoader实现了Iterable接口的方法后,具有了迭代的能力,在这个iterator方法被调用时,首先会在ServiceLoader的Provider缓存中进行查找,如果缓存中没有命中那么则在LazyIterator中进行查找。publicIteratorSiterator(){returnnewIteratorS(){IteratorMap。EntryString,SknownProvidersproviders。entrySet()。iterator();publicbooleanhasNext(){if(knownProviders。hasNext())returntrue;returnlookupIterator。hasNext();调用LazyIterator}publicSnext(){if(knownProviders。hasNext())returnknownProviders。next()。getValue();returnlookupIterator。next();调用LazyIterator}publicvoidremove(){thrownewUnsupportedOperationException();}};}
  在调用LazyIterator时,具体实现如下:publicbooleanhasNext(){if(accnull){returnhasNextService();}else{PrivilegedActionBooleanactionnewPrivilegedActionBoolean(){publicBooleanrun(){returnhasNextService();}};returnAccessController。doPrivileged(action,acc);}}privatebooleanhasNextService(){if(nextName!null){returntrue;}if(configsnull){try{通过PREFIX(METAINFservices)和类名获取对应的配置文件,得到具体的实现类StringfullNamePREFIXservice。getName();if(loadernull)configsClassLoader。getSystemResources(fullName);elseconfigsloader。getResources(fullName);}catch(IOExceptionx){fail(service,Errorlocatingconfigurationfiles,x);}}while((pendingnull)!pending。hasNext()){if(!configs。hasMoreElements()){returnfalse;}pendingparse(service,configs。nextElement());}nextNamepending。next();returntrue;}publicSnext(){if(accnull){returnnextService();}else{PrivilegedActionSactionnewPrivilegedActionS(){publicSrun(){returnnextService();}};returnAccessController。doPrivileged(action,acc);}}privateSnextService(){if(!hasNextService())thrownewNoSuchElementException();StringcnnextName;nextNamenull;Classlt;?cnull;try{cClass。forName(cn,false,loader);}catch(ClassNotFoundExceptionx){fail(service,Providercnnotfound);}if(!service。isAssignableFrom(c)){fail(service,Providercnnotasubtype);}try{Spservice。cast(c。newInstance());providers。put(cn,p);returnp;}catch(Throwablex){fail(service,Providercncouldnotbeinstantiated,x);}thrownewError();Thiscannothappen}
  4。总结
  其实不难发现,SPI机制的具体实现本质上还是通过反射完成的。即:我们按照规定将要暴露对外使用的具体实现类在METAINFservices文件下声明。
  其实SPI机制在很多框架中都有应用:Spring框架的基本原理也是类似的反射。还有dubbo框架提供同样的SPI扩展机制。
  通过SPI机制能够大大地提高接口设计的灵活性,但是SPI机制也存在一些缺点,比如:遍历加载所有的实现类,这样效率还是相对较低的;当多个ServiceLoader同时load时,会有并发问题。
  写在最后
  FreemenApp是一款专注于IT程序员求职招聘的一个求职平台,旨在帮助IT技术工作者能更好更快入职及努力协调IT技术者工作和生活的关系,让工作更自由!
  本文转载自江璇Up

一些我不敢发在朋友圈的自私的想法前几天和闺蜜打电话,我说我现在越来越自私。她问我为什么这样说?我说我明知道爸妈在吵架,我会故意好几天不联系他们,因为我不想让他们破坏我的好心情。不知道从什么时候开始,我越来越趋利避你心里是不是依然住着一个人,偶尔会想起,忘不掉,但早已放下席慕容回眸里有一句佛说,前世的五百次回眸换得今生的一次擦肩而过。佛陀弟子阿难说愿化身石桥,受五百年风吹,五百年日晒,五百年雨淋,只为心爱之人从身边经过。人世间,唯一个情字,颠倒了万自我意识是如何左右我们的日常生活的?每个人的生活都是由思想与现实共同组成的。我们通过肉体的味觉触觉听觉视觉嗅觉等来感触现实世界给我们的反馈,传递到我们的大脑里面的思想世界中,在我们的思想世界里的自我做出相对应的反馈,40分!12篮板!4盖帽!对不起,字母哥,他要抢MVPNBA新赛季开打接近1个月,老中青三代路球星宛如八仙过海各显神通,你不得不感慨长江后浪推前浪,一代新人胜旧人!在这个过程中,常规赛各大奖项的争夺空前激烈。截至目前,东契奇以微弱优势爆冷不断!马刺掀翻联盟第一雷霆豪取19分大胜火箭该认清现实北京时间11月12日,NBA常规赛争夺继续,在两场西部鱼腩球队的比赛中,马刺以11193爆冷击败联盟第一的雄鹿队,雷霆队同样完成以下克上,以132113击败猛龙取得了一场19分大胜3700万!皇马将签下拜仁主力右后卫,阿扎尔有望重返英超足球助力团皇马本赛季开局一帆风顺,联赛先后21击败同城对手马竞,国家德比也31拿下死敌巴萨,一度创造各项赛季9连胜的佳绩。但是最近状态却有所下滑,最近5场比赛仅取得2胜1平2负,联CBA三消息辽宁主力辟谣打压,杜锋派出全亲信,吴前带伤要出战爱国篮,爱CBA,我是洛姐,小伙伴们看完记得点赞!辽宁队这一次没有任何一名球员加盟国家队打球,这让球迷们感到非常意外,甚至有传言称这是杜锋指导故意没有带上辽宁队的球员,目的就是让广爆冷!马刺击溃东部第一,双子星51分,雄鹿主帅失算,字母哥缺席NBA常规赛继续进行,马刺主场迎战雄鹿。此前马刺战绩为5胜7负,排名西部第九,雄鹿战绩为10胜1负,排名东部第一。这两队最大的渊源在于,雄鹿主帅布登霍尔泽真是师传波波维奇,可以算得29分22分!波波维奇双子星超级崛起,马刺8000万续约果然赚翻了马刺队算是NBA联盟最成功的球员和教练培养基地,马刺出品,必属精品。波波维奇麾下总是能够培养出来球星,哪怕是低顺位的年轻球员,波波维奇都能把他们最大的技术特点激发出来。而且,现在联NBA官方更新MVP榜!字母哥力压东契奇居首库里杜兰特首进前十北京时间11月11日,NBA官方公布了最新一期的MVP排行榜。雄鹿10胜1负,排名联盟第一,字母哥继续高居MVP榜的首位。排名第二至五位的依次是东契奇塔图姆米切尔和莫兰特。库里和杜疯狂的双十一悄然结束,为何只有iPhone卖到了断货?疯狂的双十一终于在历经20天之后结束了,各家手机厂商也都晒出了自己的战报。有人销售额同比增长300,有人拿到了某个价位段单品销量冠军。有人全渠道累计支付金额超过了170亿元,有人什
2023年重要动向中国将要放弃与四大审计合作2月财经新势力2023年重要动向中国将要放弃与四大审计合作据外媒彭博的报道,中国敦促境内国营企业逐渐停用全球四大会计师事务所的服务,以保障中国的数据安全。如果该消息得到了证实,那么ETF观察丨数字中国建设整体布局规划出台,数字政通数字认证涨超10,数字经济ETF(159658)涨超1今日数字经济概念股大幅高开,持续走强。截至发稿,数字政通数字认证涨超10,恒久科技城地香江太极股份皖通科技等涨停,锐捷网络超图软件易华录京北方等跟涨。消息面上,近日,数字中国建设整图说中国经济国家统计局2022年我国全年GDP超121万亿元经济实力再上新台阶2月28日,国家统计局公布2022年国民经济和社会发展统计公报。数据显示,2022年,我国经济总量突破120万亿元,再上新台阶城镇调查失业率总体回落,就业基本盘总体稳定居民消费价格2022中国汽车(金融)年鉴出炉,中国汽车金融渗透率微降近日,由21世纪经济报道新汽车研究院策划编撰出品的2022中国汽车(金融)年鉴(以下简称年鉴)正式刊印成册,这是21世纪新汽车研究院第八次以产业年度盘点的形式刊出中国汽车金融年鉴,国足主帅竟投弃权票?FIFA最佳评选中国足球又开国际玩笑!历史开讲国际足坛最为隆重的年度颁奖典礼,中国足球却极不合时宜地开了个国际玩笑!在最为关键的中国男足主教练投票选项上,中国足协竟然投出了弃权票,真是有些滑天下之大稽的意思。按照国际足全民健身,健康中国驿城区峰林潭杯足球友谊赛落幕驻马店市全民健身,健康中国中西部地区县域乡村足球系列活动暨驿城区峰林潭杯足球友谊赛落幕历时15天的由中国足球发展基金会主办,河南省足球协会承办,驻马店市体育总会,驿城区卫健体委执行数字中国建设整体布局规划出炉!数字经济板块迎来发展契机!本报记者任世碧见习记者曹原赫近日,中共中央国务院印发了数字中国建设整体布局规划(以下简称规划),提出要做强做优做大数字经济。培育壮大数字经济核心产业,研究制定推动数字产业高质量发展刘亦菲新造型又美又辣,小露身材好亮眼娱评大赏刘亦菲新造型也太辣了吧!什么时候见过刘亦菲这么辣了!脸蛋和身材都是名品啊,真是要让人流鼻血的程度!舔屏舔屏舔屏刘亦菲这次就是清冷与感性并存啊,化妆后更加艳丽的容颜好绝,闭月李荣浩和井柏然一同逛街,为开演唱会瘦太狠,身形单薄如纸片2月28日,有网友在社交平台分享出偶遇男星井柏然与歌手李荣浩一同在上海逛街画面,并附文写道下电梯的时候一眼就看到李荣浩老师的小眼睛,一眼认出旁边是井柏然,不过,因为是私人行程,所以粗花呢法则全新演绎经典面料诞生于18世纪英国的粗花呢布料,由于粗花呢织法特殊,挡寒防水的优越功能性受到一众马术骑士追捧。如今的粗花呢褪去粗犷狂野的印象,成为了优雅精致的代名词,一想到花呢单品,立刻能想到职业8年抗敏亲身试验总结的好用的护肤品,分享给大家今天给大家分享一些超级好用的修复敏感皮肤的面膜和护肤品,这是我8年来亲身试验总结后的经验,绝对好用。这个得从我2014年开始说起了,14年的秋天,我和妈妈去玉米地掰玉米,被那个毛毛
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网