Java类加载器不同导致SPI加载报错: Provider ... not a subtype
如下图整段代码如下,当注释掉myThread.setContextClassLoader(myClassLoader);后才正常运行package com.hotload;import java.util.ServiceLoader;class MyThread extends Thread{@Overridepublic void run() {try{ServiceLoader<A>
·
报错如下图
明明是A接口的实现类,但是报错却说不是
整段代码如下:
package com.hotload;
import java.util.ServiceLoader;
class MyThread extends Thread{
@Override
public void run() {
try{
ServiceLoader<A> serviceLoader = ServiceLoader.load(A.class);
A a = serviceLoader.iterator().next();
a.say();
} catch (Exception e){
}
}
}
public class Application {
public static void main(String[] args) throws Exception {
String rootPath = "/Users/mubi/git_workspace/java8/java8-api/src/main/java";
MyComOtherClassLoader myClassLoader = new MyComOtherClassLoader();
myClassLoader.path = rootPath;
MyThread myThread = new MyThread();
myThread.setContextClassLoader(myClassLoader);
myThread.start();
}
}
错误分析
要知道错误,首先要理解Java spi,ServiceLoader是用线程上线文加载器的,我们debug到报错代码:接口A的类加载器和接口A的实现类Aimp1的类加载器是不同的
- 接口
com.hotload.A的classLoader是AppClassLoader - 而实现类
com.hotload.Aimp1的classLoader是线程上下文传递的自定义的MyComOtherClassLoader

在Class方法public native boolean isAssignableFrom(Class<?> cls);中
不同的类加载器加载的父子类被判定为不是继承关系,所以导致了java.util.ServiceLoader第376行报错

设置正确的ClassLoader正确运行
当代码改成如下后正常运行
package com.hotload;
import java.lang.reflect.Method;
import java.util.ServiceLoader;
class MyThread extends Thread{
@Override
public void run() {
try {
// 接口类也用线程上下文自定义的类加载器加载
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class clazzA = cl.loadClass("com.hotload.A");
ServiceLoader<A> serviceLoader = ServiceLoader.load(clazzA);
Object aimp1 = serviceLoader.iterator().next();
Method method = aimp1.getClass().getMethod("say");
method.invoke(aimp1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Application {
public static void main(String[] args) throws Exception {
String rootPath = "/Users/mubi/git_workspace/java8/java8-api/src/main/java";
MyComOtherClassLoader myClassLoader = new MyComOtherClassLoader();
myClassLoader.path = rootPath;
MyThread myThread = new MyThread();
myThread.setContextClassLoader(myClassLoader);
myThread.start();
}
}

debug 查看如下(java.util.ServiceLoade的 375行 可以走完,不再fail了)

结论
在使用Java标准spi时,需要注意ServiceLoader是使用线程上下文类加载器的,需要注意接口和实现类是否是同一个类加载器加载的,否则会报错类似 java.util.ServiceConfigurationError: com.hotload.A: Provider com.hotload.Aimp1 not a subtype 的错误
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
更多推荐



所有评论(0)