etty4的代碼比我想象的要複雜的多,不過Netty4很好的將這種複雜性隱藏了起來,暴露出來的,是一個相對容易使用的接口。Bootstrap就是Netty試圖隱藏這種複雜性的一個例子。java
bootstrap包是Netty4代碼裏最簡單的一個包,總共只有4個類:bootstrap
AbstractBootstrap是抽象類,有兩個具體的實現,Bootstrap和ServerBootstrap:promise
Netty4自帶的例子裏有一個EchoClient,看看它是如何使用Bootstrap啓動一個客戶端程序的:服務器
public class EchoClient { private final String host; private final int port; private final int firstMessageSize; public EchoClient(String host, int port, int firstMessageSize) { this.host = host; this.port = port; this.firstMessageSize = firstMessageSize; } public void run() throws Exception { // Configure the client. EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( //new LoggingHandler(LogLevel.INFO), new EchoClientHandler(firstMessageSize)); } }); // Start the client. ChannelFuture f = b.connect(host, port).sync(); // Wait until the connection is closed. f.channel().closeFuture().sync(); } finally { // Shut down the event loop to terminate all threads. group.shutdownGracefully(); } } public static void main(String[] args) throws Exception { // ... } }
看上面的例子,Bootstrap的使用很像Builder模式,Bootstrap就是Builder,EventLoopGroup、Channel和Handler等是各類Part。稍有不一樣的是,準備好各類Part後,並非直接build出一個Product來,而是直接經過connect()方法使用這個Product。ide
爲了弄清楚Bootstrap如何工做,咱們先從AbstractBootstrap入手:函數
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable { private volatile EventLoopGroup group; private volatile ChannelFactory<? extends C> channelFactory; private volatile SocketAddress localAddress; private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>(); private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>(); private volatile ChannelHandler handler; // ... }
能夠看到,AbstractBootstrap這個抽象Builder一共須要有6個Part,以下圖所示:oop
AbstractBootstrap有一組方法用來設置各個Part,例以下面這些:ui
public B group(EventLoopGroup group) public B channel(Class<? extends C> channelClass) public B channelFactory(ChannelFactory<? extends C> channelFactory) public B localAddress(SocketAddress localAddress) public <T> B option(ChannelOption<T> option, T value) public <T> B attr(AttributeKey<T> key, T value) public B handler(ChannelHandler handler)
還有一組對應方法得到各個Part,以下:this
final SocketAddress localAddress() final ChannelFactory<? extends C> channelFactory() final ChannelHandler handler() public final EventLoopGroup group() final Map<ChannelOption<?>, Object> options() final Map<AttributeKey<?>, Object> attrs()
AbstractBootstrap經過ChannelFactory建立Channel實例,channel(channelClass)方法看起來好像是設置了一個Channel,但實際上只是設置了默認的ChannelFactory實現:spa
public B channel(Class<? extends C> channelClass) { if (channelClass == null) { throw new NullPointerException("channelClass"); } return channelFactory(new BootstrapChannelFactory<C>(channelClass)); }
默認的ChannelFactory實現使用反射建立Channel實例:
private static final class BootstrapChannelFactory<T extends Channel> implements ChannelFactory<T> { private final Class<? extends T> clazz; BootstrapChannelFactory(Class<? extends T> clazz) { this.clazz = clazz; } @Override public T newChannel() { try { return clazz.newInstance(); } catch (Throwable t) { throw new ChannelException("Unable to create Channel from class " + clazz, t); } } }
再來看Bootstrap類的connect()方法:
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { if (remoteAddress == null) { throw new NullPointerException("remoteAddress"); } validate(); return doConnect(remoteAddress, localAddress); }
connect()方法調用validate()方法看各個Part是否準備就緒,而後調用doConnect()方法:
private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) { final ChannelFuture regFuture = initAndRegister(); final Channel channel = regFuture.channel(); if (regFuture.cause() != null) { return regFuture; } final ChannelPromise promise = channel.newPromise(); if (regFuture.isDone()) { doConnect0(regFuture, channel, remoteAddress, localAddress, promise); } else { regFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { doConnect0(regFuture, channel, remoteAddress, localAddress, promise); } }); } return promise; }
doConnect()方法首先調用了initAndRegister()方法,而後又調用了doConnect0()方法,方法調用示意圖以下:
final ChannelFuture initAndRegister() { final Channel channel = channelFactory().newChannel(); try { init(channel); } catch (Throwable t) { channel.unsafe().closeForcibly(); return channel.newFailedFuture(t); } ChannelPromise regPromise = channel.newPromise(); group().register(channel, regPromise); // ... }
initAndRegister()方法用ChannelFactory建立了一個Channel的實例,而後調用init()方法初始化Channel,最後將Channel註冊到EventLoopGroup上:
而Channel在實例化的時候已經自動關聯了Pipeline,這點從AbstractChannel的構造函數能夠看出:
protected AbstractChannel(Channel parent) { this.parent = parent; unsafe = newUnsafe(); pipeline = new DefaultChannelPipeline(this); }
void init(Channel channel) throws Exception { ChannelPipeline p = channel.pipeline(); p.addLast(handler()); // ... }
Bootstrap.init()方法把Handler添加到了Pipeline的末尾,到這裏,Channel就準備就緒了:
繼續Bootstrap.doConnect()
initAndRegister()方法結束以後,doConnect()方法緊接着調用了doConnect0()方法,doConnect0()方法繼而調用了Channel.connect()方法,這樣Channel就接通服務器,能夠收發消息了!