public class ddd {
public static void main(String[] args) {
ServiceLoader1<Hello> sl = ServiceLoader1.load(Hello.class);//传进去了Hello.class和线程上下文类加载器。
for(Hello h : sl) {//调用sl的iterator()
h.say();//com.ssss.impl.CHello@1324409e,
}
}
}
package com.ssss;
/*
服务提供者加载机制。针对SPI设计出来的。 服务是接口或抽象类。服务提供者是服务接口的实现。ServiceLoader是加载实现类的。服务提供者是jdbc数据库驱动。
不是线程安全的。
文件名是接口,文件内容是实现。 @since 1.6 1.6才开始加入,
根据META-INF/services/文件去加载相应的实现类。
*/ public final class ServiceLoader1<S> implements Iterable<S>{
private static final String PREFIX = "META-INF/services/";//指明了路径是在META-INF/services/下。
// 传进来的接口的class。正在加载的服务的类或接口。
private final Class<S> service;
// 线程上下文加载器。类加载器。
private final ClassLoader loader;
// 权限控制上下文。创建ServiceLoader时获取的访问控制上下文。
private final AccessControlContext acc;
// 服务提供者的缓存,服务是接口,服务提供者是接口的实现。
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// for循环遍历时候调用的是这个在遍历。用于类的懒加载,只有在迭代时加载。
private LazyIterator lookupIterator; public void reload() {//构造函数调用
providers.clear();
lookupIterator = new LazyIterator(service, loader);//interface com.ssss.Hello,AppClassLoader
} private ServiceLoader1(Class<S> svc, ClassLoader cl) {//service=interface com.ssss.Hello, cl=AppClassLoader。
service = Objects.requireNonNull(svc, "Service interface cannot be null");//interface com.ssss.Hello
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;//AppClassLoader,getSystemClassLoader返回的也是appClassLoder,所以获取应用加载器可以线程获取也可以getSystemClassLoader()来获取。
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;//null
reload();
} private static void fail(Class<?> service, String msg, Throwable cause)throws ServiceConfigurationError
{
throw new ServiceConfigurationError(service.getName() + ": " + msg,cause);
} private static void fail(Class<?> service, String msg) throws ServiceConfigurationError
{
throw new ServiceConfigurationError(service.getName() + ": " + msg);
} private static void fail(Class<?> service, URL u, int line, String msg) throws ServiceConfigurationError
{
fail(service, u + ":" + line + ": " + msg);
} private int parseLine(Class<?> service, URL u, BufferedReader r, int lc, List<String> names)
throws IOException, ServiceConfigurationError
{
String ln = r.readLine();//一行
if (ln == null) {
return -;//-1表示解析完成
}
int ci = ln.indexOf('#');
if (ci >= ) ln = ln.substring(, ci);//去掉注释
ln = ln.trim();//去掉空格
int n = ln.length();
if (n != ) {
if ((ln.indexOf(' ') >= ) || (ln.indexOf('\t') >= ))
fail(service, u, lc, "Illegal configuration-file syntax");
int cp = ln.codePointAt();
if (!Character.isJavaIdentifierStart(cp))
fail(service, u, lc, "Illegal provider-class name: " + ln);
for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
cp = ln.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
fail(service, u, lc, "Illegal provider-class name: " + ln);
}
if (!providers.containsKey(ln) && !names.contains(ln))
names.add(ln);//添加实现类名字[com.ssss.impl.CHello, com.ssss.impl.JavaHello],
}
return lc + ;
} //u = file:\H:\2019326spring\蚂蚁课堂\0005-(每特教育&每特学院&蚂蚁课堂)-3期-并发编程专题-线程池原理分析\0005
//-(每特教育&每特学院&蚂蚁课堂)-3期-并发编程专题-线程池原理分析\上课代码\thread_day_day06_test\target
//\classes\META-INF\services\com.ssss.Hello文件
private Iterator<String> parse(Class<?> service, URL u) throws ServiceConfigurationError {
InputStream in = null;
BufferedReader r = null;
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = ;
while ((lc = parseLine(service, u, r, lc, names)) >= );//添加到names里面去。
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
return names.iterator();
} private class LazyIterator implements Iterator<S>//遍历实现类
{ Class<S> service;//接口的Class
ClassLoader loader;//app应用加载器
Enumeration<URL> configs = null;//配置文件的绝对路径
Iterator<String> pending = null;//所有实现类的集合
String nextName = null;//判断是否有下一个元素时候,就设置了下一个元素的名字了nextName private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;//interface com.ssss.Hello
this.loader = loader;//AppClassLoader
} private boolean hasNextService() {//是否有下一个实现类元素
if (nextName != null) {
return true;
}
if (configs == null) {//初始化configs
try {
String fullName = PREFIX + service.getName();//文件路径:META-INF/services/com.ssss.Hello
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);//loader = AppClassLoader,加载META-INF/services/com.ssss.Hello资源
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {//初始化pending
if (!configs.hasMoreElements()) {//一个配置文件所有实现类遍历完之后,pending.hasNext()返回false,configs.hasMoreElements()返回false
return false;
}
pending = parse(service, configs.nextElement());//pending = [com.ssss.impl.CHello, com.ssss.impl.JavaHello]集合,configs.nextElement()就是配置文件的绝对路径,
}
nextName = pending.next();//判断是否有下一个元素时候,就设置了下一个元素的名字了nextName,pending里面有一个游标,一直调用next方法时候游标加一,所以一直获取的是下一个元素。
return true;
} private S nextService() {//下一个实现类
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;//实现类名字com.ssss.impl.CHello
nextName = null;//下一个名字置为null,再次获取下一个的时候重新设置值。
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);//class com.ssss.impl.CHello
} catch (ClassNotFoundException x) {
fail(service, "Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {//service = interface com.ssss.Hello,c = class com.ssss.impl.CHello.
fail(service, "Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());//p = com.ssss.impl.CHello@1324409e对象,
providers.put(cn, p);//实现类的名字,实现类的对象放入缓存。{com.ssss.impl.CHello:com.ssss.impl.CHello@1324409e,com.ssss.impl.JavaHello:com.ssss.impl.JavaHello@246ae04d}
return p;//返回实现类对象
} catch (Throwable x) {
fail(service,"Provider " + cn + " could not be instantiated",x);
}
throw new Error(); // This cannot happen
} public boolean hasNext() {//是否有下一个实现类元素
if (acc == null) {//权限判断
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
} public S next() {//下一个实现类
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
} public void remove() {
throw new UnsupportedOperationException();
} } //延迟加载:首先从缓存,配置文件加载之后加入缓存。
public Iterator<S> iterator() {//遍历方法。for循环时候先调用hasNext()在调用next()。
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();
} };
} public static <S> ServiceLoader1<S> load(Class<S> service,ClassLoader loader){
return new ServiceLoader1<>(service, loader);
} public static <S> ServiceLoader1<S> load(Class<S> service) {//service = interface com.ssss.Hello
ClassLoader cl = Thread.currentThread().getContextClassLoader();//AppClassLoader,ServiceLoader类本身是boot加载器加载的,boot加载不到应用类路径下的类,所以要用app加载器。
return ServiceLoader1.load(service, cl);
} public static <S> ServiceLoader1<S> loadInstalled(Class<S> service) {
ClassLoader cl = ClassLoader.getSystemClassLoader();//系统加载器,app加载器
ClassLoader prev = null;
while (cl != null) {//系统加载器为null,prev就是null。扩展加载器是null,prev就是系统加载器。否则prev就是扩展加载器。
prev = cl;//扩展加载器
cl = cl.getParent();//扩展加载器
}
return ServiceLoader1.load(service, prev);//目的是为了加载jvm虚拟机里面的类,不是应用类路径下的类。
} public String toString() {
return "java.util.ServiceLoader[" + service.getName() + "]";
}
}

SPI ServiceLoader源码分析的更多相关文章

  1. 【Java】ServiceLoader源码分析

    ServiceLoader主要的功能是用来完成对SPI的provider的加载. 先看下它的成员: public final class ServiceLoader<S> implemen ...

  2. Dubbo SPI 机制源码分析(基于2.7.7)

    Dubbo SPI 机制涉及到 @SPI.@Adaptive.@Activate 三个注解,ExtensionLoader 作为 Dubbo SPI 机制的核心负责加载和管理扩展点及其实现.本文以 E ...

  3. 【Java】NIO中Selector的创建源码分析

    在使用Selector时首先需要通过静态方法open创建Selector对象 public static Selector open() throws IOException { return Sel ...

  4. dubbo源码分析02:服务引用

    一.何时创建服务引用 引用官方文档的原话,如果将Dubbo托管在Spring-IOC容器下,Dubbo服务引用的时机有两个,第一个是在Spring容器调用ReferenceBean的afterProp ...

  5. 7.源码分析---SOFARPC是如何实现故障剔除的?

    我在服务端引用那篇文章里面分析到,服务端在引用的时候会去获取服务端可用的服务,并进行心跳,维护一个可用的集合. 所以我们从客户端初始化这部分说起. 服务连接的维护 客户端初始化的时候会调用cluste ...

  6. 8.源码分析---从设计模式中看SOFARPC中的EventBus?

    我们在前面分析客户端引用的时候会看到如下这段代码: // 产生开始调用事件 if (EventBus.isEnable(ClientStartInvokeEvent.class)) { EventBu ...

  7. 9.源码分析---SOFARPC是如何实现故障剔除的?

    SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...

  8. 11.源码分析---SOFARPC数据透传是实现的?

    先把栗子放上,让大家方便测试用: Service端 public static void main(String[] args) { ServerConfig serverConfig = new S ...

  9. 12.源码分析—如何为SOFARPC写一个序列化?

    SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...

随机推荐

  1. Spark常规性能调优

    1.1.1     常规性能调优一:最优资源配置 Spark性能调优的第一步,就是为任务分配更多的资源,在一定范围内,增加资源的分配与性能的提升是成正比的,实现了最优的资源配置后,在此基础上再考虑进行 ...

  2. python--面向对象之三个特性:封装、继承、多态

    一.面向对象简介 1.面向对象不是所有的情况都适用2.面向对象编程 a.定义类 class 类名: def 方法1(self, 参数名): 方法体 b.根据类创建对象,使用对象去执行类中的方法 obj ...

  3. java 面向对象2

    1.  类是对某一类事物的抽象描述,而对象用于表示现实中该类事物的个体. 玩具模型是类: 玩具是对象: ★局部变量和成员变量的区别: 区别一:定义的位置不同 定义在类中的变量是成员变量 定义在方法中或 ...

  4. ent 基本使用 三 边(关系处理)

    ent 提供了图查询的能力,实际上在关系数据库中的表现就是relation,以下代码接前文 添加边(关系) 添加schema entc init Car Group 效果: 添加字段 car pack ...

  5. Nginx+Php-fpm运行原理 代理与反向代理

    一.代理与反向代理 现实生活中的例子 1.正向代理:访问google.com 如上图,因为google被墙,我们需要vpnFQ才能访问google.com.Virtual Private Networ ...

  6. [HNOI2016]序列 CDQ+DP

    [HNOI2016]序列 CDQ 链接 loj 思路 一个点最小变为l,最大变为r,不变的时候为v 那么j能在i前面就要满足. \(j<i\) \(r[j]<=v[i]\) \(v[j]& ...

  7. 我用AI(Adobe Illustrator CS6)合并路径的两个常用方法

    作为一个切图仔,经常与设计大佬的PSD打交道,PSD里面又有各种icon图标需要导出,偷懒的方法直接导出png图片,丢个背景图上页面完美解决问题!! 第二天来个需求,能不能把这个icon图标给我换个颜 ...

  8. OpenFlow和SDN的历史和原理介绍

    OpenFlow相关的历史.新闻:http://blog.csdn.net/jincm13/article/details/7825754起源与发展[https://36kr.com/p/503598 ...

  9. R语言中的管道操作符 %>% %T>% %$% %<>%

    magrittr 包的官网 https://magrittr.tidyverse.org/ magrittr 包的 github 主页 https://github.com/tidyverse/mag ...

  10. 表格样式、表格css、

    .mytab{ border-collapse: collapse;}.mytab tr,.mytab td,.mytab th{ text-align: center; border: 1px so ...