我是靠谱客的博主 高兴酸奶,最近开发中收集的这篇文章主要介绍JAVA SPI机制什么是spi?spi接口的约定加载过程ServiceLoader原理总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

        最近在阅读hibernate-validator源码,接触到了java spi机制,借此了解学习java spi机制。

什么是spi?

         spi全称Service Provider Interface, 是java提供的一套用来加载第三方实现或扩展的服务发现机制,利用spi可以根据实际需求,轻松的启用、扩展、或者替换对应接口的实现策略,在许多知名框架中都利用了该项机制,将服务接口与服务实现分离以达到解耦,提高程序可扩展性。

spi接口的约定

  1. 在META-INF/services/目录中创建以接口全限定名命名的文件,该文件内容为api具体实现类的全限定名。
  2. 实现类必须有一个无参构造。
  3. 主程序通过java.util.ServiceLoader扫描META-INF/services目录下的配置文件找到实现类的全限定名,并且将类加载到主程序中。
  4. 实现类所在的jar包必须放在主程序的classpath中。

加载过程

         此处以hibernate-validator为例,说明spi的加载流程

定义接口

        ServiceLoader也可以用于加载自定以的接口,此时需要先定义接口:

        javax.validation.spi.ValidationProvider

public interface ValidationProvider<T extends Configuration<T>> {

	T createSpecializedConfiguration(BootstrapState state);

    Configuration<?> createGenericConfiguration(BootstrapState state);

    ValidatorFactory buildValidatorFactory(ConfigurationState configurationState);

}

配置实现类

         META-INF/

                   |—services/

                            `—javax.validation.spi.ValidationProvider

javax.validation.spi.ValidationProvider中的内容为:

org.hibernate.validator.HibernateValidator

加载实现类

        ServiceLoader中采用懒加载机制,动态加载需要的配置文件以及配置类

    ServiceLoader<ValidationProvider> loader = ServiceLoader.load( ValidationProvider.class, classloader );
	Iterator<ValidationProvider> providerIterator = loader.iterator();
	// ServiceLoader采用的懒加载机制,执行hasNext()时,才会触发加载配置文件。
	while ( providerIterator.hasNext() ){
		// 若实现类未加载,则此时加载实现类的class文件并实例化对象
		providerIterator.next();
	}

ServiceLoader原理

        ServiceLoader中的成员变量

	// 扫描目录前缀
    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 LazyIterator lookupIterator;

        ServiceLoader中,主要的源码逻辑为:

        1. 应用程序调用ServiceLoader.load方法,初始化类加载器、访问控制器、实现类缓存以及懒加载迭代器

        2. 应用程序通过迭代器接口获取实现类:

        ServiceLoader中存在两个迭代器,调用ServiceLoader的迭代器接口返回的是普通迭代器,迭代器的hasNext()及next()方法中,均先从缓存中查询实现类,当缓存查询完毕之后,则调用懒加载迭代器读取配置文件及实例化实现类:

普通迭代器的hasNext()方法及next()方法

public boolean hasNext() {
    if (knownProviders.hasNext())
        return true;
    return lookupIterator.hasNext();
}

public S next() {
    if (knownProviders.hasNext())
        return knownProviders.next().getValue();
    return lookupIterator.next();
}

        懒加载迭代器中,获取配置文件的实现为:

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

        3. 加载类对象,通过反射方法Class.forName()加载类对象,并用instance()方法实例化。

        4. 最后,将加入实例化后的类缓存到providers缓存中。

总结

  1. 使用Java spi机制的优势是实现解耦,将第三方实现与调用者业务代码分离,可以轻松实现业务模块之间的扩展或替换。
  2. 实现扩展对于原来的代码没有侵入性。
  3. 使用时不必关心实现类的路径。

最后

以上就是高兴酸奶为你收集整理的JAVA SPI机制什么是spi?spi接口的约定加载过程ServiceLoader原理总结的全部内容,希望文章能够帮你解决JAVA SPI机制什么是spi?spi接口的约定加载过程ServiceLoader原理总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(39)

评论列表共有 0 条评论

立即
投稿
返回
顶部