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编程语言中,`java.util.ServiceLoader` 是一个实用工具类,用于加载符合特定接口或抽象类的服务提供者。这个工具在Java平台标准版(Java SE)中被广泛使用,尤其是在实现模块化和插件化系统时。下面将详细...
ServiceLoader的设计灵感来源于Java的SPI(Service Provider Interface),这是一种允许外部组件为Java应用程序提供服务的方式。在JAR文件的`META-INF/services`目录下,创建一个以接口全限定名命名的文本文件,文件...
4. **加载服务**:在主程序中,通过`java.util.ServiceLoader`来加载并迭代所有的服务提供者。 ```java ServiceLoader<MyService> loader = ServiceLoader.load(MyService.class); for (MyService service : ...
SPI机制则是一种服务提供者接口的机制,它允许第三方为某个接口实现提供实现,通过扩展名定的目录(通常是META-INF/services/目录)中的配置文件,服务提供方指定的实现类的全限定名,从而使得服务加载器可以动态地...
META-INF/services 目录是 Java SPI 机制中用于存放服务提供者接口(SPI)的目录。在 JDBC 驱动加载过程中,META-INF/services/java.sql.Driver 文件中包含了驱动程序的类名,例如 com.mysql.cj.jdbc.Driver。 驱动...
Java 的 SPI(服务提供者接口)和 java.util.ServiceLoader 使用的简单演示。 介绍 这个演示应用程序包括 6 个小 jar 项目。 云服务 演示伪“云服务”提供者。 为服务提供者定义spidemo.cloud.spi.Cloud接口。 ...
开发者可以通过在类路径下的META-INF/services目录下创建以服务接口全限定名命名的文本文件,文件内容列出实现了该接口的所有类,然后使用ServiceLoader类加载这些服务提供者。这种方式使得框架或库可以在运行时动态...
线程上下文类加载器用于在多线程环境下,让每个线程能够独立地加载特定的类,特别是在服务提供者接口(SPI)的场景中,比如JDBC驱动的动态加载。 服务提供者接口(SPI)允许第三方为Java核心库提供的接口提供实现。...
java.util.ServiceLoader类从 Java 编程语言的第 6 版开始就java.util.ServiceLoader ,它允许您在应用程序中访问和使用不同的服务提供者。 例如,假设服务作者为我们提供了一个名为RandomServiceProvider的接口;...
使用java.util.ServiceLoader示例Spring Boot应用程序在类路径上动态加载所有实现。 多项目Gradle构建 1个Spring Boot应用 1个SPI项目 2个SPI实施项目 有关java.util.ServiceLoader用法,请参见app/src/test/java/...
下面是如何使用`ServiceLoader`加载和使用这些服务提供者的步骤: 1. **加载服务提供者**: ```java ServiceLoader<MinWinsockSpi> loader = ServiceLoader.load(MinWinsockSpi.class); ``` 2. **遍历并实例化...
4. 使用 ServiceLoader:使用 ServiceLoader 加载和实例化提供者的实现类。 SPI 机制的破坏双亲委派: 1. SPI 机制可以破坏双亲委派,因为 ServiceLoader 可以加载第三方提供者的实现类。 2. 例如,在 JDBC 连接池...
RocketMQ 奇技淫巧之 ServiceLoader 源码解读 抓下来打包成了HTML文件, 方便离线观看
Java服务加载器(ServiceLoader)是Java平台标准版(Java SE)中的一种设计模式,它允许应用程序使用服务提供者接口(SPI,Service Provider Interface)来动态发现和加载服务实现。这个机制使得软件组件之间可以...
4. **服务加载器**:`java.util.ServiceLoader`是SPI的核心工具类,它负责读取配置文件,加载并实例化服务提供者。我们可以通过迭代`ServiceLoader`获取所有可用的服务实例。 5. **使用SPI**:在应用中,我们通常会...
ServiceLoader是Java中的一种服务加载机制,它允许我们根据接口动态发现并加载实现类,而无需显式地通过Class.forName或者反射等方式进行实例化。这个机制主要基于Java的SPI(Service Provider Interface)服务提供...
- **提供服务实现**:然后,创建一个或多个类来实现这个接口,这些类就是服务提供者。 - **配置元数据**:在`META-INF/services`目录下创建一个以接口全限定名命名的文本文件,其中包含实现类的全限定名。这是JVM...
- **服务使用**:应用程序可以通过迭代`ServiceLoader`加载的服务提供者列表,选择合适的实现进行使用。 2. SPI工作流程: - (1) 创建`ServiceLoader`对象,传入需要加载的服务接口类型。 - (2) 使用`...
- **加载服务**:通过`java.util.ServiceLoader`加载服务,该类会读取配置文件并实例化所有服务提供者。 3. **关键组件** - **ServiceLoader**:Java SPI的主要加载工具,用于查找、加载和实例化服务提供者。 - ...