一般使用接口的实现类都是静态new一个实现类赋值给接口引用,如下:
HelloService service = new HelloImpl();
如果需要动态的获取一个接口的实现类呢?全局扫描全部的Class,然后判断是否实现了某个接口?代价太大,一般不会这么做。一种合适的方式就是使用配置文件,把实现类名配置在某个地方,然后读取这个配置文件,获取实现类名。JDK给我们提供的TestServiceLoader 就是这种方式。
在实现类的jar包的META-INF下新建一个文件夹services,并在services下新建一个文件,以接口的全限定名为文件名,内容为实现类的全限定名。
通过以下的例子来分析实现原理.
1. 新建一个接口,2个实现类。
package com.test.loader;
public interface HelloService {
public void sayHello();
}
package com.test.loader;
public class Dog implements HelloService {
@Override
public void sayHello() {
System.out.println("bark bark bark...");
}
}
package com.test.loader;
public class Sheep implements HelloService {
@Override
public void sayHello() {
System.out.println("bleat bleat bleat...");
}
}
2. 分别把接口、2个实现类打成3个jar包,放在D盘下。
3. 在Dog.jar、Sheep.jar分别加上META-INF下新建一个文件夹services,并在services下新建一个文件,以接口的全限定名为文件名,内容为实现类的全限定名。如下:
4. 使用指定的ClassLoader不包含实现类
public static void notInTheClassLoader() throws MalformedURLException {
ClassLoader serviceCL = new URLClassLoader(new URL[] { new URL("file:" + "D:/HelloService.jar") },
TestServiceLoader.class.getClassLoader().getParent());
/* 指定的ClassLoader没有实现类,所以扫描不到META-INF/services/com.test.loader.HelloService */
ServiceLoader helloServices = ServiceLoader.load(HelloService.class, serviceCL);
Iterator it = helloServices.iterator();
while (it.hasNext()) {
HelloService service = it.next();
service.sayHello();
}
}
结果不会打印任何信息。
5. 指定的ClassLoader包含实现类
public static void inTheClassLoader() throws MalformedURLException {
ClassLoader serviceCL = new URLClassLoader(
new URL[] { new URL("file:" + "D:/Dog.jar"), new URL("file:" + "D:/Sheep.jar") },
TestServiceLoader.class.getClassLoader().getParent());
/* 实现类在指定的ClassLoader,所以可以扫描META-INF/services/com.test.loader.HelloService */
ServiceLoader helloServices = ServiceLoader.load(HelloService.class, serviceCL);
Iterator it = helloServices.iterator();
while (it.hasNext()) {
HelloService service = it.next();
service.sayHello();
}
}
结果如下:
bark bark bark...
bleat bleat bleat...
6. 使用指定的ClassLoader加载接口类,不指定ClassLoader加载实现类。
public static void defaultClassLoader() throws MalformedURLException, ClassNotFoundException {
ClassLoader serviceCL = new URLClassLoader(new URL[] { new URL("file:" + "D:/HelloService.jar"),
new URL("file:" + "D:/Dog.jar"), new URL("file:" + "D:/Sheep.jar") },
TestServiceLoader.class.getClassLoader().getParent());
/* 默认会使用 ClassLoader.getSystemClassLoader() */
ServiceLoader helloServices = ServiceLoader
.load(((Class) (serviceCL.loadClass("com.test.loader.HelloService"))));
Iterator it = helloServices.iterator();
while (it.hasNext()) {
HelloService service = it.next();
service.sayHello();
}
}
结果不打印任何信息。
7. 把2个实现jar加到工程的Build Path里面,不指定ClassLoader。
public static void notSpecifyClassLoader() {
ServiceLoader helloServices = ServiceLoader.load(HelloService.class);
Iterator it = helloServices.iterator();
while (it.hasNext()) {
HelloService service = it.next();
service.sayHello();
}
}
结果如下:
bark bark bark...
bleat bleat bleat...
1. 构造函数,如果不指定ClassLoader或者指定的为null,则使用ClassLoader.getSystemClassLoader() ,即AppClassLoader。
private ServiceLoader(Class 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();
}
2. 遍历有两个方法
hasNext会调用hasNextService,如果指定的ClassLoader为空(一般不会为空,构造函数会初始化),则调用ClassLoader.getSystemClassLoader().getResources,否则调用指定的ClassLoader的getResources方法,获取META-INF/services/接口的全限定名称,如META-INF/services/com.test.loader.HelloService,从这个文件中找出实现类全限定名称。
next会调用nextService,根据hasNextService获取的实现类信息,使用指定的ClassLoader进行加载和实例化。
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
private S 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");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
注:以上代码基于JDK1.8.0_144。