Java中SPI机制

转自:https://www.jianshu.com/p/46b42f7f593c

一、SPI是什么

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

整体机制图如下:
在这里插入图片描述
Java SPI实际上是基于接口的编程+策略模式+配置文件组合实现的动态加载机制

系统设计的各个抽象,往往有很多不同的实现方案,在面向的对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制

Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦

二、使用场景

概括地说,适用于调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略

比较常见的例子:

  • 数据库驱动加载接口实现类的加载:JDBC加载不同类型数据库的驱动
  • 日志门面接口实现类加载:SLF4J加载不同提供商的日志实现类
  • Spring:Spring中大量使用了SPI,比如:对Servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等
  • Dubbo:Dubbo中也大量使用SPI的方式实现框架的扩展,不过它对Java提供的原生SPI做了封装,允许用户扩展实现Filter接口

三、使用介绍

要使用Java SPI,需要遵循如下约定:

1)、当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名

2)、接口实现类所在的jar包放在主程序的classpath中

3)、主程序通过java.util.ServiceLoader动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM

4)、SPI的实现类必须携带一个不带参数的构造方法

提供的驱动接口

package com.hand.spi.demo.driver;

public interface IDriver {
    void run();
}

驱动A实现

package com.hand.spi.demo.driver.impl;

import com.hand.spi.demo.driver.IDriver;

public class DriverAImpl implements IDriver {
    @Override
    public void run() {
        System.out.println("驱动A");
    }
}

驱动B实现

package com.hand.spi.demo.driver.impl;

import com.hand.spi.demo.driver.IDriver;

public class DriverBImpl implements IDriver {
    @Override
    public void run() {
        System.out.println("驱动B");
    }
}

实现主类

package com.hand.spi.demo;

import com.hand.spi.demo.driver.IDriver;
import java.util.ServiceLoader;

public class SpiMain {
    public static void main(String[] args) {
        ServiceLoader<IDriver> drivers = ServiceLoader.load(IDriver.class);
        for (IDriver d : drivers) {
            d.run();
        }
    }
}

在resources下创建META-INF/services目录, 然后创建以接口全限定名为文件名的文件, 文件内写入需要被发现的实现类的全限定名
在这里插入图片描述
执行结果

驱动A
驱动B

四、源码分析

从上面代码中知,SPI的核心操作类就是java.util.ServiceLoader这个类

        ServiceLoader<IDriver> drivers = ServiceLoader.load(IDriver.class);

1、属性

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

	//指定读取实现接口信息的文件位置
    private static final String PREFIX = "META-INF/services/";

    //指定要发现实现的接口
    private final Class<S> service;

    //类加载器(将实现类加载到JVM中)
    private final ClassLoader loader;

    //安全上下文
    private final AccessControlContext acc;

    //所有被发现的接口实现集合
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

    //懒加载迭代器(ServiceLoader内部类)
    private LazyIterator lookupIterator;

2、初始化

以下按调用顺序从上至下

    public static <S> ServiceLoader<S> load(Class<S> service) {
        //初始化加载器
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

    public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        //构造器构造ServiceLoader
        return new ServiceLoader<>(service, loader);
    }

    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        //判断接口是否存在,若不存在,抛出异常"Service interface cannot be null"
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        //初始化安全上下文
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        //初始实现类发现,重新发现实现类
        reload();
    }

    public void reload() {
        //删除之前多有的接口实现,重新加载接口实现
        providers.clear();
        //初始化迭代器
        lookupIterator = new LazyIterator(service, loader);
    }

初始化了service、loader、acc、lookupIterator

3、类定义

ServiceLoader类实现了Iterable接口, 这个类通过本类实例迭代器, 关联到属性
private LinkedHashMap<String,S> providers

    public Iterator<S> iterator() {
        return new Iterator<S>() {
			//返回的迭代器,其实是providers属性的迭代器
            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();
                //迭代器返回的结果是通过LazyIterator迭代器返回的结果
                return lookupIterator.next();
            }

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

        };
    }

ServiceLoader实现了Iterator接口, 方便用户通过serviceLoader遍历 实现的集合, 并隐藏内部具体的实现, 自己本身的迭代器是通过迭代维护实现的集合 providers 属性实现的, 在迭代providers时, 文件中发现的接口实现还没有被加载到JVM中, 在内部类LazyIterator中以懒加载的形式, 将接口实现类加载到JVM中, 并且实例化到providers中

4、迭代器

这是一个内部类, 通过迭代器间接调用LazyIterator的迭代方法, 在懒加载迭代器的迭代方法中, 通过反射对发现的实现类进行加载和实例化

    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 {
                	//此处获取到MATE-INF/service下的文件,读取为多个资源文件路径Enumeration<URL>
                    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);
            }
        }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值