SPI(Service provider interface)是旨在由第三方实现或扩展的API。它可以用于启用框架扩展和可替换组件。

服务是一组众所周知的接口或(通常是抽象的)类。服务提供者是服务的特定实现。提供程序中的类通常实现接口并对服务本身中定义的类进行子类化。服务提供程序可以以扩展的形式安装在Java平台的实现中,即,jar文件放置在任何通常的扩展目录中。提供者也可以通过将它们添加到应用程序的类路径或通过一些其他平台特定的手段。

在java中通过java.util.ServiceLoader 来实现。

先上代码:

Provider 可以为接口或者抽象类

package com.xwolf.spi.service;
/**
* Animal Provider
* @author xwolf
*
*/
public interface Animal { void eat(); void drink(); }

具体实现:

package com.xwolf.spi.service.impl;

import com.xwolf.spi.service.Animal;

public class Dog implements Animal {

    @Override
public void eat() {
System.out.println("Dog eat....");
} @Override
public void drink() {
System.out.println("Dog drink....");
} }
package com.xwolf.spi.service.impl;

import com.xwolf.spi.service.Animal;

public class Cat implements Animal {

    @Override
public void eat() {
System.out.println("Cat eat....");
} @Override
public void drink() {
System.out.println("Cat drink...");
} }

将接口和具体实现放在文件META-INF/services中,文件名为全类名的Provider,内容为具体实现类的全类名。

测试方法:

package com.xwolf.spi.service;

import java.util.ServiceLoader;

public class ServiceLoaderTest {

    public static void main(String[] args) {
//接口测试
ServiceLoader<Animal> loader=ServiceLoader.load(Animal.class);
for(Animal animal:loader){
animal.eat();
} } }

项目目录结构:

查看ServiceLoader部分源代码:

private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
} private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}

比较核心的代码就上边了,总之就是遍历的时候读取META-INF/services下所有为CLASS(本例子中的Animal)的全类名的文件内容,加载类。

【java】 java SPI的更多相关文章

  1. 【转】Java代码规范

    [转]Java代码规范 http://blog.csdn.net/huaishu/article/details/26725539

  2. 【深入】java 单例模式(转)

    [深入]java 单例模式 关于单例模式的文章,其实网上早就已经泛滥了.但一个小小的单例,里面却是有着许多的变化.网上的文章大多也是提到了其中的一个或几个点,很少有比较全面且脉络清晰的文章,于是,我便 ...

  3. 【转】Java HashMap 源码解析(好文章)

    ­ .fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...

  4. 【解惑】Java动态绑定机制的内幕

    在Java方法调用的过程中,JVM是如何知道调用的是哪个类的方法源代码? 这里面到底有什么内幕呢? 这篇文章我们就将揭露JVM方法调用的静态(static binding) 和动态绑定机制(auto ...

  5. 【转】Java之 内存区域和GC机制

    转自:Leo Chin 目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage ...

  6. 【三板斧】Java定位CPU使用高问题

    [三板斧]Java定位CPU使用高问题 1.TOP命令,查询消耗CPU高的进程号 PID,并记录下来,按下键盘"H"键,记录高消耗线程号,并将改线程号转换为十六进制 2.使用 js ...

  7. 【转】java.util.vector中的vector的详细用法

    [转]java.util.vector中的vector的详细用法 ArrayList会比Vector快,他是非同步的,如果设计涉及到多线程,还是用Vector比较好一些 import java.uti ...

  8. 【转】Java中equals和==的区别

    [转]Java中equals和==的区别 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boole ...

  9. 【转】JAVA的StringBuffer类

    [转]JAVA的StringBuffer类    StringBuffer类和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,所以StringBu ...

  10. 【转】Java学习之Iterator(迭代器)的一般用法 (转)

    [转]Java学习之Iterator(迭代器)的一般用法 (转) 迭代器(Iterator) 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构.迭 ...

随机推荐

  1. kafka集群中jmx端口设置

    jmx端口主要用来监控kafka集群的. 在启动kafka的脚本kafka-server-start.sh中找到堆设置,添加export JMX_PORT="9999" if [ ...

  2. python 基础笔记

    1,去掉了C语言中的大括号,用空格来对齐语句块.(空格一般用2个或4个,但没有限制) 2,要在py文件代码中使用中文,需要在第一行加入下面的代码: # -*- coding: utf-8 -*- 或者 ...

  3. android开发(32) android 中 actionbar 常用方法。设置标题,隐藏图标等

    设置标题: actionBar.setTitle("关于我们"); 使返回箭头出现 actionBar.setDisplayHomeAsUpEnabled(true); 监听返回按 ...

  4. jQuery(二):jQuery选择器

    jQuery选择器类似于CSS选择器,用来选取网页中的元素.例如: $("h3").css("background-color","red" ...

  5. linq操作符:串联操作符

    串联是一个将两个集合连接在一起的过程.在Linq中,这个过程通过Concat操作符实现.Concat操作符用于连接两个集合,生成一个新的集合.来看看Concat操作符的定义: public stati ...

  6. Android安装后没有完成和打开按钮

    File apkFile = new File(filePath); Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVIT ...

  7. jquery跨域访问解决方案

    客户端“跨域访问”一直是一个头疼的问题,好在有jQuery帮忙,从jQuery-1.2以后跨域问题便迎刃而解.由于自己在项目中遇到跨域问题,借此机会对跨域问题来刨根问底,查阅了相关资料和自己的实践,算 ...

  8. 2016年第七届蓝桥杯C/C++B组省赛题目解析

    题目1:煤球数目 有一堆煤球,堆成三角棱锥形.具体:第一层放1个,第二层3个(排列成三角形),第三层6个(排列成三角形),第四层10个(排列成三角形),....如果一共有100层,共有多少个煤球?请填 ...

  9. Spring JDBC JdbcTemplate类示例

    org.springframework.jdbc.core.JdbcTemplate类是JDBC核心包中的中心类.它简化了JDBC的使用,并有助于避免常见的错误. 它执行核心JDBC工作流,留下应用程 ...

  10. python 程序构架浅析

    定义:通常的 Python 程序的构架是指:将一个程序分割为源代码文件的集合以及将这些部分连接在一起的方法. python的程序构架可表示为: 一个python程序就是一个模块的系统.它有一个顶层文件 ...