Java SPI 与 dubbo SPI

面向接口编程+策略模式

实现 建立接口

Robot

public interface Robot { /** * 测试方法1 */ void sayHello(); } 多个实现类实现接口

RobotA

public class RobotA implements Robot { public RobotA() { System.out.println("Happy RobotA is loaded"); } @Override public void sayHello() { System.out.println("i am a very very happy Robot "); } public void sayBye(){} }

RobotB

public class RobotB implements Robot { public RobotB() { System.out.println("SB RobotB is loaded"); } @Override public void sayHello() { System.out.println("i am a da sha bi "); } public void sayBye(){} } 配置实现类接口

在META-INF/services目录下建立一个以接口全限定名为名字的文件,里面的内容是实现类的全限定名

原理

通过ServiceLoader配置文件中的全限定名加载所有实现类,根据迭代器获取具体的某一个类

我们通过对下面一段代码的分析来说明

ServiceLoader<Robot> serviceLoader=ServiceLoader.load(Robot.class); serviceLoader.forEach(Robot::sayHello);

load(Robot.class)这个方法的目的只是为了设置类加载器为线程上下文加载器,我们当然可以不这么做,直接调用load(Class service,ClassLoader loader)方法

public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }

这个load方法其实也没有做什么实质的事,仅仅是实例化了一个ServiceLoad对象返回罢了

public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) { return new ServiceLoader<>(service, loader); }

那是不是构造方法做了最核心的事呢?

private ServiceLoader(Class<S> svc, ClassLoader cl) { service = Objects.requireNonNull(svc, "Service interface cannot be null"); loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; reload(); } public void reload() { //这里的provider是一个对于已实例化对象的缓存,为Map类型 providers.clear(); lookupIterator = new LazyIterator(service, loader); }

没有,这里仅仅只是检验了参数和权限这样一些准备操作.然后实例化了一个LazyIterator

这是LazyIterator的构造函数

private LazyIterator(Class<S> service, ClassLoader loader) { this.service = service; this.loader = loader; }

然后....,没了,ServiceLoader<Robot> serviceLoader=ServiceLoader.load(Robot.class);执行完毕了,到这里,并没有实例化我们所需要的Robot对象,而仅仅只是返回了一个ServiceLoader对象

这时候如果我们去看serviceLoader的对象方法是这样的

Java SPI 与 dubbo SPI

有用的只有这三个方法,reload上面已经提到过,只是重新实例化一个对象而已.

而另外两个iterator()是个迭代器,foreach也只是用于迭代的语法糖罢了.如果我们debug的话,会发现foreach的核心依旧会变成iterator(),好了,接下来重点看iterator()

public Iterator<S> iterator() { return new Iterator<S>() { Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator(); public boolean hasNext() { if (knownProviders.hasNext()) return true; return lookupIterator.hasNext(); } public S next() { if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); } public void remove() { throw new UnsupportedOperationException(); } };

这个方法实际上是返回了一个Iterator对象.而通过这个Iterator,我们可以遍历获取我们所需要的Robot对象.

我们来看其用于获取对象的next方法

public S next() { if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); }

这个方法是先在缓存里找,缓存里找不到,就需要用最开始的实例化的lookupIterator找

再来看看它的next方法

public S next() { if (acc == null) { return nextService(); } else { PrivilegedAction<S> action = new PrivilegedAction<S>() { public S run() { return nextService(); } }; return AccessController.doPrivileged(action, acc); } }

这方法的核心是nextService,我们继续看实现,这个方法比较长,我贴一部分核心

if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<?> c = null; try { c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); }

用hasNextService()判断是否还可以继续迭代,通过class.forName反射获取实例,最后再加入到provider缓存中.于是基本逻辑就完成了.那nextName哪来的.是在hasNextService()中获取的.

依旧只有核心代码

//获取文件 String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); //解析文件配置 while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next();

根据前缀(即META-INF/services)和接口的全限定名去找到对应的配置文件.然后加载里面的配置,获取具体实现类的名字.

Dubbo增强SPI 实现 建立接口

与原生SPI不同,dubbo需要加入@SPI注解

Robot

@SPI public interface Robot { /** * 测试方法1 */ void sayHello(); } 多个实现类实现接口

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzspjd.html