9 ChannelHandlerContext,ChannelInitializer组件的理解(之间的关系)与笔记

我们今天来看看ChannelInitializer组件

ChannelInitializer这个组件我们可以理解为Channel的便捷对象,在我提供的程序中,也用到了这个类,我们来看看他到底起的是什么样的作用呢?
在这里插入图片描述
我们看到我们自己写的MyServerInitializer 这个类是继承自它的,接着我们看看它的Doc与,描述看看与这个类的作用

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
    	//获取到它的pipeline对象并且吧我们需要的对应的编码器添加到pipeline对象中
        ChannelPipeline pipeline = ch.pipeline();
        //添加一些默认的编码器与解码器
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        //我们自己所编写的业务逻辑Handle
        pipeline.addLast(new MyServerHandler());
    }
}

ChannelInitializer类图

在这里插入图片描述

需要注意的是:

a. ChannelInitializer继承于ChannelInboundHandler接口

b. ChannelInitializer是一个抽象类,不能直接使用

  1. initChannel抽象方法
    ChannelInitializer中声明了一个名为initChannel的抽象方法:

/**
 * This method will be called once the {@link Channel} was registered. After the method returns this instance
 * will be removed from the {@link ChannelPipeline} of the {@link Channel}.
 *
 * @param ch            the {@link Channel} which was registered.
 * @throws Exception    is thrown if an error occurs. In that case it will be handled by
 *                      {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
 *                      the {@link Channel}.
 */
protected abstract void initChannel(C ch) throws Exception;
ChannelInitializer的实现类必须要重写这个方法,这个方法在Channel被注册到EventLoop的时候会被调用

ChannelInitializer什么时候会被调用?

以ServerBootstrap启动这一场景为例

在ServerBootstrap.init()方法中,负责accept新链接的Channel的pipeline被添加了一个ChannelInitializer

p.addLast(new ChannelInitializer<Channel>() {
    @Override
    public void initChannel(final Channel ch) throws Exception {
        final ChannelPipeline pipeline = ch.pipeline();
        ChannelHandler handler = config.handler();
        if (handler != null) {
            pipeline.addLast(handler);
        }

        ch.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                pipeline.addLast(new ServerBootstrapAcceptor(
                        ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
            }
        });
    }
});

由于此时这个Channel还没有被register到EventLoop,于是在addLast方法的调用链中,会给pipeline添加一个PendingHandlerAddedTask,其目的是在Channel被register到EventLoop的时候,触发一个回调事件

然后在AbstractBootstrap.initAndRegister()方法中,这个Channel会被register到boss EventLoopGoup,接着会被register到boss EventLoopGoup中的某一个具体的EventLoop

在AbstractChannel.register0()方法中,之前注册的PendingHandlerAddedTask会被调用,经过一系列调用之后,ChannelInitializer.handleAdded()方法会被触发:
    /**
     * {@inheritDoc} If override this method ensure you call super!
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isRegistered()) {
            // This should always be true with our current DefaultChannelPipeline implementation.
            // The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
            // surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
            // will be added in the expected order.
            initChannel(ctx);
        }
    }

    @SuppressWarnings("unchecked")
    private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
        if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
            try {
                initChannel((C) ctx.channel());//调用子类重写的initChannel方法
            } catch (Throwable cause) {
                // Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
                // We do so to prevent multiple calls to initChannel(...).
                exceptionCaught(ctx, cause);
            } finally {
                remove(ctx);//将ChannelInitializer从pipeline中移除
            }
            return true;
        }
        return false;
    }


    /**
     * This method will be called once the {@link Channel} was registered. After the method returns this instance
     * will be removed from the {@link ChannelPipeline} of the {@link Channel}.
     *
     * @param ch            the {@link Channel} which was registered.
     * @throws Exception    is thrown if an error occurs. In that case it will be handled by
     *                      {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
     *                      the {@link Channel}.
     */
    protected abstract void initChannel(C ch) throws Exception;


    private void remove(ChannelHandlerContext ctx) {
        try {
            ChannelPipeline pipeline = ctx.pipeline();
            if (pipeline.context(this) != null) {
                pipeline.remove(this);
            }
        } finally {
            initMap.remove(ctx);
        }
    }

大概意思是:

a. 触发ChannelInitializer的initChannel方法,执行子类定义的一系列操作(在ServerBootstrap这个例子中就是将ServerBootstrapAcceptor注册到pipeline中)

b. 将ChannelInitializer从pipeline中移除

总结

ChannelInitializer的主要目的是为程序员提供了一个简单的工具,用于在某个Channel注册到EventLoop后,对这个Channel执行一些初始化操作。ChannelInitializer虽然会在一开始会被注册到Channel相关的pipeline里,但是在初始化完成之后,ChannelInitializer会将自己从pipeline中移除,不会影响后续的操作。

使用场景:

a. 在ServerBootstrap初始化时,为监听端口accept事件的Channel添加ServerBootstrapAcceptor

b. 在有新链接进入时,为监听客户端read/write事件的Channel添加用户自定义的ChannelHandler

接着我们看看 ChannelHandlerContext这个对象

a 这个组件可以理解为ChannelHandler和ChannelPipeline之间的桥梁
b 它可以让ChannelHandler与ChannelPipeline以及其他handler交互,此外一个context还可以通知下一个handle,也可以修改pipeline中的对象
c 你可以通过获取到pipeline之后,获取到context进行相关对象的操作,可以动态的去操作
如它的里面的Doc来说:

    可以提前把ChannelHandlerContext 保存起来供后面使用
    例如*在处理程序方法之外触发事件,甚至可以从不同的线程触发。
    //创建一个MyHandler 并且继承 ChannelDuplexHandle 
      public class MyHandler extends {@link ChannelDuplexHandler} {
 *		//提前获取到一个context对象  通过构造函数赋值
 *     private  ChannelHandlerContext ctx ;
 * 
 *	
 *     public void beforeAdd({@link ChannelHandlerContext} ctx) {
 *         <b>this.ctx = ctx;</b>
 *     }
 *     public void login(String username, password) {
 *         //在在另外的业务逻辑当中处理别的业务.
 * 			//在其他地方操作
 *         ctx.write(new LoginMessage(username, password));
 *     }
 *     ...
 * } 

ChannelHandlerContext特点

1 通常情况下一个Handle有一个Context对象与之对应 ,但是也允许一个Handle可以有多个Context对象  

2 一个pipeline可以多次添加,同一个Handler对象,并且,每添加一次就会有之对应的Context对应自动生成. 

3 ChannelHandlerContext 是连接两个组件之间的桥梁	 
问题 pipeline 本身是一个存放Handler容器, 那pipeline与Context之间的关系是什么?

a , pipeline本身是一个与Channel所关联的容器对象.存放的是ChannelHandle对象
b ,ChannelHandlerContext – >是Netty自动生成的, 是 ChannelHandle 与 pipeline之间的一个纽带
c ,将Handle 关联到Netty创建的Context当中 并且吧Context 加入到pipeline 当中
d ,用 ChannelInitializer 一次性添加的若干个对象,并且添加进去,我们自己手写的那个也就是Myinit()的方法,添加完成之后便会删除该 ChannelInitializer对象
e ,实例中虽然添加的是Handler对象.但是Netty会自动的会吧该handle生成context对象并且把,它添加到pipeline当中

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值