1.几个不错的关于ServiceLoader的文章,大家可以先参考一下
1) http://www.myexception.cn/program/1355384.html 这篇的后面的问题分析不错
2) http://singleant.iteye.com/blog/1497259
2.实例代码
直接上代码吧
1)接口
package com.unei.serviceloader;
/**
* Created by sun on 2015/7/25.
*/
public interface Command {
public void execute();
}
2)实现类
package com.unei.serviceloader;
/**
* Created by sun on 2015/7/25.
*/
public class ShutdownCommand implements Command{
public void execute() {
System.out.println("shutdown....");
}
}
package com.unei.serviceloader;
/**
* Created by sun on 2015/7/25.
*/
public class StartCommand implements Command{
public void execute() {
System.out.println("start....");
}
}
3)配置文件
由于是使用maven构建的项目,所以就在resources下面新建目录META-INF/services,在该目录下新建文件com.unei.serviceloader.Command,即完整的接口名
文件内容如下:
com.unei.serviceloader.ShutdownCommand
com.unei.serviceloader.StartCommand
4)main方法
package com.unei.serviceloader;
import java.util.ServiceLoader;
/**
* Created by sun on 2015/7/25.
*/
public class Main {
public static void main(String[] args) {
ServiceLoader<Command> serviceLoader=ServiceLoader.load(Command.class);
for(Command command:serviceLoader){
command.execute();
}
}
}
5)编译执行
mvn clean install -Dmaven.test.skip=true
mvn exec:java -Dexec.mainClass=com.unei.serviceloader.Main
3.activemq中的使用实例
第一次接触到ServiceLoader就是在activemq的启动过程中,所以学习了一下
activemq broker启动时,调用的是org.apache.activemq.console.command.ShellCommand类,ShellCommand.main调用runTask,runTask会调用getCommands方法,下面看一下getCommands的代码:
ArrayList<Command> getCommands() {
ServiceLoader<Command> loader = ServiceLoader.load(Command.class);
Iterator<Command> iterator = loader.iterator();
ArrayList<Command> rc = new ArrayList<Command>();
boolean done = false;
while (!done) {
try {
if( iterator.hasNext() ) {
rc.add(iterator.next());
} else {
done = true;
}
} catch (ServiceConfigurationError e) {
// it's ok, some commands may not load if their dependencies
// are not available.
}
}
return rc;
}
ServiceLoader会读取META-INF/services/org.apache.activemq.console.command.Command 配置的类并在迭代时将其实例化。
回头看一下runTask方法:
protected void runTask(List<String> tokens) throws Exception {
// Process task token
if (tokens.size() > 0) {
Command command=null;
String taskToken = (String)tokens.remove(0);
for( Command c: getCommands() ) {
if( taskToken.equals(c.getName()) ) {
command = c;
break;
}
}
if( command == null ) {
if (taskToken.equals("help")) {
printHelp();
} else {
printHelp();
}
}
if( command!=null ) {
command.setCommandContext(context);
command.execute(tokens);
}
} else {
printHelp();
}
}
该方法根据类名选择Command,并执行。
问题:
1.配置文件为什么要放在META-INF/services下面?
ServiceLoader.PREFIX定义如下:
private static final String PREFIX = "META-INF/services/";
JDK已经写死了。
但是如果ServiceLoader在load时提供Classloader,则可以从其他的目录读取。
2.ServiceLoader读取实现类是什么时候实例化的?
ServiceLoader.LazyIterator.nextService中实例化,即load的结果迭代时才会被实例化。