JAVA之SPI-服务发现机制

目录

SPI是什么?

怎么用

实现原理


SPI是什么?

SPI :全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。同Spring的IoC/DI类似。

这一机制为很多框架扩展提供了可能,比如在Spring、JDBC、Dubbo中都使用到了SPI机制。

IoC:Inversion of Control,控制反转组件之间依赖关系由容器在运行期决定

DI:Dependency Injection,依赖注入,IoC的一种实现方式。

怎么用

  1. 创建一个接口或类
    ​
    package com.lizz.fundation.spi;
    
    public interface SPIService {
        //必须有一个不带参数方法
        void exe();
    }

     

  2. 创建两个需要发现的实现类
    package com.lizz.fundation.spi;
    
    public class SPIService1Impl implements SPIService {
    
        @Override
        public void exe() {
            System.out.println("SPIService1Impl exe...");
        }
    }
    
    package com.lizz.fundation.spi;
    
    public class SPIService2Impl implements SPIService {
        @Override
        public void exe() {
            System.out.println("SPIService2Impl exe...");
        }
    }
    

     

  3. 在src/main/resources/META-INF/services目录下创建与SPI接口类全类名相同的文件com.lizz.fundation.spi.SPIService
    com.lizz.fundation.spi.SPIService1Impl
    com.lizz.fundation.spi.SPIService2Impl

     

  4.  程序中服务发现(单测)
    package com.lizz.fundation.spi;
    
    import org.junit.Test;
    import sun.misc.Service;
    
    import java.util.Iterator;
    import java.util.ServiceLoader;
    
    public class SPIServiceTest {
        @Test
        public void spiTest(){
            //加载接口类对应的配置文件中的实现类
            ServiceLoader load = ServiceLoader.load(SPIService.class);
            Iterator iterator = load.iterator();
            //循环获取实现类并执行其中的方法
            while(iterator.hasNext()) {
                SPIService ser = iterator.next();
                ser.exe();
            }
        }
    }
    
    SPIService1Impl exe...
    SPIService2Impl exe...

     

实现原理

  1. 查看ServiceLoader源码
    public final class ServiceLoader
        implements Iterable
    {
        //加载文件目录
        private static final String PREFIX = "META-INF/services/";
    
        // 需要加载的服务类或接口
        private final Class service;
    
        // 类加载器
        private final ClassLoader loader;
    
        // 创建serviceloader时获取访问堆栈控制上下文
        private final AccessControlContext acc;
    
        // 服务提供者缓存,按加载顺序存储
        private LinkedHashMap providers = new LinkedHashMap<>();
    
        // 当前使用的惰性迭代器
        private LazyIterator lookupIterator;
    ...
    }
  2.  初始化服务加载器:ServiceLoader.load
    /**
    * 服务加载程序
    * @param   需要发现的服务类型 SPIService
    * @param  service 需要发现服务接口或抽象类 SPIService.class
    */    
    public static  ServiceLoader load(Class service) {
        //获取当前线程上下文类加载器
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }
    /**
    * 服务加载程序
    * @param   需要发现的服务类型 SPIService
    * @param  service 需要发现服务接口或抽象类 SPIService.class
    * @param  loader 需要使用的类加载器
    */  
    public static  ServiceLoader load(Class service, ClassLoader loader){
        return new ServiceLoader<>(service, loader);
    }
    
    /**
    * 初始化服务加载器
    */
    private ServiceLoader(Class svc, ClassLoader cl) {
        //设置需要加载的服务类,判断是否为空
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        //设置加载器,为空则使用默认AppClassLoader加载器
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        //设置加载器访问上下文,无权写返回null
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        //重置加载器
        reload();
    }
    /**
    * 清除当前已加载的服务,并初始化延时迭代器
    */
    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }
        
  3. 初始化延时迭代器:load.iterator()
    /**
    * 获取已加载服务迭代器,默认为空,通过hasNext方法加载
    */
    public Iterator iterator() {
        return new Iterator() {
            //获取当前已加载服务迭代器
            Iterator> knownProviders = providers.entrySet().iterator();
            // 重写Iterator.hasNext方法
            // 判断已加载迭代器中是否存在下一个,存在则返回true
            // 否则返回待加载迭代器中是否存在下一个
            public boolean hasNext() {
                if (knownProviders.hasNext())
                    return true;
                return lookupIterator.hasNext();
            }
            // 重写Iterator.next方法
            // 判断已加载迭代器中是否存在下一个,存在则返回这个实现类
            // 否则返回待加载迭代器中的下一个,为空抛出异常
            public S next() {
                if (knownProviders.hasNext())
                   return knownProviders.next().getValue();
                return lookupIterator.next();
            }
            //重写方法,不允许溢出
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

     

  4.  懒加载服务,加载key:Iterator.hasNext() ,加载value:terator.next()
    // ServiceLoader的延时得带起私有内部类
    private class LazyIterator implements Iterator {
        // 提供者服务类
        Class service;
        // 当前服务加载器
        ClassLoader loader;
        // 配置文件地址
        Enumeration configs = null;
        // 待加载迭代器
        Iterator pending = null;
        // 服务类名
        String nextName = null;
    
        private LazyIterator(Class service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }
    
        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    // 服务接口类地址 
                    // META-INF/services/com.lizz.fundation.spi.SPIService
                    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();
            //cn = com.lizz.fundation.spi.SPIService1Impl
            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 action = new PrivilegedAction() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
        // 读取下一个服务
        public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction action = new PrivilegedAction() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
        // 不允许移除服务
        public void remove() {
            throw new UnsupportedOperationException();
        }
    
    }

     

你可能感兴趣的:(SPI,SPI机制,服务发现机制,java)