Java之SPI机制
JavaSPI机制基本使用
创建一个接口
public interface DemoService {
void print();
}
创建一个实现类
public class DemoServiceImpl implements DemoService {
@Override
public void print() {
System.out.println("print......");
}
}
在resources下创建META-INF文件夹,在创建一个services目录,最后创建一个文件名为接口全类名的文件。
文件内容为实现类全路径名
多个实现类按行书写,每行一个实现类
com.gitee.kenewstar.java.spi.DemoServiceImpl
com.gitee.kenewstar.java.spi.DemoServiceImpl2
测试SPI加载类
public class TestSPI {
public static void main(String[] args) {
ServiceLoader<DemoService> load = ServiceLoader.load(DemoService.class);
for (DemoService demoService : load) {
demoService.print();
}
}
}
如图获取DemoService接口的两个实现类的实例对象,并执行print()方法。
手写实现JavaSPI机制
我们通过查看ServiceLoader类的源码从而简单实现JavaSPI的功能
代码如下:
public class DemoLoader<T> implements Iterable<T> {
private static final String PREFIX = "META-INF/services/";
private ClassLoader loader;
private String fullName;
private List<T> serviceList = new ArrayList<>();
private DemoLoader(String className) {
loader = Thread.currentThread().getContextClassLoader();
fullName = PREFIX + className;
URL resource = loader.getResource(fullName);
try {
BufferedReader reader
= new BufferedReader(new FileReader(new File(resource.toURI())));
String impl;
while ((impl = reader.readLine()) != null) {
impl = impl.trim();
serviceList.add((T) Class.forName(impl).newInstance());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T> DemoLoader<T> loader(Class<T> service) {
return new DemoLoader<>(service.getName());
}
@Override
public Iterator<T> iterator() {
return serviceList.iterator();
}
public static void main(String[] args) {
DemoLoader<DemoService> loader = DemoLoader.loader(DemoService.class);
for (DemoService demoService : loader) {
demoService.print();
}
}
}
main方法执行结果如下:
SpringBoot之SPI机制的扩展
SpringBoot中的自动配置通过spring.factories的实现方式
接下来我们通过手动实现SpringBoot的spring.factories文件自动配置
在SpringBoot启动类自动注入注解中如下:
通过@Import注解导入AutoConfigurationImportSelector类
获取所有配置类通过getCandidateConfigurations()方法
方法中通过SpringFactorisLoader类加载所有的spring.factories配置文件中的所有类
接下来我们简单的实现SpringFactoriesLoader加载获取配置类信息
public final class DemoSpringFactoriesLoader {
private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private Map<String, List<String>> configListMap = new HashMap<>();
private ClassLoader loader;
private DemoSpringFactoriesLoader() {
loader = Thread.currentThread().getContextClassLoader();
try {
Enumeration<URL> resources = loader.getResources(FACTORIES_RESOURCE_LOCATION);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
Properties properties = new Properties();
properties.load(url.openStream());
properties.forEach((k, v) -> {
List<String> configList = configListMap.get(String.valueOf(k));
if (Objects.isNull(configList)) {
configList = new ArrayList<>();
}
configList.addAll(Arrays.asList(String.valueOf(v).split(",")));
configListMap.put((String) k, configList);
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Map<String, List<String>> getConfigListMap() {
return this.configListMap;
}
public static Map<String, List<String>> loadFactories() {
return new DemoSpringFactoriesLoader().getConfigListMap();
}
public static void main(String[] args) {
Map<String, List<String>> configListMap = DemoSpringFactoriesLoader.loadFactories();
configListMap.forEach((k, v) -> {
System.out.println(k);
System.out.println(v);
});
}
}
spring.factories文件如下:
执行结果如下: