`
wydyyhwzx
  • 浏览: 9518 次
社区版块
存档分类
最新评论

ServiceLoader服务提供者加载设施帮助类

阅读更多

ServiceLoader是jdk提供的一个简单的服务提供者加载设施,一个服务(接口)的实现者在其资源目录META-INF/services 中放置提供者配置文件 来标识服务提供者。

文件名称是服务类型的完全限定名称。该文件包含一个具体提供者类的完全限定名称列表,每行一个。

通过ServiceLoader.load创建服务实现者加载器,通过iterator以延迟方式加载此加载器服务的可用提供者。

 

本帮助类对ServiceLoader的功能进行了增强:

1.对加载到的服务提供者进行缓存,避免重复加载。

2.提供一种按key/value的方式来配置服务提供者,按key来获取具体服务提供者的扩展。

 

代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务提供者加载设施帮助类,应用中每个ClassLoader都有不同的ServiceLoaderHelp,因为对同一个服务,使用不同的ClassLoader,
 * 加载的jar包可能不同,资源目录META-INF/services下放置的服务提供者配置文件也就可能不同,导致不同ClassLoader加载到的服务提供者不同。
 */
public class ServiceLoaderHelp {
    private static final Logger log = LoggerFactory.getLogger(ServiceLoaderHelp.class);

    private static final String PREFIX = "META-INF/services/";
    private static final ConcurrentHashMap<ClassLoader, ServiceLoaderHelp> MAP_Help_CACHE = new ConcurrentHashMap<ClassLoader, ServiceLoaderHelp>();

    private final ClassLoader classLoader;
    private final ConcurrentHashMap<Class<?>, Object> INSTANCE_CACHE = new ConcurrentHashMap<Class<?>, Object>();
    private final ConcurrentHashMap<Class<?>, Map<String, Class<?>>> MAP_CLASSES_CACHE = new ConcurrentHashMap<Class<?>, Map<String, Class<?>>>();
    private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Object>> MAP_INSTANCES_CACHE = new ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Object>>();

    private ServiceLoaderHelp(ClassLoader classLoader){
        this.classLoader = classLoader;
    }

    /**
     * 根据ClassLoader获取服务提供者加载设施帮助类。
     * @param classLoader
     * @return 服务提供者加载设施帮助类
     */
    public static ServiceLoaderHelp getHelpByClassLoader(ClassLoader classLoader){
        ServiceLoaderHelp help = MAP_Help_CACHE.get(classLoader);
        if (help == null){
            help = new ServiceLoaderHelp(classLoader);
            MAP_Help_CACHE.putIfAbsent(classLoader, help);
            return MAP_Help_CACHE.get(classLoader);
        } else {
            return help;
        }
    }

    /**
     * 根据服务接口Class获取服务提供者,若有多个服务提供者取ServiceLoader.load到的第一个服务提供者。
     * @param classType 服务接口Class
     * @return 服务提供者
     */
    @SuppressWarnings("unchecked")
    public <T> T getInstance(Class<T> classType) {
        T instance = (T) INSTANCE_CACHE.get(classType);
        if (instance == null) {
            try {
                instance = ServiceLoader.load(classType, classLoader).iterator().next();
                INSTANCE_CACHE.putIfAbsent(classType, instance);
                return (T) INSTANCE_CACHE.get(classType);
            } catch (Throwable e) {
                log.error("Cannot Load " + classType.getName());
                throw new RuntimeException("Cannot Load " + classType, e);
            }
        } else {
            if (classType.isAssignableFrom(instance.getClass())) {
                return instance;
            } else {
                throw new RuntimeException("instance not type " + classType);
            }
        }
    }

    /**
     * 根据服务接口Class获取服务提供者列表。
     * @param classType 服务接口Class
     * @return 服务提供者列表
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> getInstances(Class<T> classType) {
        List<T> list = (List<T>) INSTANCE_CACHE.get(classType);
        if (list == null) {
            try {
                list = new ArrayList<T>();
                for (T instance : ServiceLoader.load(classType, classLoader)) {
                    list.add(instance);
                }
                INSTANCE_CACHE.putIfAbsent(classType, list);
                return (List<T>) INSTANCE_CACHE.get(classType);
            } catch (RuntimeException e) {
                log.error("Cannot Load List By" + classType.getName());
                throw new RuntimeException("Cannot Load List By" + classType, e);
            }
        } else {
            if (List.class.isAssignableFrom(list.getClass())) {
                return list;
            } else {
                throw new RuntimeException("instance not List, type " + classType);
            }
        }
    }

    /**
     * 根据服务接口Class和服务提供者key获取服务提供者。
     * 此种服务提供者加载方式,在资源目录META-INF/services下放置的服务提供者配置文件的内容不是ServiceLoader
     * 规范中的每行一个“具体服务提供者类的完全限定名称”,
     * 而是每行一个“key=具体服务提供者类的完全限定名称”。
     * 此方式提供了根据配置选取服务提供者的便利。
     *
     * @param classType 服务接口Class
     * @param key 服务提供者key
     * @return 服务提供者
     */
    @SuppressWarnings("unchecked")
    public <T> T getInstance(Class<T> classType, String key) {
        ConcurrentHashMap<String, Object> objMap = MAP_INSTANCES_CACHE.get(classType);
        if (objMap == null) {
            objMap = new ConcurrentHashMap<String, Object>();
            MAP_INSTANCES_CACHE.putIfAbsent(classType, objMap);
        }
        objMap = MAP_INSTANCES_CACHE.get(classType);
        T obj = (T) objMap.get(key);
        if (obj != null) {
            return obj;
        }
        Map<String, Class<?>> classMap = MAP_CLASSES_CACHE.get(classType);
        if (classMap == null) {
            loadFile(classType);
        }
        classMap = MAP_CLASSES_CACHE.get(classType);
        if (classMap == null) {
            throw new IllegalStateException("Failed to load extension class(interface: " + classType + ", key: " + key
                    + ")");
        }
        Class<?> clazz = classMap.get(key);
        if (clazz != null) {
            try {
                Object newObj = clazz.newInstance();
                Object provious = objMap.putIfAbsent(key, newObj);
                return null == provious ? (T) newObj : (T) provious;
            } catch (Exception e) {
                throw new IllegalStateException("Failed to load extension class(interface: " + classType + ", key: "
                        + key + ")", e);
            }
        }
        throw new IllegalStateException("Failed to load extension class(interface: " + classType + ", key: " + key
                + ")");
    }

    private void loadFile(Class<?> type) {
        String fileName = PREFIX + type.getName();
        Map<String, Class<?>> map = new HashMap<String, Class<?>>();
        try {
            Enumeration<URL> urls;
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                        try {
                            String line = null;
                            while ((line = reader.readLine()) != null) {
                                final int ci = line.indexOf('#');
                                if (ci >= 0)
                                    line = line.substring(0, ci);
                                line = line.trim();
                                if (line.length() > 0) {
                                    try {
                                        String name = null;
                                        int i = line.indexOf('=');
                                        if (i > 0) {
                                            name = line.substring(0, i).trim();
                                            line = line.substring(i + 1).trim();
                                        }
                                        if (line.length() > 0) {
                                            Class<?> clazz = Class.forName(line, false, classLoader);
                                            if (!type.isAssignableFrom(clazz)) {
                                                throw new IllegalStateException(
                                                        "Error when load extension class(interface: " + type
                                                                + ", class line: " + clazz.getName() + "), class "
                                                                + clazz.getName() + "is not subtype of interface.");
                                            }
                                            map.put(name, clazz);
                                        }
                                    } catch (Throwable t) {
                                    }
                                }
                            } // end of while read lines
                        } finally {
                            reader.close();
                        }
                    } catch (Throwable t) {
                        log.error("", "Exception when load extension class(interface: " + type + ", class file: "
                                + url + ") in " + url, t);
                    }
                } // end of while urls
            }
        } catch (Throwable t) {
            log.error("", "Exception when load extension class(interface: " + type + ", description file: "
                    + fileName + ").", t);
        }
        MAP_CLASSES_CACHE.put(type, map);
    }
}

 

分享到:
评论

相关推荐

    java.util.ServiceLoader demo

    在Java编程语言中,`java.util.ServiceLoader` 是一个实用工具类,用于加载符合特定接口或抽象类的服务提供者。这个工具在Java平台标准版(Java SE)中被广泛使用,尤其是在实现模块化和插件化系统时。下面将详细...

    ServiceLoader的使用

    ServiceLoader的设计灵感来源于Java的SPI(Service Provider Interface),这是一种允许外部组件为Java应用程序提供服务的方式。在JAR文件的`META-INF/services`目录下,创建一个以接口全限定名命名的文本文件,文件...

    通过SPI实现动态加载外部jar的实现类

    4. **加载服务**:在主程序中,通过`java.util.ServiceLoader`来加载并迭代所有的服务提供者。 ```java ServiceLoader&lt;MyService&gt; loader = ServiceLoader.load(MyService.class); for (MyService service : ...

    Java类加载及SPI机制.pdf

    SPI机制则是一种服务提供者接口的机制,它允许第三方为某个接口实现提供实现,通过扩展名定的目录(通常是META-INF/services/目录)中的配置文件,服务提供方指定的实现类的全限定名,从而使得服务加载器可以动态地...

    数据库jdbc驱动加载过程

    META-INF/services 目录是 Java SPI 机制中用于存放服务提供者接口(SPI)的目录。在 JDBC 驱动加载过程中,META-INF/services/java.sql.Driver 文件中包含了驱动程序的类名,例如 com.mysql.cj.jdbc.Driver。 驱动...

    JavaServiceProviderInterfaceDemo:Java 的 SPI(服务提供者接口)和 java.util.ServiceLoader 使用的简单演示

    Java 的 SPI(服务提供者接口)和 java.util.ServiceLoader 使用的简单演示。 介绍 这个演示应用程序包括 6 个小 jar 项目。 云服务 演示伪“云服务”提供者。 为服务提供者定义spidemo.cloud.spi.Cloud接口。 ...

    Java类加载及SPI机制.zip

    开发者可以通过在类路径下的META-INF/services目录下创建以服务接口全限定名命名的文本文件,文件内容列出实现了该接口的所有类,然后使用ServiceLoader类加载这些服务提供者。这种方式使得框架或库可以在运行时动态...

    Java类加载器学习总结.pdf

    线程上下文类加载器用于在多线程环境下,让每个线程能够独立地加载特定的类,特别是在服务提供者接口(SPI)的场景中,比如JDBC驱动的动态加载。 服务提供者接口(SPI)允许第三方为Java核心库提供的接口提供实现。...

    java-service-spi-example:Java SPI(服务提供者示例)

    java.util.ServiceLoader类从 Java 编程语言的第 6 版开始就java.util.ServiceLoader ,它允许您在应用程序中访问和使用不同的服务提供者。 例如,假设服务作者为我们提供了一个名为RandomServiceProvider的接口;...

    spring-boot-java-util-service-loader:使用java.util.ServiceLoader的示例Spring Boot应用程序在类路径上动态加载所有SPI实现

    使用java.util.ServiceLoader示例Spring Boot应用程序在类路径上动态加载所有实现。 多项目Gradle构建 1个Spring Boot应用 1个SPI项目 2个SPI实施项目 有关java.util.ServiceLoader用法,请参见app/src/test/java/...

    SPI基础服务提者示演示

    下面是如何使用`ServiceLoader`加载和使用这些服务提供者的步骤: 1. **加载服务提供者**: ```java ServiceLoader&lt;MinWinsockSpi&gt; loader = ServiceLoader.load(MinWinsockSpi.class); ``` 2. **遍历并实例化...

    Java SPI 机制(SPI实战+ServiceLoader源码分析+SPI 应用场景+破坏双亲委派)

    4. 使用 ServiceLoader:使用 ServiceLoader 加载和实例化提供者的实现类。 SPI 机制的破坏双亲委派: 1. SPI 机制可以破坏双亲委派,因为 ServiceLoader 可以加载第三方提供者的实现类。 2. 例如,在 JDBC 连接池...

    RocketMQ 奇技淫巧之 ServiceLoader 源码解读 - GitChat

    RocketMQ 奇技淫巧之 ServiceLoader 源码解读 抓下来打包成了HTML文件, 方便离线观看

    serviceloader:这是一个让你理解java服务加载器机制的例子

    Java服务加载器(ServiceLoader)是Java平台标准版(Java SE)中的一种设计模式,它允许应用程序使用服务提供者接口(SPI,Service Provider Interface)来动态发现和加载服务实现。这个机制使得软件组件之间可以...

    spi-Demo.rar

    4. **服务加载器**:`java.util.ServiceLoader`是SPI的核心工具类,它负责读取配置文件,加载并实例化服务提供者。我们可以通过迭代`ServiceLoader`获取所有可用的服务实例。 5. **使用SPI**:在应用中,我们通常会...

    详谈ServiceLoader实现原理

    ServiceLoader是Java中的一种服务加载机制,它允许我们根据接口动态发现并加载实现类,而无需显式地通过Class.forName或者反射等方式进行实例化。这个机制主要基于Java的SPI(Service Provider Interface)服务提供...

    java的spi测试

    - **提供服务实现**:然后,创建一个或多个类来实现这个接口,这些类就是服务提供者。 - **配置元数据**:在`META-INF/services`目录下创建一个以接口全限定名命名的文本文件,其中包含实现类的全限定名。这是JVM...

    SPI设计思想.zip

    - **服务使用**:应用程序可以通过迭代`ServiceLoader`加载的服务提供者列表,选择合适的实现进行使用。 2. SPI工作流程: - (1) 创建`ServiceLoader`对象,传入需要加载的服务接口类型。 - (2) 使用`...

    java spi实现工程

    - **加载服务**:通过`java.util.ServiceLoader`加载服务,该类会读取配置文件并实例化所有服务提供者。 3. **关键组件** - **ServiceLoader**:Java SPI的主要加载工具,用于查找、加载和实例化服务提供者。 - ...

Global site tag (gtag.js) - Google Analytics