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集群之间的日志是怎么复制的? 我当时哑口无言,说不出是怎么实现的,所 ...
随机推荐
- Python中xlrd、xlwt、win32com模块对xls文件的读写操作
# -*- coding: utf-8 -*- #xlrd和xlwt只支持xls文件读写,openpyxl只支持xlsx文件的读写操作 import xlrd import xlwt import w ...
- 【题解】Ride to Office
题目描述 起点与终点相隔4500米.现Charley 需要从起点骑车到终点.但是,他有个习惯,沿途需要有人陪伴,即以相同的速度, 与另外一个人一起骑.而当他遇到以更快的速度骑车的人时,他会以相应的速度 ...
- 数据结构与算法简记--redis有序集合实现-跳跃表
跳表 定义 为一个值有序的链表建立多级索引,比如每2个节点提取一个节点到上一级,我们把抽出来的那一级叫做索引或索引层.如下图所示,其中down表示down指针,指向下一级节点.以此类推,对于节点数为n ...
- MATLAB中的fspecial函数
Matlab 的fspecial函数用法 转载:https://blog.csdn.net/majinlei121/article/details/50255837 fspecial函数用于建立预定义 ...
- flask中配置并使用mongodb
在你安装并运行了mongodb的情况下: 随便在一个文件中写入以下代码: import pymongo client = pymongo.MongoClient(host="localhos ...
- Arcpy 遍历每一个要素新建要素类
#方法一:cursor= arcpy.da.SearchCursor(aim_fc,["OID@","SHAPE@"]) for row in cursor: ...
- 【目录】ASP.NET Core 2.1 入门教程
ASP.NET Core 2.1 快速学习.入门系列教程,这个入门系列教程为了帮助大家快速上手ASP.NET Core. 本教程包含且不限于: 使用VS Code开发ASP.NET Core应用 AS ...
- 查看linux防火墙的状态
如有需要可以加我Q群[308742428]大家一起讨论技术,有偿服务. 后面会不定时为大家更新文章,敬请期待. 喜欢的朋友可以关注下.iptables防火墙1.基本操作 # 查看防火墙状态 servi ...
- Elasticsearch添加Shield后TransportClient如何连接?
Elasticsearch添加Shield后TransportClient如何连接? 时间 2015-12-28 10:24:01 旁门左道 原文 http://log.medcl.net/ite ...
- swat - 基于web的samba管理工具
总览 swat [ -s smb config file ] [ -a ] 描述 此程序是 samba 套件的一部分. swat 允许 samba 管理员通过web浏览器配置复杂的 smb.conf ...