您的位置:首页 > 编程语言 > Java开发

Java ServiceLoader(SPI)学习

2016-07-16 20:49 375 查看

1.几个不错的关于ServiceLoader的文章,大家可以先参考一下

1) http://www.myexception.cn/program/1355384.html 这篇的后面的问题分析不错
2) http://singleant.iteye.com/blog/1497259

2.实例代码

直接上代码吧
1)接口

[java] view
plain copy

package com.unei.serviceloader;  

  

/** 

 * Created by sun on 2015/7/25. 

 */  

public interface Command {  

    public void execute();  

}  

2)实现类

[java] view
plain copy

package com.unei.serviceloader;  

  

/** 

 * Created by sun on 2015/7/25. 

 */  

public class ShutdownCommand implements Command{  

    public void execute() {  

        System.out.println("shutdown....");  

    }  

}  

[java] view
plain copy

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,即完整的接口名
文件内容如下:

[plain] view
plain copy

com.unei.serviceloader.ShutdownCommand  

com.unei.serviceloader.StartCommand  

4)main方法

[java] view
plain copy

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)编译执行

[plain] view
plain copy

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的代码:

[java] view
plain copy

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方法:

[java] view
plain copy

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