serviceloader java_【java编程】ServiceLoader使用看这一篇就够了

转载:https://www.jianshu.com/p/7601ba434ff4

想必大家多多少少听过spi,具体的解释我就不多说了。但是它具体是怎么实现的呢?它的原理是什么呢?下面我就围绕这两个问题来解释:

实现: 其实具体的实现类就是java.util.ServiceLoader这个类。

要想了解一个机制的原理,首先得知道它是怎么运行的,需要什么配置,才能运行起来。然后再分解来了解实现。对于技术实现也是一样,先看这个类是怎么实现的,先让它跑起来,看到效果。然后再讲原理。

按照使用说明文档,应该分下面几个步骤来使用:

创建一个接口文件

在resources资源目录下创建META-INF/services文件夹

在services文件夹中创建文件,以接口全名命名

创建接口实现类

我们想测试一下,一般是在这个工程中建立一个测试类来测试。来看下代码片段:

接口类

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public interfaceIMyServiceLoader {

String sayHello();

String getName();

}

View Code

实现类:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public class MyServiceLoaderImpl1 implementsIMyServiceLoader {

@OverridepublicString sayHello() {return "hello1";

}

@OverridepublicString getName() {return "name1";

}

}public class MyServiceLoaderImpl2 implementsIMyServiceLoader {

@OverridepublicString sayHello() {return "hello2";

}

@OverridepublicString getName() {return "name2";

}

}

View Code

测试类:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public classTestMyServiceLoader {public static voidmain(String[] argus){

ServiceLoader serviceLoader = ServiceLoader.load(IMyServiceLoader.class);for(IMyServiceLoader myServiceLoader : serviceLoader){

System.out.println(myServiceLoader.getName()+myServiceLoader.sayHello());

}

}

}

View Code

正常情况下这里应该输出

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

name2hello2

name1hello1

View Code

看了这些步骤,想必你也知道原理了,我在这里总结下。

原理:在ServiceLoader.load的时候,根据传入的接口类,遍历META-INF/services目录下的以该类命名的文件中的所有类,并实例化返回。

相信看到这里,有的看客该爆粗话了,说啥子看着一篇就够了,这些知识点随便一搜,到处都是好伐。是的,上面说的,确实随便一搜都可以搜到,所以这里我要划重点了:

一、问题

上面说了,正常情况下会那样输出,但是你运行程序你就会发现,马丹,怎么不起作用啊,我哪里做错了,都是按照文章步骤来做的。弄的你都开始怀疑人生了。不要怀疑人生,在一个工程中做测试,确实不能实现想要的效果。

二、回忆场景

回忆一下spi的使用场景。它是给制作标准的一放用的,用来指定标准,然后不同实现方,用不同的方式实现标准供使用方使用。那标准方和实现方必然不是一个。想到这里,你应该能够向明白了吧。

三、解决方案

要解决问题,就把之前做的打jar包,引入新工程测试,这样就可以了。

四、疑问

但是有人会说标准方和实现方也可能是一个啊,好比标准方我提供一个内部的实现方案也是可以的啊。也确实有道理啊,那这种怎么实现呢?

五、思考

当然也有办法,下面就说下实现方法。想要实现上面的需求,首先要知道拦阻这个需求实现的问题,然后把这些问题都解决了,需求自然也就实现了。那就先来分析问题吧,为什么在一个工程中获取不到接口的实现类呢?经过观察发现是因为资源文件没有在classPath中,为什么这么说呢,可以看下build的目录下面是没有META-INF文件夹。现在知道了原因,这么解决呢?

六、疑问临时解决方案

最简单的方法,把资源下的META-INF文件夹拷贝到build目录下,然后再运行,发现可以了,这也就验证了,确实是这个问题造成的。搞定!

七、再次发出疑问

这样就结束了,那我总不能手动拷贝吧,这不算解决方案,只是临时方案。那要怎么解决呢?

我就不卖关子了。其实要解决这个问题,只要在编译的时候把这些文件放到build目录中就行了,是不是很简单。思路是有了,可是怎么实现呢?这个时候要用到拦截编译处理,然后再里面做这件事情。

方案一

继承AbsStractProcessor,在process方法中把资源文件移到build目录下。

方案二

这里用到了google开源的AutoService

大概看了下autoService的源码,其实它也是使用方案一的方法,拦截编译过程,然后再build目录下生成配置文件,这里来大概看下它的process方法:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

ublic boolean process(Set extends TypeElement>annotations, RoundEnvironment roundEnv) {try{returnprocessImpl(annotations, roundEnv);

}catch(Exception e) {

...return true;

}

}private boolean processImpl(Set extends TypeElement>annotations, RoundEnvironment roundEnv) {if(roundEnv.processingOver()) {

generateConfigFiles();

}else{

processAnnotations(annotations, roundEnv);

}return true;

}

View Code

这里你会发现其实就是generateConfigFiles()和processAnnotations(annotations, roundEnv)看名字可以猜到processAnnotations是处理注解的,这里实现类都实现了注解,所以这里应该是找到实现类。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

rivate void processAnnotations(Set extends TypeElement>annotations,

RoundEnvironment roundEnv) {

Set extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);for(Element e : elements) {

TypeElement providerImplementer=(TypeElement) e;

AnnotationMirror providerAnnotation= getAnnotationMirror(e, AutoService.class).get();

DeclaredType providerInterface=getProviderInterface(providerAnnotation);

TypeElement providerType=(TypeElement) providerInterface.asElement();

...

String providerTypeName=getBinaryName(providerType);

String providerImplementerName=getBinaryName(providerImplementer);

providers.put(providerTypeName, providerImplementerName);

}

}

View Code

确实如此,这里会把所有的实现类存起来。

再来看看generateConfigFiles()方法

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

private voidgenerateConfigFiles() {

Filer filer=processingEnv.getFiler();for(String providerInterface : providers.keySet()) {

String resourceFile= "META-INF/services/" +providerInterface;try{

SortedSet allServices =Sets.newTreeSet();try{

FileObject existingFile= filer.getResource(StandardLocation.CLASS_OUTPUT, "",

resourceFile);

Set oldServices =ServicesFiles.readServiceFile(existingFile.openInputStream());

allServices.addAll(oldServices);

}catch(IOException e) {

}

Set newServices = new HashSet(providers.get(providerInterface));

allServices.addAll(newServices);

FileObject fileObject= filer.createResource(StandardLocation.CLASS_OUTPUT, "",

resourceFile);

OutputStream out=fileObject.openOutputStream();

ServicesFiles.writeServiceFile(allServices, out);

out.close();

}catch(IOException e) {return;

}

}

}

View Code

这里是在build下创建META-INF目录。和我们想的一模一样。

八、总结

好了,要实现文章开头的需求,除非你觉得你比google开源AutoService的工程师写的更好,不然就直接使用AutoService吧。这篇文章不仅是分析ServiceLoader的原理,实现我们的需求,更重要的是高速我们遇到问题该怎么分析问题,解决问题。

九、扩展

其实还有很多比较好玩的,比如在拦截到编译过程时,可以再编译期生成一些有意思的代码,来帮我们实现一些自动化处理。这就需要动用我们的大脑就想了,介绍一下生成代码的库javapoet,大家可以了解一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/287504.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET7 Preview4 之OpenAPI swagger改进

在MiniAPI系列中,《.NET6之MiniAPI(十八):OpenAPI swagger》介绍了swagger在MiniAPI框架中的使用,当时留下很多不足,随着.NET7 Preview4的推出,这方面得到了很大的改进,我还是使用“十八”这篇文章的案例。…

Swift - 自定义单元格实现微信聊天界面

1,下面是一个放微信聊天界面的消息展示列表,实现的功能有: (1)消息可以是文本消息也可以是图片消息(2)消息背景为气泡状图片,同时消息气泡可根据内容自适应大小(3&#x…

[python opencv 计算机视觉零基础到实战] 十三 直方图颜色提鲜

一、学习目标 了解了均衡化的作用是什么了解灰度、YUV、彩色图片均衡化的方法是使用什么方法了解了合并通道的方法是什么了解了分离通道的方法是什么 如有错误欢迎指出~ 二、了解图像均衡化 2.1 了解直方图均衡化 图像直方图均衡化主要是对图像中的少数灰度进行压缩&#…

java 中字符串比较方法_java中常用的字符串的比较方法(两种)

比较字符串比较常用的两个方法是运算符“”和String的equals方法。使用“”比较两个字符串,是比较两个对象的的“地址”是否一致,本质就是判断两个变量是否指向同一个对象,如果是则返回true,否则返回的是false。而String类的equal…

分布式服务框架dubbo原理解析 转

alibaba有好几个分布式框架,主要有:进行远程调用(类似于RMI的这种远程调用)的(dubbo、hsf),jms消息服务(napoli、notify),KV数据库(tair)等。这个框架/工具/产品在实现的时候,都考虑到了容灾,扩展&#xff…

[python opencv 计算机视觉零基础到实战] 十五 直方图反向投影

一、学习目标 了解了直方图反向投影的一般流程了解2D直方图的使用 如有错误欢迎指出~ 二、了解直方图反向投影 2.1 了解2D直方图 需要对直方图进行反向投影,需要使用2D直方图。2D直方图需要使用calcHist方法。calcHist方法在前两节中已经有了解,现在…

关联规则java代码_重量挖掘关联规则挖掘方法,哪个大神可以将以下伪代码转换为Java代码?...

重量挖掘关联规则挖掘方法,哪个大神可以将以下伪代码转换为Java代码? 10改进的加权关联规则算法的基本步骤与Apriori算法相似: 首先找到加权支持度不小于用户指定的最小加权支持度的所有频繁项集加权关联规则,然后使用频繁项集生成所有满足最…

多种语言《九九乘法表》荟萃:C、C++、C#、JavaScript、SQL、VB、VBA、Python

九九乘法表对于我们学习循环结构,尤其是双重循环特别有帮助,本文演示用C、C++、C#、HTML、SQL、VB、VBA、Python等多种语九九乘法表。 一、C语言 #include<stdio.h> main() {int i,j;for(i=1;i<=9;i++){for(j=1;j<=i;j++){printf("%d*%d=%d\t",j,i,i*j…

[python opencv 计算机视觉零基础到实战] 十六、用opencv画画

一、学习目标 了解如何使用line方法了解如何使用rectangle方法了解如何使用ellipse方法 如有错误欢迎指出~ 二、了解OpenCV的绘图方法 2.1 了解直线绘图方法 我们在前两节中有了解使用OpenCV中的矩形绘制&#xff0c;接下来我们了解一下更多的图形绘制方法。我们在OpenCV中…

外部中断0(含知识点)

1 #include "stm32f10x.h" // 相当于51单片机中的 #include <reg51.h>2 #include "stm32f10x_gpio.h"3 #include "stm32f10x_exti.h"4 #include "misc.h"5 6 /*外部中断配置*/7 8 9 int main(void)10 {11 /*************…

[python opencv 计算机视觉零基础到实战] 十七、用鼠标画画

一、学习目标 了解长轴和短轴参数了解旋转角度参数了解起始角度参数了解终止角度参数 二、深入了解OpenCV的ellipse方法 2.1 纵横 椭圆的绘制方法在上一节中我们已经知道了是使用ellipse&#xff0c;ellipse的函数原型如下&#xff0c;为了清晰认识ellipse方法&#xff0c;…

Microsoft Build 2022 到来,开发者们做好准备了吗?

Microsoft Build 2022 将会在5月24日 - 5月26日正式举行&#xff0c;作为开发者的你是否和我一样期待本次的 Build&#xff0c;它会带来什么的技术革新以及黑科技呢&#xff1f;下面我就针对今年 Build 的几个主题结合相关技术来谈谈我对 Build 的期待。开发技术和相关工具今年…

【测绘程序设计】视距测量神器V1.0(附源程序)

神器预览: 一、视距测量概念 视距测量是利用水准仪的望远镜内十字丝分划板上的视距丝在视距尺(水准尺)上读数,根据光学和几何学原理,同时测定仪器到地面点的水平距离和高差的一种方法。 视距测量具有操作简便、速度快、不受地面起伏变化的影响的优点,被广泛应用于碎部测…

Android Studio之查找当前类的位置图标没了

1 问题 我们知道在Android studio里面如果想快速看到该文件所在的目录位置&#xff0c;点击这个图标 现在这个图标没了&#xff0c;找不到了 2 解决办法 把下面的Autoscroll from Source的勾去掉就行。

[python opencv 计算机视觉零基础到实战] 十八、用鼠标进行画画

一、学习目标 了解如何在图片中加入文字了解如何使用鼠标进行图像绘制 二、了解如何通过鼠标进行图像绘制 2.1 了解putText方法的使用 putText方法接收图像&#xff0c;文字内容&#xff0c; 坐标 &#xff0c;字体&#xff0c;大小&#xff0c;颜色&#xff0c;字体厚度这…

【测绘程序设计】坐标正算神器V1.0(附C/C#/VB源程序)

坐标正算,就是根据直线的边长、坐标方位角和一个端点的坐标,计算直线另一个端点的坐标的工作。本文用C#语言和VB语言实现坐标正算,编写坐标正算神器。 计算实例: 实例1,设直线AB的边长DAB和一个端点A的坐标XA、YA为已知,则直线另一个端点B的坐标为: XB=XA+ΔXAB YB=YA+…

Blazor University (22)路由 —— 定义路由

原文链接&#xff1a;https://blazor-university.com/routing/路由与标准 ASP.NET MVC 一样&#xff0c;Blazor 路由是一种用于检查浏览器 URL 并将其匹配到要呈现的页面的技术。路由比简单地将 URL 匹配到页面更灵活。它允许我们根据文本模式进行匹配&#xff0c;例如&#xf…

Linux文件I/O编程(二)lseek函数

文件I/O编程处理open、read、write、close&#xff0c;等必要函数对文件进行读写操作外&#xff0c;lseek、fcntl也是I/O编程很重要的函数。lseek函数lseek函数主要用来移动当前读写位置&#xff0c;第一个参数是文件描述符fd&#xff0c;第二个参数是偏移距离&#xff0c;第三…

[python opencv 计算机视觉零基础到实战] 十九、简易绘画板制作

一、学习目标 了解事件编写一个简易绘画板 二、了解如何制作简易绘画板 2.1 了解鼠标多种事件 上一节我们简单的使用opencv的图形绘制方法&#xff0c;用鼠标绘制了一些内容。上一节所响应的是简单的双击事件EVENT_LBUTTONDBLCLK&#xff0c;在OpenCV的鼠标事件中还有很多。…