SPI

Java SPI(Service Provider Interface)允许第三方为特定接口提供实现,使得应用在运行时可以通过配置文件选择合适的实现。本文通过示例展示了如何创建SPI服务及消费者,解释了SPI的工作原理,并指出JDBC是其典型应用场景。SPI通过读取`META-INF/services`目录下配置文件加载服务实现类,并使用反射进行实例化。
摘要由CSDN通过智能技术生成

SPI 将服务接口与服务实现分离,通过本地注册发现获取具体的实现类,具备可插拔的特点。

SPI 示例

SPI 示例程序结构图
Driver:

public interface Driver {
    void connect();
}

public class MySqlDriver implements Driver {
    @Override
    public void connect() {
        System.out.println("mysql connect");
    }
}

public class OracleDriver implements Driver {
    @Override
    public void connect() {
        System.out.println("oracle connect");
    }
}

在 resouces 目录中创建 META-INF/services 目录,并创建以 Driver 全路径名为文件名的文件,写入具体实现:

com.xmustang.spi.MySqlDriver

Client:

public class Client {
    public static void main(String[] args) {
        ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
        for (Driver driver : drivers) {
            driver.connect();
        }
    }
}

运行结果:
SPI运行结果

SPI 使用场景

JDBC 就是典型的 SPI 的使用场景。各大厂商(如 Mysql、Oracle)会根据一个统一的规范(java.sql.Driver)开发各自的驱动实现逻辑。客户端使用 jdbc 时不需要改变代码,直接引入不同的 SPI 接口服务即可。
Mysql SPI

SPI 原理

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;

    public Iterator<S> iterator() {
        return new Iterator<S>() {

            Iterator<Map.Entry<String, S>> knownProviders = providers.entrySet().iterator();

            public boolean hasNext() {
                if (knownProviders.hasNext())
                    return true;
                return lookupIterator.hasNext();
            }

            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

    // 私有内部类,提供对所有的 service 的类的加载与实例化,实现 Iterator 接口
    private class LazyIterator implements Iterator<S> {

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;

        private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }

        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    // 通过相对路径读取 classpath 中 META-INF 目录中文件,也就是读取服务提供者的实现类全限定名
                    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
        }

        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);
            }
        }

        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、付费专栏及课程。

余额充值