NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制
NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制
Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)
SelectorProvider 定义了创建 Selector、ServerSocketChannel、SocketChannel 等方法,采用 JDK 的 Service Provider Interface (SPI) 方式实现。
public static ServerSocketChannel open() throws IOException {
return SelectorProvider.provider().openServerSocketChannel();
}
一、SelectorProvider SPI
SelectorProvider 是一个抽象类,需要子类实现。主要方法如下:
public abstract DatagramChannel openDatagramChannel() throws IOException;
public abstract DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException;
public abstract ServerSocketChannel openServerSocketChannel() throws IOException;
public abstract SocketChannel openSocketChannel() throws IOException;
public abstract AbstractSelector openSelector() throws IOException;
public abstract Pipe openPipe() throws IOException;
总结: SelectorProvider 相当于一个工厂类,提供了对 DatagramChannel、ServerSocketChannel、SocketChannel、Selector 了创建方法。
java.nio.channels.spi 中提供了一系列的抽象类,由具体的厂商实现,当然我们一般使用的都是 JDK 自己的实现。相关的 SPI 接口如下:
AbstractInterruptibleChannel -> SocketChannelImpl/ServerSocketChannelImpl
AbstractSelectableChannel
AbstractSelectionKey -> SelectionKeyImpl
AbstractSelector -> WindowsSelectorImpl/PollSelectorImpl/EpollSelectorImpl
SelectorProvider -> DefaultSelectorProvider
二、SelectorProvider 加载过程
2.1 SelectorProvider 加载
private static SelectorProvider provider = null;
public static SelectorProvider provider() {
synchronized (lock) {
if (provider != null)
return provider;
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run() {
// 1. java.nio.channels.spi.SelectorProvider 属性指定实现类
if (loadProviderFromProperty())
return provider;
// 2. SPI 指定实现类
if (loadProviderAsService())
return provider;
// 3. 默认实现,Windows 和 Linux 下不同
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
总结: SelectorProvider 提供了三种方式来自定义 SelectorProvider 的实现类。
java.nio.channels.spi.SelectorProvider属性指定实现类- 采用 SPI 方法创建 SelectorProvider
- 默认实现 DefaultSelectorProvider,Windows 和 Linux 下具体的实现不同。

public abstract class SelectorProviderImpl extends SelectorProvider {
public DatagramChannel openDatagramChannel() throws IOException {
return new DatagramChannelImpl(this);
}
public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException {
return new DatagramChannelImpl(this, family);
}
public Pipe openPipe() throws IOException {
return new PipeImpl(this);
}
public abstract AbstractSelector openSelector() throws IOException;
public ServerSocketChannel openServerSocketChannel() throws IOException {
return new ServerSocketChannelImpl(this);
}
public SocketChannel openSocketChannel() throws IOException {
return new SocketChannelImpl(this);
}
}
总结: SelectorProviderImpl 提供了 ServerSocketChannel、SocketChanne 的创建,至于 Selector 在不同的平台下有不同的实现。
2.2 Windows 下 DefaultSelectorProvider
public class DefaultSelectorProvider {
public static SelectorProvider create() {
return new sun.nio.ch.WindowsSelectorProvider();
}
}
public class WindowsSelectorProvider extends SelectorProviderImpl {
public AbstractSelector openSelector() throws IOException {
return new WindowsSelectorImpl(this);
}
}
2.3 Unix 下 DefaultSelectorProvider
public class DefaultSelectorProvider {
public static SelectorProvider create() {
String osname = AccessController
.doPrivileged(new GetPropertyAction("os.name"));
if (osname.equals("SunOS"))
return createProvider("sun.nio.ch.DevPollSelectorProvider");
if (osname.equals("Linux"))
return createProvider("sun.nio.ch.EPollSelectorProvider");
return new sun.nio.ch.PollSelectorProvider();
}
}
总结: Unix 平台下需要根据不同的操作系统选择不同的 Selector,例如 Linux 下是 EPollSelectorProvider。
public class EPollSelectorProvider extends SelectorProviderImpl {
public AbstractSelector openSelector() throws IOException {
return new EPollSelectorImpl(this);
}
public Channel inheritedChannel() throws IOException {
return InheritedChannel.getChannel();
}
}
总结: 无论是 WindowsSelectorProvider 还是 EPollSelectorImpl,它们都继承 SelectorProviderImpl,关于 ServerSocketChannel、SocketChanne 的创建都是一样的,区别是 Selector 有兼容性问题。难道 Socket 在 Windows 和 Linux 下就没有区别吗,肯定也是有兼容性问题的。
ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
super(sp);
this.fd = Net.serverSocket(true); // 创建 socket,这个 Net 工具本身是跨平台的
this.fdVal = IOUtil.fdVal(fd);
this.state = ST_INUSE;
}
Socket 的创建是在 sun.nio.ch.Net 工具类的 socket0 完成的,这个类很多方法都是 native 方法,在不同的平台有不同的实现。
每天用心记录一点点。内容也许不重要,但习惯很重要!
NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制的更多相关文章
- NIO 源码分析(05) Channel 源码分析
目录 一.Channel 类图 二.begin 和 close 是什么 2.1 AbstractInterruptibleChannel 中的 begin 和 close 2.2 Selector 中 ...
- NIO 源码分析(02-1) BIO 源码分析
目录 一.BIO 最简使用姿势 二.ServerSocket 源码分析 2.1 相关类图 2.2 主要属性 2.3 构造函数 2.4 bind 方法 2.5 accept 方法 2.6 总结 NIO ...
- NIO 源码分析(03) 从 BIO 到 NIO
目录 一.NIO 三大组件 Channels.Buffers.Selectors 1.1 Channel 和 Buffer 1.2 Selector 1.3 Linux IO 和 NIO 编程的区别 ...
- NIO 源码分析(02-2) BIO 源码分析 Socket
目录 一.BIO 最简使用姿势 二.connect 方法 2.1 Socket.connect 方法 2.2 AbstractPlainSocketImpl.connect 方法 2.3 DualSt ...
- NIO 源码分析(01) NIO 最简用法
目录 一.服务端 二.客户端 NIO 源码分析(01) NIO 最简用法 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) J ...
- 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 百篇博客分析OpenHarmony源码 | v41.03
百篇博客系列篇.本篇为: v41.xx 鸿蒙内核源码分析(任务切换篇) | 看汇编如何切换任务 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...
- jQuery 源码分析 8: 回头看jQuery的构造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)
在第一篇jQuery源码分析中,简单分析了jQuery对象的构造过程,里面提到了jQuery.fn.jQuery.prototype.jQuery.fn.init.prototype的关系. 从代码中 ...
- NIO源码分析:SelectionKey
SelectionKey SelectionKey,选择键,在每次通道注册到选择器上时都会创建一个SelectionKey储存在该选择器上,该SelectionKey保存了注册的通道.注册的选择器.通 ...
- 8. SOFAJRaft源码分析— 如何实现日志复制的pipeline机制?
前言 前几天和腾讯的大佬一起吃饭聊天,说起我对SOFAJRaft的理解,我自然以为我是很懂了的,但是大佬问起了我那SOFAJRaft集群之间的日志是怎么复制的? 我当时哑口无言,说不出是怎么实现的,所 ...
随机推荐
- array排序(按数组中对象的属性进行排序)
使用array.sort()对数组中对象的属性进行排序 <template> <div> <a @click="sortArray()">降序& ...
- PAT甲级——A1148 WerewolfSimpleVersion【20】
Werewolf(狼人杀) is a game in which the players are partitioned into two parties: the werewolves and th ...
- 力扣算法题—144Binary Tree Preorder Traversal
Given a binary tree, return the preorder traversal of its nodes' values. Example: Input: [1,null,2,3 ...
- 【Linux】- 同步网络时间
转自:Linux同步网络时间 Linux服务器运行久时,系统时间就会存在一定的误差,一般情况下可以使用date命令进行时间设置,但在做数据库集群分片等操作时对多台机器的时间差是有要求的,此时就需要使用 ...
- 用Emacs进行Python开发
用Emacs进行Python开发 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} ...
- 关于document.body.scrollTop用法
网页可见区域宽: document.body.clientWidth;网页可见区域高: document.body.clientHeight;网页可见区域宽: document.body.offset ...
- multipart/form-data,application/json和application/x-www-form-urlencoded区别
application/json和application/x-www-form-urlencoded都是表单数据发送时的编码类型. EncType: enctype 属性规定在发送到服务器之前应该如何 ...
- JSON 和 xml 浅读
1. JSON数据格式 JSON 语法是 JavaScript 对象表示法语法的子集. 数据在名称/值对中:名称是字符串,使用双引号表示.值可以是:数字(整数或浮点数),字符串(在双引号中),数组(在 ...
- mac 完全卸载vscode
原文分隔线====================== while writing go this morning, I found that the wrong code are not under ...
- python--知识小结和集合
知识小结 一. = 表示赋值 ==表示比较值的大小 is 比较内存地址 二. 数字小数据池:在-5~256之内,id在电脑里是一样的 字符串id一样要求: ①:不能有特殊字符 ②:s(一个单字符)*2 ...