1. 几个不错的关于ServiceLoader的文章,大家可以先参考一下
1) http://www.myexception.cn/program/1355384.html 这篇的后面的问题分析不错
2.实例代码
直接上代码吧
1) 接口
package com.ctrip.lzyan.test.element.serviceloader.sample1; public interface Command { public void execute(); }
2) 实现类
package com.ctrip.lzyan.test.element.serviceloader.sample1; public class StartCommand implements Command { public void execute() { System.out.println("start...."); } }
package com.ctrip.lzyan.test.element.serviceloader.sample1; public class ShutdownCommand implements Command { @Override public void execute() { System.out.println("shutdown...."); } }
3) 配置文件
由于是使用maven构建的项目,所以就在resources下面新建目录META-INF/services,在该目录下新建文件com.ctrip.lzyan.test.element.serviceloader.sample1.Command, 即完整的接口名
文件内容如下:
com.ctrip.lzyan.test.element.serviceloader.sample1.StartCommand
com.ctrip.lzyan.test.element.serviceloader.sample1.ShutdownCommand
4) 测试类
package com.ctrip.lzyan.test.element.serviceloader.sample1; import java.util.ServiceLoader; public class CommandTester { 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.ctrip.lzyan.test.element.serviceloader.sample1.CommandTester
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/";
但是如果ServiceLoader在load时提供Classloader,则可以从其他的目录读取。
ServiceLoader.LazyIterator.nextService中实例化,即load的结果迭代时才会被实例化。
转自:
http://blog.csdn.net/unei66/article/details/47051017