Java ServiceLoader(SPI)学习
来源:互联网 发布:淘宝买苹果主板靠谱吗 编辑:程序博客网 时间:2024/06/05 07:01
研究Neo4j代码过程中jar包中配置接口实现方法,通过JAVA SPI方式实现。
我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。
为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。
java spi的具体约定如下 :
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。
jdk提供服务实现查找的一个工具类:java.util.ServiceLoader
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的结果迭代时才会被实例化。
0 0
- Java ServiceLoader(SPI)学习
- Java ServiceLoader(SPI)学习
- Java ServiceLoader(SPI)学习
- ServiceLoader : JAVA SPI 机制
- [JAVA]ServiceLoader类学习
- ServiceLoader java
- java ServiceLoader
- java.util.ServiceLoader使用
- java.util.ServiceLoader使用
- java.util.ServiceLoader使用
- JAVA ServiceLoader详解
- java.util.ServiceLoader使用
- Java ServiceLoader源码分析
- java.util.ServiceLoader使用
- ServiceLoader
- ServiceLoader
- java.util.ServiceLoader源码分析
- SPI之从ServiceLoader联想到DriverManager的使用总结
- hdoj1599find the mincost route【floyd+最小环】
- 入党积极分子选举
- Event bus介绍
- java集合类
- Electron-windows下的构建
- Java ServiceLoader(SPI)学习
- 4、第一 个mapReduce程序
- 导航条按钮设置
- 找出数组中的峰值
- JavaScript DOM编程艺术—幻灯片动画
- HDU 5144 NPY and shot (公式+三分)
- sql 提高效率技巧。
- strcpy,strncpy函数实现——string.h库函数
- C++开发中一个解决方案里,两个项目的相互引用,相互依赖的实现方法(解决方法)