• java中的SPI机制


    介绍

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

    使用

    定义一个接口,可以飞的

    public interface Flyable {
    
      void fly();
    }
    

    定义两个实现类,老鹰和鸽子

    public class Eagle implements Flyable {
    
      @Override
      public void fly() {
        System.out.println("eagle fly");
      }
    }
    
    public class Dove implements Flyable {
    
      @Override
      public void fly() {
        System.out.println("dove fly");
      }
    }
    
    public class Client {
      public static void main(String[] args) {
        ServiceLoader<Flyable> serviceLoader = ServiceLoader.load(Flyable.class);
        for (Flyable flyable : serviceLoader) {
          flyable.fly();
        }
      }
    }
    

    在resources目录下创建META-INF目录,META-INF下创建services目录,services下创建文件名为接口的全限定名的文件,内容为接口的实现类的全限定名,每个一行。
    输出结果:

    dove fly
    eagle fly
    

    原理

    我们看一下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 LinkedHashMap<String,S> providers = new LinkedHashMap<>();
    // 延迟迭代器 在hasNext时才真正解析文件
        private LazyIterator lookupIterator;
    
        private class LazyIterator implements Iterator<S> {	
    	//判断是否有下一条记录是才真正解析文件
    	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
            }
        }
    }
    

    在jdbc的Driver的加载过程中就用到了SPI。

    mysql的配置文件

  • 相关阅读:
    Angular语法(三)——数据绑定
    Angular语法(二)——模板语法
    Angular语法(一)——展示数据
    Angular常用指令
    windows下启动redis
    WPF实现弹幕
    微信获得用户信息
    拉普拉斯变换
    Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering
    Python之并行
  • 原文地址:https://www.cnblogs.com/strongmore/p/13284433.html
Copyright © 2020-2023  润新知