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

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

  八股文背多了,相信大家都听说过一个词, SPI扩展 。
  有的面试官就很喜欢问这个问题, SpringBoot的自动装配是如何实现的?
  基本上,你一说是基于spring的SPI扩展机制,再把 spring.factories 文件和EnableAutoConfiguration 提一下,那么这个问题就答的八九不离十了。
  就像四五年前,我去面试的时候被问到这个问题, SPI动态扩展机制 这几个词从嘴里一说出来,就把面试官唬的一愣一愣的。可能他们也没见过这么能装逼的,一句话能简简单单说明白,非要拽一个听上去很高大上的词。
  话说回来,被唬住的可不止是面试官,其实还有我自己。至于SPI扩展究竟是个啥,是怎么实现的,我当时也根本不明白。
  不过现在的面试就是这样,对线八股文,要想唬住面试官,就得先唬住自己。
  那么我们今天暂且不提spring的SPI扩展,先来看看java本身自带的SPI扩展机制是怎么一回事。 1、简介
  SPI的全称是 Service Provider Interface ,翻译过来就是服务提供者的接口,它所实现的其实是一种服务的发现机制。
  这么说起来可能还是有点不好理解,我举个例子来类比一下。
  在spring项目中,写service层代码前,会约定俗成的会添加一个接口层。然后通过spring中的依赖注入,可以借助 @Autowired 等方式注入这个接口的实现类的实例对象,之后对于service的调用一般也基于接口操作。
  简单形容就是这样的:
  如图所示,接口、实现类都是由服务提供方提供,我们可以把controller看作服务调用者,调用方只管调用接口就可以了。
  虽然也有声音认为,大部分情况下service只有一个实现类,接口层显得有些多余。但是在《Head First Design Patterns》这本书中,大佬们还是建议过:
  Program to an interface, not an implementation.
  没错,就是常说的 要面向接口编程 。至于好处,也不外乎是降低耦合度、方便日后扩展、提高了代码的灵活性和可维护性等等。
  在上面这个例子里,这个接口层和其中的方法我们可以称之为 API ,而我们要讨论的 SPI 和它相比,有类似也有差异,还是先看图:
  简单来说,就是服务的调用方定义一个接口规范,可以由不同的服务提供者实现。并且,调用方能够通过某种机制来发现服务提供方,并通过接口调用它的能力。
  通过对比,我们可以看出它们虽然都有着 接口 这一层面,但还是有很大的不同:
  API中的接口是服务提供者给服务调用者的一个功能列表,而SPI中更多强调的是,服务调用者对服务实现的一种约束,服务提供者根据这种约束实现的服务,可以被服务调用者发现。
  说白了,Java中的SPI实现的就是,你按我的接口规范实现服务,我就能通过某种机制为这个接口寻找到这个服务。
  这么说起来可能还有些抽象,下面我们举一个例子,类比具体描述一下这个过程。 2、定义接口
  说起智能家居系统,大家现在都比较熟悉了,只要是相同品牌下的产品,连上wifi就能够通过手机app控制了,非常方便。
  虽然产品不断更新换代,型号更新层出不穷,但是同种家电在app上操作起来,功能一般都是一样的。就拿空调来说,我们在app上操作起来一般也就三个主要功能: 开关 , 选模式 , 调节温度 。
  假设我现在在客厅、卧室、书房安装了3款不同型号的空调,并把它们都接入到了我app中,那么之后的操作都是相同的几个按键,简单粗暴。
  思考一下,无论是开关还是调温,都是通过app去调用设备的接口罢了,那么如果不同型号的空调各写各的接口,后端app在开发的时候光对接接口都麻烦的要死。
  解决方法也很简单,我先定义一套接口规范,不管你以后什么型号的空调,都按我的规范来实现接口。以后只要我能发现你的设备,那么都可以按相同的方法来调用接口。
  那么下面就先来定义这么一套接口规范,如果你以后想要接入智能家居系统,那么就要遵循这个规范来开发接口。
  新建一个项目作为标准,就叫 aircondition-standard 好了,然后创建一个接口。除了3个操作以外,我们再添加一个获取空调型号的方法。public interface IAircondition {     // 获取型号     String getType();          // 开关     void turnOnOff();      // 调节温度     void adjustTemperature(int temperature);      // 模式变更     void changeModel(int modelId); }
  这个接口后面要给服务的实现方来使用,用maven把它打成jar包: mvn clean install
  之后服务提供者在项目中就可以引入这个jar包了,有了这套规范,就保证了产品后期不管怎么更新换代,都能接入到系统来。 3、服务实现
  制定并发布完规则后, 挂式空调 作为第一个服务提供者就来了,新建一个项目 aircondition-hanging-type ,并引入刚才打好的jar包:     com.cn.hydra     aircondition-standard     1.0-SNAPSHOT 
  创建服务类,并实现前面定义的接口: public class HangingTypeAircondition         implements IAircondition{     public String getType() {         return "HangingType";     }          public void turnOnOff() {         System.out.println("挂式空调开关");     }      public void adjustTemperature(int i) {         System.out.println("挂式空调调节温度");     }      public void changeModel(int i) {         System.out.println("挂式空调更换模式");     } }
  在项目的 resources 的目录下,创建META-INF/services 目录,然后以前面定义的接口名com.cn.hydra.IAircondition 创建文件,并在文件中写入实现类的全限定名。com.cn.hydra.HangingTypeAircondition
  整个项目结构非常简单:
  这样,一个服务方的简单实现就搞定了,用maven打成jar包,之后就可以提供给调用方使用了。
  同理,我们可以再创建一个 立式空调 的项目 aircondition-vertical-type ,也只创建一个服务类:public class VerticalTypeAircondition         implements IAircondition{     public String getType() {         return "VerticalType";     }          public void turnOnOff() {         System.out.println("立式空调开关");     }      public void adjustTemperature(int i) {         System.out.println("立式空调调节温度");     }      public void changeModel(int i) {         System.out.println("立式空调更换模式");     } }
  还是按上面的命名规则,创建一个配置文件: com.cn.hydra.VerticalTypeAircondition
  同样,打成jar包就完事了,至于服务调用者如何去发现和调用这两个服务,下面详细再说。 4、服务发现
  现在两个服务提供方都实现了接口,下面关键的一步就是服务发现,这一步java中的spi发现机制已经帮我们实现好了。
  创建一个新项目 aircondition-app ,引入上面打好的两个jar包。              com.cn.hydra         aircondition-hanging-type         1.0-SNAPSHOT                    com.cn.hydra         aircondition-vertical-type         1.0-SNAPSHOT      
  按照上面的说法,虽然每个服务提供者对于接口都有不同的实现,但是作为调用者来说,它并不需要关心具体的实现类,我们要做的是通过接口来调用服务提供者实现的方法。
  下面,就是关键的服务发现环节,我们写一个方法,根据型号去调用对应空调的开关方法。 public class AirconditionApp {     public static void main(String[] args) {         new AirconditionApp().turnOn("VerticalType");     }      public void turnOn(String type){         ServiceLoader load = ServiceLoader                 .load(IAircondition.class);          for (IAircondition iAircondition : 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中引入slf4j-log4j12 ,就可以使用具体功能。     org.slf4j     slf4j-api     2.0.3       org.slf4j     slf4j-log4j12     2.0.3 
  引入项目后,点开它的jar包看一下具体结构:
  有没有发现一个彩蛋,先说为什么我们pom中引入的明明是 slf4j-log4j12 ,实际上引入的是slf4j-reload4j ?翻一下官网的文档:
  大意就是在2015年和2022年, log4j1.x 就已经宣布end of life 终止了,原因也不难猜,估计是因为频繁爆出的漏洞。在那之后,slf4j-log4j 在构建阶段就会自动重定向到slf4j-reload4j 了,并且官方也强烈建议使用slf4j-reload4j 作为替代。
  再回头看一下jar包的 META-INF.services 里面,通过spi注入了Reload4jServiceProvider 这个实现类,它实现了SLF4JServiceProvider 这一接口,在它的初始化方法initialize() 中,会完成初始化等工作,后续可以继续获取到LoggerFactory 和Logger 等具体日志对象。7、总结
  Java中的SPI提供了一种比较特别的服务发现和调用机制,通过接口灵活的将服务调用与服务提供者分离,用于提供给第三方实现扩展时还是很方便的。但是也有缺点,比方说一旦加载一个接口,就会把所有实现类都加载进来,可能会加载到不需要的冗余服务。不过站在整体角度上,还是给我们提供了一种非常不错的框架扩展、集成的思路。
  来源:https://mp.weixin.qq.com/s/8xaz1kVw7YShj2EP9izE4A

视力恢复训练是什么?视力恢复训练是什么?视力恢复训练是按照弱视和假性近视分为两种情况,但是一旦确诊为真性近视,通过视力训练很难恢复原来的样子。1。假性近视通过远视训练,滴低浓度阿托品可恢复视力儿童假性兰州工业学院和甘政法哪个好?两个学院都属于本科二批,一个是理工类学院,一个是政法类学院甘肃政法学院是甘肃省属的唯一一所政法类普通本科院校,也是全国最早建立的省属本科政法院校之一。学校前身是1956年创建的甘肃中医对中药颗粒怎么看?真正的中医不相信颗粒药的,只有庸医才会用。颗粒的成分,药物是否失效过期变质?只有自己亲手配制的药才会放心。据我了解,如今的中医对中药颗粒有三种看法无用将就着用有用。分述如下。中药颗小孩有点地包天,手术矫正要20万,有没有什么可以不动手术的方法,跪求?这个不知道你们信不信,我和我姐小时候都这个样,后来听我妈的都治过来了。用上嘴唇包住下嘴唇,不要用牙咬,光包着就行。尤其小孩,我妈讲是骨头软(大概这个意思,或者牙口还没定型的),这样银行什么月份最缺钱?我是银行的员工,银行的存款考核是有节点的。根据提问,银行什么月份最缺钱,那要根据所在银行的考核办法来定了。一般银行对下面的机构存款考核分为日均存款考核和时点存款考核两种。(一)日均当你提离职,为什么领导明着挽留,暗里要找人接替呢?473hr当你提出了辞职申请,领导明面上尽量挽留,暗地里找人准备接替你的工作。这种情况基本上属于职场通行作业手法。以我本人从业近20年的经历来看,基本上提交了辞呈就挽留不住人的。作做为一个厨师如何提升自己?你好,我也是一个厨师,现在上海酒店任行政总厨,自己在外地也承包了两家厨房至于说厨师这么提升自己?其实也很简单的,你这么问,说明你是一个爱学习的厨师我给你以下几点建议,供你参考我认为男婴什么时候开始说话?该怎么锻炼说话?你好,我是C妈,很高兴回答你的问题。我们家的宝宝1岁8个月了,到现在还是叠字发音,偶尔会几个单独的词语,而且都还是最近一个月才开始的学大人说话的,而朋友的女儿,只大我儿子3个月,已纸尿裤多久换一次最好?纸尿裤多久换一次最好?一导语有了纸尿裤真的是妈妈们的好帮手,省去了非常多的时间。可是很多新手妈妈不知道纸尿裤要多久换。经常看到妈妈们纸尿裤虽然很好,但是宝宝容易红屁股。除了宝宝皮肤如果一个人每天把牛奶当水喝,他会发生什么变化?我曾经做过一个实验,有一个假期我独自在家,一个人也不爱做饭,就突发奇想如果只喝牛奶不吃任何东西会怎样?于是到超市搬了四箱牛奶。从第二天早晨开始到晚上睡觉只要饿了就喝一盒,不限数,不新生儿多久可以晒太阳?我家是7。8天的时候就开始晒太阳了,我家从医院回来的时候医生说黄疸比较厉害,要多喝水,多晒太阳。所以早早的就开始晒太阳了。正常的话,一般两周之后就可以晒太阳了,一定要在市内晒太阳。
一两陈皮一两金!4种花样吃法,消食助眠还暖胃,现在吃正好每年10月,茶枝柑成熟之后,就会开始陈皮的制作。储存年份越长保存越完好的陈皮,等级越高价值越高。民间有言一两陈皮一两金百年陈皮胜黄金,小小一块陈皮为什么备受大家喜爱呢?中医认为,陈为何有些人特别惹蚊?最新研究发现是皮肤气味所致美国研究发现,蚊会倾向寻找叮咬的对象,而这是受人体散发的气味所致。这似乎解释了为何有些人特别惹蚊,特别容易在郊外野餐的时候,成为叮咬的目标。纽约洛克菲勒大学(Rockefeller夜读一个人应具备的三个品质来源河南法制报人们常说,细微之处见格局。优秀的人往往具备下面这三个品质,让他们在纷繁的人生路上,张弛有度不骄不躁,稳步地朝着目标前行。内心坚强有人说其实人跟树一样,越是向往高处的阳有人替我来爱你纵使三生三世魂牵梦绕奈何桃花拂面不是当年模样世间万般皆苦,唯情执最苦。爱不重不生娑婆,念不一不生净土。我嫁进江家那一天,就把自己的后半生放弃了。父亲生意失败,欠下高利贷,要债的堵着瑞光照亮心世界为什么冬季要多晒太阳冬天里晒太阳可不仅是取暖这么简单。阳光是人类生存和健康的必要条件。西方医学之父希波克拉底就说过,人间最好的医生是阳光空气和运动。随着冬季的到来,每天的日照时间逐渐变短,加上因天气寒健胃消食片巧搭配,不但效果翻倍,还可疏肝健脾,治这6种病大家好,我是赵医生,说起健胃消食片,大家都知道此药可以增强胃动力,帮助消化,用于消化不良腹胀腹泻打嗝反酸等各种症状,但它搭配这6种药,还可以再治这6种病,快来看看吧!第一个搭配四君少林寺年收入破亿元,数额如此惊人,这些钱都花哪儿去了?虽然佛教起源于印度,但它在中国有很大的影响,自古以来,许多人信仰佛教,这导致了中国有许多大小不同的寺庙。例如,一些大型寺庙将有更高的自然声誉,每隔一段时间就会有许多朝拜者和游客,在4大种子出局!16强出炉国羽6胜2负锁定13个席位日本冠军爆大冷北京时间10月20日消息,结束第二比赛日争夺之后,2022年世界羽联丹麦羽毛球公开赛5个单项的16强全部出炉,在这个比赛日,国羽取得了6胜2负战绩,算上第一比赛日晋级的球员,国羽一2514!史密斯中远投伊森两扣,火箭两新人上演完美首秀文水清清北京时间10月20日,火箭107117不敌老鹰。结果不尽人意,但过程很喜人,格林16分5篮板3助攻,波特21分6篮板7助攻,申京15分9篮板,上赛季历练的三位新星均有不错的孙铭徽空砍18分8助攻北京全场28次助攻力克东阳光斩获3连胜北京时间10月20日15时,202223赛季CBA常规赛第五轮,北京队对阵东阳光。历经一番角逐,北京队10076战胜东阳光夺取3连胜。数据统计北京队利夫17分7篮板4助攻,约翰逊1WTT澳门冠军赛首个冷门!王曼昱2比3不敌37岁华裔老将,遭一轮游北京时间10月20日消息,2022年世界乒乓球职业大联盟WTT澳门冠军赛展开第二日争夺。女单首轮,王曼昱状态欠佳,挽救多个赛点仍以2比3不敌37岁的法国华裔老将袁嘉楠,遭遇一轮游,