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

java菜鸟到大佬全网最全java的SPI讲解

  目录:
  一:什么是SPI机制?
  二:SPI机制的广泛应用?
  三:带着问题深入理解SPI机制?
  一:什么是SPI机制
  SPI全称:ServiceProviderInterface
  字面意思:服务提供者的接口
  我们来拆开分析一下,首先它是一个接口,然后是提供服务调用者使用的。
  为什们要使用spi:
  在面向对象编程中,基于开闭原则和解耦的需要,一般建议用接口进行模块之间通信编程,通常情况下调用方模块是不会感知到被调用方模块的内部具体实现。
  为了实现在模块装配的时候不用在程序里面动态指明,这就需要一种服务发现机制。JavaSPI就是提供了这样一个机制:为某个接口寻找服务实现的机制。这有点类似IoC的思想,将装配的控制权移交到了程序之外。
  重新理解spi机制:
  SPI是专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。
  SPI将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。
  SPI整体机制图如下:
  二:SPI机制的简单示例
  这里分享一个我司做资源搜索的实现方案:
  2。1先定义一个搜索接口:
  2。2视频搜索实现:
  2。2试卷搜索实现:
  2。3在项目METAINFservices创建一个文本文件:名称为接口的全限定名,内容为实现类的全限定名:
  2。4编写测试类:
  测试代码打印结果:
  视频搜索!!!!
  试卷搜索!!!!
  2。5ServiceLoader源码解析
  便我们看一下ServiceLoader的源码信息,首先通过常量的定义,我们可以看到为什么要将文件配置在METAINFservices下了。
  ,简单介绍一下该类的基本操作流程。通过ServiceLoader的load(Classservice)方法进入程序内部;
  上面load方法内获得到ClassLoader,并再此调用内部的load(Classservice,lassLoaderloader)方法,该方法内会创建ServiceLoader对象,并初始化一些常量。
  ServiceLoader的构造方法内会调用reload方法,来清理缓存,初始化LazyIterator,注意此处是Lazy,也就懒加载。此时并不会去加载文件下的内容。
  当遍历器被遍历时,才会去读取配置文件。
  5。关于读取METAINFservices下配置文件的核心代码如下:
  同过以上代码我们会发现,其实ServiceLoader扫描了所有jar包下的配置文件。然后通过解析全限定名获得,并在遍历时通过Class。forName进行实例化。三:SPI机制的广泛应用
  3。1SPI机制在JDBCDriverManager中的应用
  在JDBC4。0之前,我们开发有连接数据库的时候,通常会用Class。forName(com。mysql。jdbc。Driver)这句先加载数据库相关的驱动,然后再进行获取连接等的操作。而JDBC4。0之后不需要用Class。forName(com。mysql。jdbc。Driver)来加载驱动,直接获取连接就可以了,现在这种方式就是使用了Java的SPI扩展机制来实现。
  JDBC接口的定义
  首先在java中只是定义了接口java。sql。Driver,具体的实现都是由不同厂商来提供的。
  在mysql中的实现
  在mysql的jar包mysqlconnectorjava6。0。6。jar中,可以找到METAINFservices目录,该目录下会有一个名字为java。sql。Driver的文件,文件内容是com。mysql。cj。jdbc。Driver,这里面的内容就是针对Java中定义的接口的实现。
  在postgresql中的实现
  同样在postgresql的jar包postgresql42。0。0。jar中,也可以找到同样的配置文件,文件内容是org。postgresql。Driver,这是postgresql对Java的java。sql。Driver的实现。
  JDBC的SPI机制
  首先来个简单的代码示例:
  程序在加载DriverManager类时,会将MySQL的Driver对象注册进DriverManager中,这是SPI思想的一个典型的实现。得益于SPI思想,应用程序中无需指定类似com。mysql。cj。jdbc。Driver这种全类名,尽可能地将第三方驱动从应用程序中解耦出来。
  源码分析:
  DriverManager是管理Jdbc驱动的基础服务类,位于Java。sql包中,由boot类加载器来进行加载。加载该类时,会先执行如下代码块:
  上面静态代码块会执行loadInitialDrivers()方法,用于加载各个数据库的驱动。代码如下:
  ServiceLoader。load(Driver。class)此方法会实例化一个ServiceLoader对象,并且注入线程上下文类加载器和Driver。class;loadedDrivers。iterator():此方法获得ServiceLoader对象的迭代器;driversIterator。hasNext():此方法用于查找Driver类;driversIterator。next():在实现的next()方法中进行类加载,使用上面的线程上下文类加载器。
  ServiceLoader。load(Driver。class);代码及相关调用方法如下:
  经过上述过程,使用成员变量privatefinalClassLoaderloader;引用传入的类加载器,使用service接收Driver。class。同时,上述过程中实例化了一个LazyIterator对象,并用成员变量lookupIterator来引用。
  执行ServiceLoader的hasNext()方法时最终会调用lookupIterator迭代器的hasNext()方法(此处暂且省略调用过程),如下:
  上述过程通过configsloader。getResources(fullName)来查找并实现Driver接口的类。
  同样,ServiceLoader的迭代器的next()方法最终会调用lookupIterator迭代器的next()方法,如下:
  可以看到,next()会最终调用到nextService()方法,并在此方法中通过cClass。forName(cn,false,loader);执行类加载。此处的loader也是由ServiceLoader中的loader传入的,即为前文提到的线程上下文类加载器。
  经历了上述ServiceLoader类中的一系列操作之后(包括服务发现和类加载),位于mysql驱动包中的Driver类会被初始化。该类如下所示
  上述Driver类加载时,会执行静态代码块,即执行DriverManager。registerDriver(newDriver());方法向DriverManager中注册一个Driver实例。
  我们再回到DriverManager类中,看看registerDriver方法:
  会将该MySQL驱动添加到成员变量registeredDrivers中,该成员变量存放已注册的jdbc驱动列表,如下:
  这样一来,服务发现、类加载、驱动注册便到此结束。接下来,应用程序执行数据库连接操作时,会调用getConnection方法,遍历registeredDrivers,获取驱动,建立数据库连接。
  3。2SPI机制在CommonLogging中的实现
  commonslogging是Apachecommons类库中的一员。commonslogging自带了日志实现类,但是功能比较简单,更多的是将其作为门面,底层实现依赖其它框架。commonslogging能够选择使用Log4j还是JDKLogging,但是它并不依赖Log4j或JDKLogging的API。commonslogging会自动检测项目classpath中包含的支持的框架,从而自动选择实现框架。使用commonslogging能否灵活地选择使用哪些日志框架,而且不需要修改源代码。
  首先,日志实例是通过LogFactory的getLog(String)方法创建的:
  Log接口部分代码:
  LogFatory是一个抽象类,它负责加载具体的日志实现,分析其FactorygetFactory()方法:
  可以看出,抽象类LogFactory加载具体实现的步骤如下:
  1。从vm系统属性org。apache。commons。logging。LogFactory
  2。使用SPI服务发现机制,发现org。apache。commons。logging。LogFactory的实现
  3。查找classpath根目录commonslogging。properties的org。apache。commons。logging。LogFactory属性是否指定factory实现
  4。使用默认factory实org。apache。commons。logging。impl。LogFactoryImpl
  LogFactory的getLog()方法返回类型是org。apache。commons。logging。Log接口,提供了从trace到fatal方法。可以确定,如果日志实现提供者只要实现该接口,并且使用继承自org。apache。commons。logging。LogFactory的子类创建Log,必然可以构建一个松耦合的日志系统四:带着问题深入理解SPI机制SPI和API的区别是什么
  API图解:
  SPI图解:
  如上面所示:
  API依赖的接口位于实现者的包中,概念上更接近于实现方,组织上存在于实现者的包中,实现和接口同时存在在实现者的包中
  SPI依赖的接口在调用方的包中,概念上更接近于调用方,组织上位于调用者的包中,实现逻辑在单独的包中,实现可插拔。SPI机制的缺陷
  通过SPI机制能够大大地提高接口设计的灵活性,但是SPI机制也存在一些缺点,比如:
  1。不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。
  2。获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类。(Spring的BeanFactory,ApplicationContext就要高级一些了。)
  3。多个并发多线程使用ServiceLoader类的实例是不安全的。
  4。3手撸一个ServiceLoaderpackageedu。jiangxuan。up。service;importjava。io。BufferedReader;importjava。io。InputStream;importjava。io。InputStreamReader;importjava。lang。reflect。Constructor;importjava。net。URL;importjava。net。URLConnection;importjava。util。ArrayList;importjava。util。Enumeration;importjava。util。List;publicclassMyServiceLoaderS{对应的接口Class模板privatefinalClassSservice;对应实现类的可以有多个,用List进行封装privatefinalListSprovidersnewArrayList();类加载器privatefinalClassLoaderclassLoader;暴露给外部使用的方法,通过调用这个方法可以开始加载自己定制的实现流程。publicstaticSMyServiceLoaderSload(ClassSservice){returnnewMyServiceLoader(service);}构造方法私有化privateMyServiceLoader(ClassSservice){this。serviceservice;this。classLoaderThread。currentThread()。getContextClassLoader();doLoad();}关键方法,加载具体实现类的逻辑privatevoiddoLoad(){try{读取所有jar包里面METAINFservices包下面的文件,这个文件名就是接口名,然后文件里面的内容就是具体的实现类的路径加全类名EnumerationURLurlsclassLoader。getResources(METAINFservicesservice。getName());挨个遍历取到的文件while(urls。hasMoreElements()){取出当前的文件URLurlurls。nextElement();System。out。println(Fileurl。getPath());建立链接URLConnectionurlConnectionurl。openConnection();urlConnection。setUseCaches(false);获取文件输入流InputStreaminputStreamurlConnection。getInputStream();从文件输入流获取缓存BufferedReaderbufferedReadernewBufferedReader(newInputStreamReader(inputStream));从文件内容里面得到实现类的全类名StringclassNamebufferedReader。readLine();while(className!null){通过反射拿到实现类的实例Classlt;?clazzClass。forName(className,false,classLoader);如果声明的接口跟这个具体的实现类是属于同一类型,(可以理解为Java的一种多态,接口跟实现类、父类和子类等等这种关系。)则构造实例if(service。isAssignableFrom(clazz)){Constructorlt;?extendsSconstructor(Constructorlt;?extendsS)clazz。getConstructor();Sinstanceconstructor。newInstance();把当前构造的实例对象添加到Provider的列表里面providers。add(instance);}继续读取下一行的实现类,可以有多个实现类,只需要换行就可以了。classNamebufferedReader。readLine();}}}catch(Exceptione){System。out。println(读取文件异常);}}返回spi接口对应的具体实现类列表publicListSgetProviders(){returnproviders;}}
  主要的流程就是:通过URL工具类从jar包的METAINFservices目录下面找到对应的文件,读取这个文件的名称找到对应的spi接口,通过InputStream流将文件里面的具体实现类的全类名读取出来,根据获取到的全类名,先判断跟spi接口是否为同一类型,如果是的,那么就通过反射的机制构造对应的实例对象,将构造出来的实例对象添加到Providers的列表中。

自由真好自由真好。这是一句电视连续剧中的台词。自喜欢上了今日头条之后,我己很少看电视,更无暇观看各类电视连续剧。但是,今晚我看了央视总台电视剧频道播出的电视连续剧巜玫瑰之战,在第二十三集中破产的斯里兰卡怎么了斯里兰卡崩溃了,它不会是最后一个!那到底破产的斯里兰卡发生什么了呢7月14日,斯里兰卡首都科伦坡爆发了抗议游行,反对者撤离总理官邸后高呼口号。作为一个斯里兰卡人,我发现看国际新闻报早上刚刮了胡子,下午就长出来了,胡须生长太快说明了啥?在平时我们常说,爱美是女人的天性,其实到了现代社会,每一个人都非常重视自己的外在形象,男性也不例外。对于绝大多数男性来说,每天早上起床之后都会洗头发刮胡子,在这上面花费的时间并不比星界龙频频出问题,难怪被大家戏称为设计师亲儿子在S7赛季中,星界龙可谓问题不断,一直非常热门。刚开始的时候,强化符文有星界龙转职纹章,只是黄金级,但是对于星界龙而言,对彩色更重要,因为有了星界龙转职纹章之后,可以轻松追三星四费印度外长谈采购俄石油人均年收入2000美元,我们承受不起高油价据印度报业托拉斯17日报道,印度外长苏杰生称,美国和一些其他国家可能不赞同印度购买俄罗斯石油,但他们也接受了这个事实,印度并没有对自己的立场进行辩护,而是让他们意识到政府在不合理高32万元起售?皇冠陆放2。0T成都车展上市,进口2。0T8AT日前,Auto情报处从相关渠道获悉,皇冠陆放2。0T将于成都车展正式上市,新车之前已经在工信部申报图,售价方面,按照汉兰达2。0T的价格来看,皇冠陆放2。0T的起售价预计在32万元中方是否同意撤销中巴经济走廊事务局?外交部回应2022年8月19日外交部发言人汪文斌主持例行记者会印度广播公司记者据巴基斯坦媒体报道,巴基斯坦总理夏巴兹周三批准撤销中巴经济走廊事务局,但须征得中方同意。巴政府表示,这一决定将有台海会打起来吗?台海会打起来吗?肯定不会。因为美国很清楚,一但中国和美国打起来,根据中国的毅志和决心以及近海的军事优势,美国将在地缘政治上完全失去治衡中国最有用的一枚棋子,台湾。最关键的是,一但失老年人白天总是打瞌睡?排查是否被3种病缠身,提前防备相信不少老年人都有这样一个感受,就是晚上睡不着,白天睡不够,很多中老年人在晚上入睡的时候往往躺在床上,大眼瞪天花板半天睡不着觉,可是等到白天的时候却又感觉脑袋昏昏沉沉,经常不停的打B站推出老粉计划用户可投3个币,获得特殊身份铭牌IT之家8月19日消息,B站哔哩哔哩近日推出了老粉计划,用户可投注3个硬币表达对UP主的特别支持,待UP主粉丝突破1000时,用户将成为UP主的老粉。IT之家了解到,用户需将哔哩哔长沙某院医生被曝作风问题!若全部属实,简直是丧心病狂的恶魔今天是中国医师节,和教师节一样,是赞美和歌颂咱们白衣天使的好日子。众所周知,医生在任何国家都是一个被人敬仰,充满神圣的职业,某种程度上,医生象征着世间的纯洁与美好,救死扶伤的同时,
老年人常吃核桃宜健康核桃富含保护心脏的3,3属于一种多不饱和脂肪酸,可以防止血液凝聚血管收缩,对心脏和血管健康有益。要想获得3脂肪酸,除了吃深海鱼类,还可以吃几个核桃。核桃可以生吃,也可以炒菜炖粥吃。中医专家提醒晨起黄金10分钟,常做这6件小事,助您健康长寿俗话说一日之计在于晨,尤其是起床后这段时间,即使一个很小的动作,都可能给一天的生活带来影响。对此,专家总结早晨起床多做六个小动作,更健康长寿。一揉肚子早起做做揉肚子的运动,可以有效小阳人吃什么好得快?现在走街串巷的碰到熟人,不再是问你吃了吗而是阳了吗朋友圈都是阳人的聚集地了。全面开放,让群众用自身的免疫力去抵抗病毒,症状的轻重与身体的免疫力有很大的关系。专家建议,多摄入蛋白质提如果没有退烧药,这些物理降温方法你得知道经过无数人的亲身实践,新冠病毒带给人体伤害最大的就是发烧,一般高烧39度到40度。如果长时间高烧没有及时缓解,有可能会造成身体脱水,影响酸碱平衡和消化功能下降,甚至惊厥,而高烧41感染了奥密克戎,怎样快速从阳转阴?做好这5点,加速病毒排出最近感染奥密克戎的朋友逐渐增多,感染病毒以后,身体会出现各种不适,怎么才能快速从阳转阴呢?做好以下5点,加速病毒的排出1多喝水发烧时需要多喝水,可以及时补充体内水分,防止虚脱,帮助送瘟神,加时赛开始了有序放开,既不是防疫结束了,也不是抗疫刚刚开始。与疫情搏斗三年,我们已经在上半场的突击抗疫下半场的常态化防控中全力以赴,取得优异成绩,现在算是进入了加时赛。加时赛往往是紧张刺激,甚科尔你不光得能接受追梦带来的激情ampampamp竞争力也得接受他被驱逐直播吧12月16日讯在昨天勇士对阵步行者的比赛中,追梦格林因两次技术犯规被罚出场。今天,勇士主帅科尔在参加DamonAndRatto的播客节目时谈到了此事。他说道这令人沮丧,但我能教师工作总结(大班)作者梁子馨晨原创一学期又过去了。大班是幼小衔接最关键的一年。幼儿即将离开幼儿园,进入一个全新的环境学习生活了。如何使幼儿更好地适应小学的学习和生活是我们的工作重点,因此,加强幼儿的加拿大搜救协会称苹果iPhone的车祸检测功能已影响工作IT之家12月16日消息,苹果为iPhone和AppleWatch新增了车祸检测功能,系统在检测到疑似车祸碰撞之后就会向当地政府发出求救。不过该功能目前仍不完善,在过山车滑雪的时候为应对低温天气久事公交做好车辆保障工作图说为做好冬季低温下的车辆保障工作,久事公交执行了一系列保障措施采访对象供图新民晚报讯(记者任天宝)记者今天从久事公交巴士五公司了解到,为做好冬季低温下的车辆保障工作,公交公司执行履职耕耘结硕果团结奋进新征程民建十一大以来工作回眸编者按凝心聚力启征程,继往开来续华章。回望过去,民建不忘合作初心,集全会之智,聚全会之力,为夺取新时代中国特色社会主义伟大胜利贡献了智慧和力量展望未来,民建同心携手奋进,矢志履职尽
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网