package com.thfund.wuyy.test;
import java.util.Iterator;
import java.util.ServiceLoader;
import com.thfund.wuyy.intf.CacheDataSource;
public class CMain {
public static void main (String[] args){
ServiceLoader<CacheDataSource> s = ServiceLoader.load(CacheDataSource.class);
Iterator<CacheDataSource>it = s.iterator();
for (;it.hasNext();){
CacheDataSource cacheDataSource = it.next();
cacheDataSource.getDataSource();
}
}
}
上面是主线程的源码:
package com.thfund.wuyy.intf;
public interface CacheDataSource {
String getDataSource();
}
此为SPI接口源码:
package com.thfund.wuyy.impl;
import com.thfund.wuyy.intf.CacheDataSource;
public class MemoryCacheDataSource implements CacheDataSource {
@Override
public String getDataSource() {
// TODO Auto-generated method stub
System.out.println(this.getClass().getClassLoader());
System.out.println("MemoryCacheDataSource");
return null;
}
}
这是具体实现类的源码:
整个项目的目录结构:
com.thfund.wuyy.intf.CacheDataSource 文件的内容:com.thfund.wuyy.impl.MemoryCacheDataSource
编译命令:
javac -d bin src/com/thfund/wuyy/impl/*.java src/com/thfund/wuyy/intf/*.java src/com/thfund/wuyy/test/*.java
此处对比C++,C++用makfile编译需要自己编写每个文件之前的依赖关系,但明显javac 不用。这时非常方便的!
运行命令:
java.exe -Dfile.encoding=UTF-8 -classpath D:\workspace\Program\practice\selftest\SPITest\bin com.thfund.wuyy.test.CMain
第一个细节点(极其简单高手勿喷):
用编译命令编译完项目目录结构如下:
D:\workspace\Program\practice\selftest\SPITest>javac -d bin src/com/thfund/wuyy/impl/*.java src/com/thfund/wuyy/intf/*.java src/com/thfund/wuyy/test/*.java
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
D:\workspace\Program\practice\selftest\SPITest>dir bin
Volume in drive D is DATA
Volume Serial Number is 526A-FE14
Directory of D:\workspace\Program\practice\selftest\SPITest\bin
2018/11/21 16:47 <DIR> .
2018/11/21 16:47 <DIR> ..
2018/11/21 16:29 <DIR> com
0 File(s) 0 bytes
3 Dir(s) 233,028,145,152 bytes free
D:\workspace\Program\practice\selftest\SPITest>
此时我们在META-INF/services 目录创建的文件还没有拷过来需要用命令拷贝一下,如果用eclipse 编译器,eclipse 会将src目录下所有的目录都拷贝到bin目录下。
下面我们来看一下ServiceLoader.load 是怎么找到配置文件并通过配置文件中的类名加载类、执行。
首先看一下ServiceLoader类的一点代码
public final class ServiceLoader<S>
implements Iterable<S>
{
private static final String PREFIX = "META-INF/services/";
// The class or interface representing the service being loaded
private Class<S> service;
/*
此处省略一万行
*/
}
我们看到ServiceLoader类写死了META-INF 目录,再来看一下我们的执行命令:
java.exe -Dfile.encoding=UTF-8 -classpath D:\workspace\Program\practice\selftest\SPITest\bin com.thfund.wuyy.test.CMain
ServiceLoader类会扫描所有classpath 目录中的META-INF/services/ 然后查找com.thfund.wuyy.intf.CacheDataSource文件中的类名称,然后创建。
加载函数如下:
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}