Java ServiceLoader(SPI)学习

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的结果迭代时才会被实例化。

你可能感兴趣的:(ServiceLoader,SPI)