上一章 中的标准netty启动代码中,ServerBootstrap究竟是如何启动的呢?这一章咱们来瞅下。java
server.group(bossGroup, workGroup); server.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100); server.childHandler(new DealNettyServerInitializer()); ChannelFuture future = server.bind(7878).sync();
启动代码无非这么几行,我一行一行的瞅。服务器
server.group(bossGroup, workGroup);socket
还记得上一章定义的两个NioEventLoopGroup不?bossGroup和workGroup。这里的ServerBootstrap的group方法就是将workGroup直接赋值给了该对象的childGroup。而bossGroup传到了其父类AbstractBootstrap中,进行保存。oop
看到这,咱们知道了两个类的关系:ServerBootstrap是AbstractBootstrap的子类。this
server.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100);spa
会将NioServerSocketChannel的class对象做为参数构建ReflectiveChannelFactory对象。这是什么对象呢?进去看一下:.net
哦,原来是一个经过反射构建对象的工厂类。回到上一步,netty会把ReflectiveChannelFactory对象做为参数调用channelFactory()方法。进入这个方法看一下:3d
原来只是保存ReflectiveChannelFactory对象。注意,channel方法是属于AbstractBootstrap对象的,所以,ReflectiveChannelFactory对象是保存在AbstractBootstrap对象中的。rest
一样,option方法也是AbstractBootstrap对象的,options会保存该option对象,那这么说,options应该是一个集合了,netty
果真是一个map集合。
server.childHandler(new DealNettyServerInitializer());
childHandler是属于ServerBootstrap类的,因此将DealNettyServerInitializer对象赋值给了ServerBootstrap的childHandler成员变量。
至此,只是保存变量,没有使用。看下server.bind(7878)方法,此方法就是服务器启动的真正入口。
很简单,继续跟进,
调用doBind方法,继续跟进:
这么多代码,主要是initAndRegister方法,所以跟进去。
下面正式进入启动流程,启动流程大致分为4步:
一、建立服务端channel:NioServerSocketChannel
二、初始化服务端Channel:NioServerSocketChannel
三、注册Selector:将Channel注册到Selector上
四、端口的绑定:服务端端口的监听。
下面,一步一步的分析:
一、建立服务端channel:NioServerSocketChannel
忘了说了,这个bind方法是调用的AbstractBootstrap的方法哦,所以这个channelFactory就是channel()方法的参数ReflectiveChannelFactory,下图为证:
还记的ReflectiveChannelFactory的newChannel方法吗?
很明显,就是经过反射构建NioServerSocketChannel。由于咱们传入的是NioServerSocketChannel的Class对象。
NioServerSocketChannel对象是生成了,咱们一块儿看一下这个NioServerSocketChannel的构造方法吧。
这个newSocket是作了什么?
哦,原来是调用jdk的代码,生成Nio的ServerSocketChannel呀。继续查看:
跟进this:
记住,这里的第三个参数是accept事件,,后续咱们还会提到。跟进父类的构造方法:
咱们能够看到,NioServerSocketChannel的父类是AbstractNioMessageChannel。而后这个构造方法只是继续调用父类的构造方法。
咱们又知道了,AbstractNioMessageChannel的父类是AbstractNioChannel。而且刚才说注意的accept事件,赋值给了AbstractNioChannel的成员变量readInterestOp,后续还会遇到。
注意这个方法,ch是什么?就是传进来的jdk原生的ServerSocketChannel,一次该方法明显是将此通道将被置于非阻塞模式。
继续跟进父类的构造方法:
咱们又知道了AbstractNioChannel的父类是AbstractChannel。而且,在该构造方法中,实例化了id,unsafe和pipeline。
明显此处的NioMessageUnsafe的父类是AbstracNioUnsafe。
到这里,一系列的父类构造方法的调用结束了。咱们先总结下他们之间的关系。
NioServerSocketChannel的父类是AbstractNioMessageChannel;AbstractNioMessageChannel的父类是AbstractNioChannel;AbstractNioChannel的父类是AbstractChannel;还有一个额外的关系
NioMessageUnsafe的父类是AbstractNioUnsafe。
即:
NioServerSocketChannel -> AbstractNioMessageChannel -> AbstractNioChannel -> AbstractChannel;
NioMessageUnsafe -> AbstractNioUnsafe
为啥非要强调这种继承关系?由于后续的调用很复杂,若是不记住他们的关系,很容易分不清楚变量是从何 而来的。。
好了,我再回到开始的NioServerSocketChannel的构造方法上:
看过了super一系列的调用,咱们瞅下下面的代码。先看下javaChannel()干了什么?
调用了父类的javaChannel方法。NioServerSocketChannel的父类是AbstractNioMessageChannel,所以看AbstractMessageNioChannel类的javaChannel方法,咱们发现AbstractMessageNioChannel类中并无javaChannel方法,所以应该调用的是AbstractNioMessageChannel的父类AbstractNioChannel的javaChannel,咱们看一下:
这个ch,不就是以前传入的jdk原声的ServerSocketChannel对象么。。。图下证:
所以javaChannel方法,获取的是原生的ServerSocketChannel对象。点个赞,这个方法的名字真是见名知意呀。那javaChannel().socket()咱们就很容易知道了,就是打开一个socket链接。并将该链接封装到NioServerSocketChannelConfig对象中保存在NioServerSocketChannel的config成员变量上。至此,建立服务端channel:NioServerSocketChannel完成。
二、初始化服务端Channel:NioServerSocketChannel
回到initAndRegister方法,看完了NioServerSocketChannel的实例化,咱们看下紧接着的代码init():
进入init方法:
哎呦,这个又一坨代码。。。。不要紧,不少代码都很简单:
上面代码很简单,就是setChannelOptions和setChannelAttrs。
同理,setChildOptions和setChildAttrs。
首先声明下,initChannel方法不是当即被调用的哦,后面会讲到什么时候被调用。总之会被调用,但不是这里。
p是啥,,这个p就是NioServerSocketChannel里成员变量,准确的说是父类父类父类的成员变量,即AbstractChannel的成员变量。下图证:
那个initChannel的方法里的pipeline变量也是p。那pipeline是啥呢?之后会讲的,这里只要知道它是一个双向链表结构就能够了。
所以这段的意思是将handler对象放入到pipeline中。
看到这里,一下懵了,NioServerSocketChannel里面啥时候实例化的EventLoop对象呀?说明下,此刻尚未实例化,由于没有调用该方法,因此暂时不会又null异常。
这段的逻辑就是向pipeline中添加链接器ServerBootstrapAcceptor。
好了,今天先到这,下面两步代码太深,明天再继续吧。最后,必定要记住继承的关系。