Java原生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
的对象方法是这样的
有用的只有这三个方法,reload
上面已经提到过,只是重新实例化一个对象而已.
而另外两个iterator()
是个迭代器,foreach
也只是用于迭代的语法糖罢了.如果我们debug的话,会发现foreach
的核心依旧会变成iterator()
,好了,接下来重点看iterator()
public Iterator<S> iterator() {
return new Iterator<