JDK java.util.ServiceLoader SPI 扩展机制

博文目录


Java SPI详解

SPI

SPI全称 Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。

  • API (Application Programming Interface)在大多数情况下,都是实现方制定接口并完成对接口的实现,调用方仅仅依赖接口调用,且无权选择不同实现。 从使用人员上来说,API 直接被应用开发人员使用。
  • SPI (Service Provider Interface)是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。 从使用人员上来说,SPI 被框架扩展人员使用。

SPI 示例

定义接口

package demo.java.util.ServiceLoader;

public interface Protocol {
	String getName();
}

两个不同的实现类

package demo.java.util.ServiceLoader;

public class DubboProtocolImpl implements Protocol {
	@Override
	public String getName() {
		return "dubbo";
	}
}
package demo.java.util.ServiceLoader;

public class HttpProtocolImpl implements Protocol {
	@Override
	public String getName() {
		return "http";
	}
}

resources中配置 META-INF/services/demo.java.util.ServiceLoader.Protocol, 路径为 “META-INF/services/” + 接口全限定名, 内容就是该接口的两个实现类的全限定名, 一行一个

demo.java.util.ServiceLoader.DubboProtocolImpl
demo.java.util.ServiceLoader.HttpProtocolImpl

Demo演示

public class Demo {
	public static void main(String[] args) {
		ServiceLoader<Protocol> serviceLoader = ServiceLoader.load(Protocol.class);
		for (Protocol protocol : serviceLoader) {
			System.out.println(protocol.getClass());
			System.out.println(protocol.getName());
		}
	}
}

结果

class demo.java.util.ServiceLoader.DubboProtocolImpl
dubbo
class demo.java.util.ServiceLoader.HttpProtocolImpl
http

真实案例

JDK定义了数据库操作规范 java.sql.Driver, 各厂家提供驱动包, 如mysql.jar和oracle.jar, 在 mysql-connector-java-8.0.20.jar 中有如下结构
在这里插入图片描述
其内容只有一行 com.mysql.cj.jdbc.Driver, 即mysql提供的Driver接口的实现类, 在 DriverManager 类中有静态初始化代码如下

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
	// ...
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
    	public Void run() {
    		ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
    		Iterator<Driver> driversIterator = loadedDrivers.iterator();
    		try{
    		    while(driversIterator.hasNext()) {
    		    	// 实例化
    		    	driversIterator.next();
    		    }
    		} catch(Throwable t) {
    		    // Do nothing
    		}
    		return null;
    	}
    });
    // ...
}

原理

编译前

public static void main(String[] args) {
	ServiceLoader<Protocol> serviceLoader = ServiceLoader.load(Protocol.class);
	for (Protocol protocol : serviceLoader) {
		System.out.println(protocol.getClass());
		System.out.println(protocol.getName());
	}
}

编译后, 本质上还是获取到Iterator, 然后调用hasNext()和next()方法

public static void main(String[] args) {
    ServiceLoader<Protocol> serviceLoader = ServiceLoader.load(Protocol.class);
    Iterator var2 = serviceLoader.iterator();
    while(var2.hasNext()) {
        Protocol protocol = (Protocol) var2.next();
        System.out.println(protocol.getClass());
        System.out.println(protocol.getName());
    }
}

ServiceLoader 关键源码

public final class ServiceLoader<S> implements Iterable<S> {

    // 扫描目录前缀, 官方定死了
    private static final String PREFIX = "META-INF/services/";

    // 被加载的类或接口
    private final Class<S> service;

    // 用于定位、加载和实例化实现方实现的类的类加载器
    private final ClassLoader loader;

    // 上下文对象(权限控制)
    private final AccessControlContext acc;

    // 按照实例化的顺序缓存已经实例化的实现类
    private LinkedHashMap<String, S> providers = new LinkedHashMap<>();

    // 懒查找迭代器
    private java.util.ServiceLoader.LazyIterator lookupIterator;

    // 私有内部类,提供对所有的service的类的加载与实例化能力
    private class LazyIterator implements Iterator<S> {
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        String nextName = null;

        //...
		private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            // 这个if只有第一次调用该方法时会走
            if (configs == null) {
            	// 获取指定文件下的所有实现类配置
            	// 会从各类加载器路径下查找所有的该路径文件, 包括有如下
            	// 引导类加载器的 jre/lib/*.jar
            	// 扩展类加载器的 jre/lib/ext/.*.jar
            	// 应用类加载器的 classpath(*/target/classes/*) 和 其他引入的所有 jar
                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);
                }
            }
            // 最终应该是在 classpath 下找到了文件, 然后将其每一行解析成一个List, 最终pending=list.iterator()
            // 这个while只有第一次调用该方法时会走
            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
        }

		// 迭代器的 hasNext 方法, 调用 hasNextService
		public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

		// 迭代器的 next 方法, 调用 nextService
        public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
    }
}

总结

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值