欢迎光临,广州澳门威尼斯人官网,澳门威尼斯城,澳门威尼斯赌场公司!                                           Tel:400-888-9999

当前位置:官网首页 > 新闻动态 > 公司新闻 > 公司新闻

SPI机制的原理和应用

SPI ,全称为 Service Provider Interface,是一种服务发现机制。它经过在ClassPath途径下的META-INF/services文件夹查找文件,主动加载文件里所界说的类。

这一机制为许多结构的扩展供给了或许,比如在Dubbo、JDBC、SpringBoot中都运用到了SPI机制。尽管他们之间的完结办法不同,但原理都差不多。今日咱们就来看看,SPI到底是何方神圣,在许多开源结构中又扮演了什么人物。

咱们先从JDK开端,经过一个很简单的比如来看下它是怎样用的。

首要,咱们需求界说一个接口,SpiService

public interface SpiService {
 void println;
仿制代码

然后,界说一个完结类,没其他意思,只做打印。

public class SpiServiceImpl implements SpiService {
 @Override
 public void println {
 System.out.println;
仿制代码

最终呢,要在resources途径下装备增加一个文件。文件姓名是接口的全限制类名,内容是完结类的全限制类名,多个完结类用换行符分隔。

com.youyouxunyin.service.impl.SpiServiceImpl
仿制代码

然后咱们就可以经过ServiceLoader.load办法拿到完结类的实例,并调用它的办法。

public static void main{
 ServiceLoader SpiService load = ServiceLoader.load;
 Iterator SpiService iterator = load.iterator;
 while ){
 SpiService service = iterator.next;
 service.println;
仿制代码

首要,咱们先来了解下ServiceLoader,看看它的类结构。

public final class ServiceLoader S implements Iterable S {
 //装备文件的途径
 private static final String PREFIX = "META-INF/services/";
 //加载的服务类或接口
 private final Class S service;
 //已加载的服务类调集
 private LinkedHashMap String,S providers = new LinkedHashMap ;
 //类加载器
 private final ClassLoader loader;
 //内部类,真实加载服务类
 private LazyIterator lookupIterator;
仿制代码

当咱们调用load办法时,并没有真实的去加载和查找服务类。而是调用了ServiceLoader的结构办法,在这里最重要的是实例化了内部类LazyIterator,它才是接下来的主角。

private ServiceLoader {
 //要加载的接口
 service = Objects.requireNonNull;
 //类加载器
 loader =  ? ClassLoader.getSystemClassLoader : cl;
 //拜访控制器
 acc =  != null) ? AccessController.getContext : null;
 //先清空
 providers.clear;
 //实例化内部类 
 LazyIterator lookupIterator = new LazyIterator;
仿制代码

查找完结类和创立完结类的进程,都在LazyIterator完结。当咱们调用iterator.hasNext和iterator.next办法的时分,实际上调用的都是LazyIterator的相应办法。

public Iterator S iterator {
 return new Iterator S  {
 public boolean hasNext {
 return lookupIterator.hasNext;
 public S next {
 return lookupIterator.next;
 .......
仿制代码

所以,咱们要点重视lookupIterator.hasNext办法,它最终会调用到hasNextService,在这里回来完结类称号。

private class LazyIterator implements Iterator S {
 Class S service;
 ClassLoader loader;
 Enumeration URL configs = null;
 Iterator String pending = null;
 String nextName = null; 
 private boolean hasNextService {
 //第2次调用的时分,现已解析完结了,直接回来
 if  {
 return true;
 if  {
 //META-INF/services/ 加上接口的全限制类名,便是文件服务类的文件
 //META-INF/services/com.viewscenes.netsupervisor.spi.SPIService
 String fullName = PREFIX + service.getName;
 //将文件途径转成URL目标
 configs = loader.getResources;
 while  || !pending.hasNext) {
 //解析URL文件目标,读取内容,最终回来
 pending = parse);
 //拿到第一个完结类的类名
 nextName = pending.next;
 return true;
仿制代码

然后当咱们调用next办法的时分,调用到lookupIterator.nextService。它经过反射的办法,创立完结类的实例并回来。

private S nextService {
 //全限制类名
 String cn = nextName;
 nextName = null;
 //创立类的Class目标
 Class ? c = Class.forName;
 //经过newInstance实例化
 S p = service.cast);
 //放入调集,回来实例
 providers.put;
 return p; 
仿制代码

到这停止,现已获取到了类的实例。

咱们最初说,SPI机制为许多结构的扩展供给了或许,其实JDBC就运用到了这一机制。

在曾经,需求先设置数据库驱动的衔接,再经过DriverManager.getConnection获取一个Connection。

String url = "jdbc:mysql:///consult?serverTimezone=UTC";
String user = "root";
String password = "root";
Class.forName;
Connection connection = DriverManager.getConnection;
仿制代码

而现在,设置数据库驱动衔接,这一进程就不再需求,那么它是怎样分辩是哪种数据库的呢?答案就在SPI。

咱们把目光回到DriverManager类,它在静态代码块里边做了一件比较重要的事。很明显,它现现已过SPI机制, 把数据库驱动衔接初始化了。

public class DriverManager {
 static {
 loadInitialDrivers;
 println;
仿制代码

详细进程还得看loadInitialDrivers,它在里边查找的是Driver接口的服务类,所以它的文件途径便是:

META-INF/services/java.sql.Driver

private static void loadInitialDrivers {
 AccessController.doPrivileged {
 public Void run {
 //很明显,它要加载Driver接口的服务类,Driver接口的包为:java.sql.Driver
 //所以它要找的便是META-INF/services/java.sql.Driver文件
 ServiceLoader Driver loadedDrivers = ServiceLoader.load;
 Iterator Driver driversIterator = loadedDrivers.iterator;
 try{
 //查到之后创立目标
 while) {
 driversIterator.next;
 } catch {
 // Do nothing
 return null;
仿制代码

那么,这个文件哪里有呢?咱们来看MySQL的jar包,便是这个文件,文件内容为:com.mysql.cj.jdbc.Driver。

上一篇:没有了

上一篇:没有了