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学习笔记:通配符之glob模块(过滤)
glob模块用来查找文件目录和文件,可以和常用的find功能进行类比.glob支持*?[]这三种通配符.返回的数据类型是list.常见的两个方法有glob.glob()和glob.iglob(),ig ...
- VC2008中将CString转换成const char*的一种有效方法
文章转载自http://blog.csdn.net/lanbing510/article/details/7425613 在Visual Studio 200X下,CString直接转换成const ...
- Hibernate4之注解零配置
@Entity ,注册在类头上,将一个类声明为一个实体bean(即一个持久化POJO类) . @Table ,注册在类头上,注解声明了该实体bean映射指定的表(table). @Id用来注册主属性, ...
- Octave的安装
本文是参考吴恩达老师的深度学习视频而做的笔记 深度学习 引言 挑战:AI真正的挑战在于解决那些对人类来说很容易执行,但很难形式化描述的问题,比如识别人们所说的话/图像中的脸/分辨苹果和梨. 解决方案: ...
- springMVC+freemarker整合
转自:http://angelbill3.iteye.com/blog/1980904 在springMVC的项目中,加入freemarker 1.首先导入springMVC-webmvc所需的JAR ...
- 正规式α向有限自动机M的转换
[注:这一节是在学习东南大学廖力老师的公开课时,所记录的一些知识点截屏,谢谢廖力老师的辛劳付出] 引入3条正规式分裂规则来分裂α,所得到的是NFA M(因为包含ε弧,之后进行确定化就是所需要求得DF ...
- Unity Document
{ https://docs.unity3d.com/ScriptReference/Events.UnityEventTools.html }
- Delphi 鼠标控制函数GetCursorPos、SetCursorPos
GetCursorPos函数 获取鼠标的位置 var P: TPoint; begin GetCursorPos(P); //获取鼠标位置 end; SetCursorPos函数 设置鼠标的位置 v ...
- 8、iota枚举
1.iota常量自动生成器,每一行,自动累加1 2.iota给常量赋值使用 3.如果iota遇到const,就会重置为0 4.可以可以只写一个iota 5.如果是同一行,值是一样的 // 09_iot ...
- Android中.9图片的了解和制作过程
个部分(九宫格),分别为4个角,4条边,以及一个中间区域,4个角是不做拉升的,所以还能一直保持圆角的清晰状态,而2条水平边和垂直边分别只做水平和垂直拉伸,所以不会出现边会被拉粗的情况,只有中间用黑线指 ...