前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java Spi与SpringFactoriesLoader

java Spi与SpringFactoriesLoader

作者头像
用户5546570
发布2021-02-22 11:18:29
4040
发布2021-02-22 11:18:29
举报

spring的SpringFactoriesLoader是spring框架内部工具类,在 Spring boot 应用启动的过程中,这个类的工作很重要, 启动逻辑使用该类从classpath上所有jar包中找出各自的 META-INF/spring.factories 属性文件,并分析出其中定义的工厂类。这些工厂类进而被启动逻辑使用,应用于进一步初始化工作。其使用的方式和java的spi基本类似,我们可以先学习java的spi而且进一步学习SpringFactoriesLoader。

一、java spi

1、什么是Spi

SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。

2、Spi的应用场景

SPI ( Service Provider Interface) 是 调用方 来制定接口规范,提供给外部来实现,调用方在调用时则 选择自己需要的外部实现。 从使用人员上来说,SPI 被框架扩展人员使用。

3、Spi的简单demo

(1)、定义一个接口

代码语言:javascript
复制
public interface Upload {
    void upload();
}

(2)、接口实现类

代码语言:javascript
复制
package cn..antispam.soa.service;
public class BduUploadImpl implements Upload {
    @Override
 public void upload(){
        System.out.println("上传到百度云");
 }
}
代码语言:javascript
复制
public class AliUploadImpl implements Upload {
    @Override
 public void upload() {
        System.out.println("上传到阿里oss");
 }
}

(3)、测试类

代码语言:javascript
复制
public class spidemo {
   public static void main(String[] args) {
       ServiceLoader<Upload> uploads = ServiceLoader.load(Upload.class);
for (Upload upload :uploads){
           upload.upload();
}
   }
}

(4)输出

代码语言:javascript
复制
上传到阿里oss
上传到百度云

Process finished with exit code 0

4、Spi源码分析

代码语言:javascript
复制
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;

    // 私有内部类,提供对所有的service的类的加载与实例化
    private class LazyIterator implements Iterator<S> {
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        String nextName = null;

   public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){
       return new ServiceLoader<>(service, loader);
    }
   public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        //....
        reload();
    }
   public void reload() {
       // 清空providers
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

   private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
   }
        //...
        private boolean hasNextService() {
            if (configs == null) {
                try {
                    //获取目录下所有的类
                    //"META-INF/services/"+service的全限定类名
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        //加载 META-INF/services/"+service里的所有全限定类名
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    //...
                }
                //....
            }
        }

        private S nextService() {
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                //反射加载类
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
            }
            try {
                //实例化
                S p = service.cast(c.newInstance());
                //放进缓存
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                //..
            }
            //..
        }
    }
}

上面的代码只贴出了部分关键的实现,下面贴出比较直观的spi加载的主要流程供参考:

image

来源:https://www.tuicool.com/articles/NR3meem

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、java spi
  • 1、什么是Spi
  • 2、Spi的应用场景
  • 3、Spi的简单demo
  • 4、Spi源码分析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档