Java中的SPI

什么是SPI

SPI全称是Service Provider Interface,是Java提供的一套用来被第三方实现或扩展的API,可以用来启用框架的扩展或组件的替换。

官网描述

SPI的应用

SPI有很多的使用场景,比如:

  1. JDBC中的使用,各种数据库的驱动实现
  2. SLF4J中的使用,加载不同的日志实现
  3. Spring中的使用,对servlet的ServletContainerInitializer的实现
    ……

SPI的实战

  1. 创建一个接口
    public interface DataBaseDriver {
    	
       void driver();
    
    }
    
  2. 实现该接口
    public class MyDataBaseDriver implements DataBaseDriver {
    
        @Override
        public void driver() {
            System.out.println("这是MyDataBase的驱动");
        }
    }
    
  3. 在实现接口的项目的resources目录下创建META-INF/services目录,并创建以接口全类名命名的文件,里面写上接口实现的全类名
    Java中的SPI_第1张图片

    文件名:com.demo.DataBaseDriver
    文件内容:com.demo.MyDataBaseDriver

  4. 通过ServiceLoader进行使用
    	public class DataBaseDriverTest {
    
    	    public static void main(String[] args) {
    	        ServiceLoader<DataBaseDriver> dataBaseDrivers = ServiceLoader.load(DataBaseDriver.class);
    	        for (DataBaseDriver dataBaseDriver : dataBaseDrivers) {
    	            dataBaseDriver.driver();
    	        }
    	    }
    	}
    

SPI的原理

  1. 通过ServiceLoader.load(Class service)实例化一个ServiceLoader 对象
  2. 读取文件内容
  3. 通过反射得到对象

关键源码如下:

private static final String PREFIX = "META-INF/services/";
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
        }

你可能感兴趣的:(Java,java,spi,ServiceLoader)