ServiceLoader的功能比ClassLoader简单,它可以帮我们获取所有实现了某接口或基类的类。当然前提是ClassLoader已经加载过的类。举个例子:
定义一个接口:
public interface IService {
public String sayHello();
public String getScheme();
}
以及两个实现类:
public class HDFSService implements IService {
@Override
public String sayHello() {
return "Hello HDFS!!";
}
@Override
public String getScheme() {
return "hdfs";
}
}
public class LocalService implements IService {
@Override
public String sayHello() {
return "Hello Local!!";
}
@Override
public String getScheme() {
return "local";
}
}
将 HDFSService 和 LocalService 打包成 jar,java包的 META-INFO/services 下以IService这个类的全名来新建立一个文件,文件中的内容为两个实现类的全名:
org.hadoop.java.HDFSService
org.hadoop.java.LocalService
我们通过一下方式来调用对应的实现类:
public class ServiceLoaderTest {
public static void main(String[] args) {
//need to define related class full name in /META-INF/services/....
ServiceLoader<IService> serviceLoader = ServiceLoader
.load(IService.class);
for (IService service : serviceLoader) {
System.out.println(service.getScheme()+"="+service.sayHello());
}
}
}
插件模式
Netbean的插件就是使用ServiceLoader动态加载。先用类加载器将新的插件jar包加载到JVM,然后就可以使用ServiceLoader调用。
类责任链模式
有时候我们的程序需要根据匹配的条件,执行某段代码,如:
if (optionA) {
if (optionB) {
doSomething1();
} else {
doSomething2();
}
} else {
doSomething3();
}
直接用if else条件判断,比较复杂,不好维护。
类似上面的代码,根据不同的输入选项或命令行参数等调用不同的方法来完成某些操作,而不是单纯的返回数据。因此,这些选项是为了确定现在这个request是谁的职责,而这正是"责任链模式"要解决的问题!本节的标题为"类责任链模式",表示我的解决方案是类似"责任链模式",并不严格和它保持一致,但核心思想是一致的:使多个对象都有机会处理请求。
因此,每个RequestHandler都需提供一个接口判断自己能否处理当前请求;如果能处理,则Client调用另一个执行的接口:
public interface Handler {
public boolean accept(Properties options);
public void execute();
}
于是,上面的分支结构对应三个独立的Handler类:
public class RequestHandler1 implements Handler {
public boolean accept(Properties options) {
return options.getProperty("A") != null
&& options.getProperty("B") != null;
}
public void execute() {
doSomething1();
}
}
public class RequestHandler2 implements Handler {
public boolean accept(Properties options) {
return options.getProperty("A") != null
&& options.getProperty("B") == null;
}
public void execute() {
doSomething2();
}
}
public class RequestHandler3 implements Handler {
public boolean accept(Properties options) {
return options.getProperty("A") == null;
}
public void execute() {
doSomething3();
}
}
接下来还需要一个额外的管理类负责这些类的实例化的请求的分发:
import java.util.ServiceLoader;
import java.util.Iterator;
public class Manager {
private static Arraylist;
static {
list = new Array();
ServiceLoaderloader = ServiceLoader.load(Handler.class);
Iteratorit = loader.iterator();
while (it.hasNext()) {
list.add(it.next());
}
}
public static void process(Properties options) {
for (Handler handler : list) {
if (handler.accept(options)) {
handler.execute();
}
}
}
}
上面代码使用了服务加载功能自动实例化所有注册过的Handler子类,如果你还不了解它的原理,可查看相应的API文档。有了这些代码,已经万事具备!也许你已经发现,这样的设计和JDBC的接口不谋而合:Manager对应java.sql.DriverManager、Handler对应java.sql.Driver、RequestHandler这些类则对应数据库厂商自己实现的驱动程序。
基于这样的框架,它的代码总量也许比原来的要多,但你不再需要在一堆if else中仔细推敲代码执行的前提条件,所有的前提条件都在accept函数里;添加新的功能所要做的仅需实现一个新的类,无须修改现有代码,符合开闭原则。