单位的网站建设费会计处理,凡客诚品电话,网站文章怎么做标签,少儿编程加盟学校
一、SPI#xff08;Service Provider Interface#xff09;
1.1 介绍
SPI#xff08;Service Provider Interface#xff09;#xff0c;是JDK内置的一种 服务提供发现机制(为某个接口寻找服务实现的机制)#xff0c;可以用来启用框架扩展和替换组件#xff0c;其…
一、SPIService Provider Interface
1.1 介绍
SPIService Provider Interface是JDK内置的一种 服务提供发现机制(为某个接口寻找服务实现的机制)可以用来启用框架扩展和替换组件其核心思想就是 解耦。
模块之间基于接口编程模块之间不对实现类进行硬编码当代码里涉及具体的实现类就违反了可拔插的原则为了实现在模块装配的时候能不在程序里动态指明就需要spi了。
这里我们要跟API区分开来简单介绍一下API APIApplication Programming Interface是一种应用程序编程接口它定义了一组用于与特定软件组件或服务进行交互的函数、方法和数据结构。 其目的主要用于提供一种与特定软件组件或服务进行交互的抽象层。 比如我们常见的系统API接入的各种三方API这些API的特点是实现方式以及做好了开发者调用这些API来做一些有预期的事情。 1.2 使用场景
举个简单的例子例如芯片公司定义了一个规范需要第三方厂商去实现那么对于芯片公司方来说只需要集成对应厂商的插件就可以完成对应规范的实现机制。 形成一种插拔式的扩展手段。
如JDBC、日志框架等都用到。
Java中SPI机制主要思想是将装配的控制权移到程序之外在模块化设计中这个机制尤其重要通常由服务提供者如库或框架的开发者实现以提供特定功能的多种实现我们看个图
1.3 原理
将接口的实现类放在配置文件中在程序运行过程中读取配置文件通过反射加载实现类。
具体流程 – 读取META-INF/services/下的配置文件 – 获得所有能被实例化的类的名称 – 通过反射方法Class.forName()加载类对象并用instance()方法将类实例化 – 把实例化后的类缓存到providers对象中(LinkedHashMapString,S类型然后返回实例对象。
JDK中查找服务的实现的工具类是java.util.ServiceLoader。JDK标准的SPI会一次性加载实例化扩展点的所有实现。
1.4 使用demo
我们举例先看下完整的项目目录 我们现在需要进行股票交易有多个券商可用。
1、先定义好接口新建module-spi
package com.test;public interface ITrade {void trade();
}
2、两个接口的实现 新建module-effecta module-effectb表示不同的实现方。 这两个module分别要引用 接口的module dependenciesdependencygroupIdorg.example/groupIdartifactIdmodule-spi/artifactIdversion1.0-SNAPSHOT/versionscopecompile/scope/dependency/dependencies添加实现
package com.effectA;import com.test.ITrade;public class TradeEffectA implements ITrade {Overridepublic void trade() {System.out.println(券商 A);}
}
最后进行注册 java目录下 增加 resources/META-INF/services/ 目录在该目录下创建文件 如下图所示 module-effectb重复操作
再写一个测试方法新建一个module-main充当调用者首先添加引用
dependenciesdependencygroupIdorg.example/groupIdartifactIdmodule-spi/artifactIdversion1.0-SNAPSHOT/versionscopecompile/scope/dependencydependencygroupIdorg.example/groupIdartifactIdmodule-effectA/artifactIdversion1.0-SNAPSHOT/versionscopecompile/scope/dependencydependencygroupIdorg.example/groupIdartifactIdmodule-effectB/artifactIdversion1.0-SNAPSHOT/versionscopecompile/scope/dependency/dependenciespackage com.test;import java.util.ServiceLoader;public class Test {public static void main(String[] args) {ServiceLoaderITrade loader ServiceLoader.load(ITrade.class);for (ITrade itrade: loader) {itrade.trade();}}
}那为什么配置文件为什么要放在META-INF/services下面 可以打开ServiceLoader的代码里面定义了文件的PREFIX如下 private static final String PREFIX “META-INF/services/” 我们看下源码 public final class ServiceLoaderS implements IterableS{//路径前缀就是我们放置配置文件的目录
private static final String PREFIX META-INF/services/;// 代表被加载的类或者接口private final ClassS service;// 用于定位加载和实例化providers的类加载器private final ClassLoader loader;// 创建ServiceLoader时采用的访问控制上下文private final AccessControlContext acc;// 缓存providers按实例化的顺序排列private LinkedHashMapString,S providers new LinkedHashMap();// 懒查找迭代器private LazyIterator lookupIterator;......
}在这个例子中每次都要手动去新建META-INF/services/的文件是不是很麻烦我们可以用Autoservice来简化代码先上demo
新建module-effectc-autoservice表示不同使用autoservice自动写入配置的的实现方。 引用 接口的module-spi 及 autoservice dependenciesdependencygroupIdorg.example/groupIdartifactIdmodule-spi/artifactIdversion1.0-SNAPSHOT/versionscopecompile/scope/dependencydependencygroupIdcom.google.auto.service/groupIdartifactIdauto-service/artifactIdversion1.0-rc4/versionscopecompile/scope/dependency/dependencies添加实现 AutoService(ITrade.class)
SupportedAnnotationTypes({com.test.ITrade})
SupportedSourceVersion(SourceVersion.RELEASE_7)
public class TredeEffectC implements ITrade {Overridepublic void trade() {System.out.println(券商 C);}
}到这里就结束了是不是要简化了很多。这个机制同样适用Android如安卓组件化demo比较简单就不贴代码了。
二、Autoservice
AutoService是Google开发一个自动生成SPI清单文件的框架。
自动往 resources/META-INF/services/ 写入文件。
https://github.com/google/auto
不用它也行如果不使用它就需要手动去创建这个文件、手动往这个文件里添加服务(接口实现)。
其原理步骤
遍历找到所有带有AutoService注解的类验证AutoService注解的值是否正确遍历所有的下沉接口在META-INF/services/路径下创建文件文件名以类的接口类全路径命名在文件里写入内容实现类当前注解类的全路径
我们看下autoservice的注解处理
private void processAnnotations(Set? extends TypeElement annotations, RoundEnvironment roundEnv) {//获取所有加了AutoService注解的类Set? extends Element elements roundEnv.getElementsAnnotatedWith(AutoService.class);for (Element e : elements) {//将Element转成TypeElementTypeElement providerImplementer MoreElements.asType(e);//获取AutoServce注解指定的valueAnnotationMirror annotationMirror getAnnotationMirror(e, AutoService.class).get();//获取value集合SetDeclaredType providerInterfaces getValueFieldOfClasses(annotationMirror);//如果没有指定value报错if (providerInterfaces.isEmpty()) {error(MISSING_SERVICES_ERROR, e, annotationMirror);continue;}//遍历所有的value获取value的完整类名(例如javax.annotation.processing.Processor)for (DeclaredType providerInterface : providerInterfaces) {TypeElement providerType MoreTypes.asTypeElement(providerInterface);//判断是否是继承关系是则放入providers缓存起来否则报错if (checkImplementer(providerImplementer, providerType, annotationMirror)) {providers.put(getBinaryName(providerType), getBinaryName(providerImplementer));} else {//报错代码略}}}}注解处理完毕就会生成SPI注册文件。如果SPI路径上文件已经存在先要把已存在的SPI清单读进内存再把新的provider加进去然后全部写出覆盖原来的文件。这部分逻辑如下
private void generateConfigFiles() {//获取文件工具类processingEnv是AbstractProcessor的成员变量直接拿来用。Filer filer processingEnv.getFiler();//遍历之前解析的providers缓存for (String providerInterface : providers.keySet()) {//providerInterface就是value字段指定的接口例如javax.annotation.processing.ProcessorString resourceFile META-INF/services/ providerInterface;log(Working on resource file: resourceFile);try {SortedSetString allServices Sets.newTreeSet();try {//已经存在的SPI文件FileObject existingFile filer.getResource(StandardLocation.CLASS_OUTPUT, , resourceFile);//SPI文件中的service条目清单SetString oldServices ServicesFiles.readServiceFile(existingFile.openInputStream());log(Existing service entries: oldServices);allServices.addAll(oldServices);} catch (IOException e) {log(Resource file did not already exist.);}//新的service条目清单SetString newServices new HashSet(providers.get(providerInterface));//如果已经存在则不处理if (!allServices.addAll(newServices)) {log(No new service entries being added.);continue;}//以下是将缓存的services写入文件中。log(New service file contents: allServices);FileObject fileObject filer.createResource(StandardLocation.CLASS_OUTPUT, , resourceFile);try (OutputStream out fileObject.openOutputStream()) {ServicesFiles.writeServiceFile(allServices, out);}log(Wrote to: fileObject.toUri());} catch (IOException e) {fatalError(Unable to create resourceFile , e);return;}}}所以我们将AutoService加到java项目中其实就是引入了AutoServiceProcessor这个注解处理器帮助我们处理AutoService注解将我们的服务(一般是APT类也可以是其它的类通过value指定)自动注册进SPI文件中。
三、Javapoet
javapoet是square推出的开源java代码生成框架提供Java Api生成.java源文件 这个框架功能非常实用。