Java SPI

5 篇文章 0 订阅

什么是SPI

SPI全名是Service Provider Interface,通过接口寻找其具体的实现类,从而避免了使用方与提供方之间的强耦合,实现了动态可拔插的特性。

比如JDBC的数据库驱动模块,在JDBC以前的版本中,需要直接在代码中采用Class.forName(驱动实现类全名)的方式进行驱动初始化,导致想要更换数据库驱动模块就需要修改代码,具有很强的耦合度。

Class.forName("com.mysql.jdbc.Driver");


// 下面是mysql的驱动实现类
package com.mysql.cj.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

在后续版本中,JDBC采用了SPI机制,数据库驱动只需要在META-INF/services下面新建一个与驱动接口全路径名相同的文件,里面写上其驱动实现类的全路径名,这样JDBC就可以通过Java提供的ServiceLoad接口动态的去加载。

在这里插入图片描述
在这里插入图片描述

SPI简单实战

  1. 首先定义一个接口
package com.dubbo.service;

public class AudiService implements ICarService {
    @Override
    public void drive() {
        System.out.println("audi drive speed 100KM/H");
    }
}
  1. 创建两个该接口的实现类
package com.dubbo.service;

public class AudiService implements ICarService {
    @Override
    public void drive() {
        System.out.println("audi drive speed 100KM/H");
    }
}
package com.dubbo.service;

public class BMWService implements ICarService {
    @Override
    public void drive() {
        System.out.println("bmw drive speed 200KM/H");
    }
}
  1. resources/META-INF/service/接口全路径目录下创建文件
// 我的文件路径:META-INF\services\com.dubbo.service.ICarService
com.dubbo.service.AudiService
com.dubbo.service.BMWService
  1. 创建测试类
package com.dubbo.service;
import java.util.ServiceLoader;

public class Test {
    public static void main(String[] args) {
        ServiceLoader<ICarService> services = ServiceLoader.load(ICarService.class);
        for (ICarService service : services) {
            service.drive();
        }
    }
}
  1. 执行main方法,输出如下:
audi drive speed 100KM/H
bmw drive speed 200KM/H

SPI原理

ServiceLoader这个类是SPI机制实现的关键,下面是该类的源码:

package java.util;

import java.io.IOException;
import java.net.URL;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;

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;

    // 用于存储服务提供者的Map
    private LinkedHashMap<String, S> providers = new LinkedHashMap<>();

    // 懒迭代器
    private LazyIterator lookupIterator;

    // 重新加载实现类,清空providers缓存,并生成新的懒迭代器
    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

    // 构造方法,对接口、类加载器、上下文进行初始化
    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }

    // 懒迭代器定义
    private class LazyIterator implements Iterator<S> {
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;

        // META-INF下ICarService文件的迭代器
        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 {
                    // 接口全名
                    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;
                }

                // 读取META-INF下ICarService文件中的实现类全名列表的迭代器
                pending = parse(service, configs.nextElement());
            }

            // 获取META-INF下ICarService文件中的下一个实现类全名,并赋予nextName
            nextName = pending.next();
            return true;
        }

        // 获取下一个实现类
        private S nextService() {
            // 判断是否存在下一个实现类
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                // 获取nextName中对应的实现类的类对象
                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 {
                // 创建nextName中对应的实现类的实例
                S p = service.cast(c.newInstance());
                // 放入到providers缓存中
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service, "Provider " + cn + " could not be instantiated", x);
            }
            throw new Error();          // This cannot happen
        }

        // 实现Iterator的方法,判断是否还有下一个元素
        @Override
        public boolean hasNext() {
            if (acc == null) {
                // 调用hasNextService()方法,判断是否还存在下一个服务实例
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() {
                        return hasNextService();
                    }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

        @Override
        public S next() {
            if (acc == null) {
                // 调用nextService()获取下一个服务实现类对象
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() {
                        return nextService();
                    }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

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


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


在这里插入图片描述

缺陷

  • 不能按需加载。遍历所有实现类,并全部载入、实例化,造成了资源浪费、性能损耗。
  • 获取类的方式不够灵活。只能通过Iterator迭代器进行遍历获取,而不能通过标识获取指定的实现类。
  • 线程不安全。在多线程环境下使用ServiceLoader可能会出现问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值