我要设置receiveBufferSize, 那么我就要config.put( “receiveBufferSize”, 1024 ); 这有一个弊端, 让使用
者需要手动输入字符串key, 而且, 如果key需要有些特殊的属性等信息, 也没法实现, 这些key通常是固定的, 也就是
我们所说的常量, 于是Netty为了能够让key拥有更多的功能, 而不是仅仅用字符串来表示, 就出现了ChannelOption
这个类, 我们来看看ChannelOption的继承关系及相关代码(继承关系如下图):
public interface Constant<T extends Constant<T>> extends Comparable<T> {
int id();
String name();
}
public abstract class AbstractConstant implements Constant{
private final int id;
private final String name;
getter / setter / toString / hashCode / equals / compareTo
}
public class ChannelOption<T> extends AbstractConstant<ChannelOption<T>> {
private static final ConstantPool<ChannelOption<Object>> pool =
new ConstantPool<ChannelOption<Object>>() {
@Override
protected ChannelOption<Object> newConstant(int id, String name) {
return new ChannelOption<Object>(id, name);
}
};
public static <T> ChannelOption<T> valueOf(String name) {
return (ChannelOption<T>) pool.valueOf(name);
}
public static final ChannelOption<Integer> SO_TIMEOUT = valueOf("SO_TIMEOUT");
public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF");
}
分析:
上面一共三个类, Constant是一个常量接口, 定义了id和name方法, AbstractConstant对Constant中的id和
name方法进行了实现, 其实就等于get方法而已, 可以看到, 每一个常量对象都有一个name, 这个name就是这个
常量对象的字符串表示形式, 更加具体一点, 之前我们设置到ServerSocketChannel中的receiveBufferSize这
个配置, 就对应一个常量对象, 常量对象的name就是receiveBufferSize, id是一个随机生成的值, 用来表示唯
一性, 在这里我们仅仅分析到了AbstractConstant, 抽象类, 定义了所有情况下的常量的公共信息id和name
再往后, 要配置Channel, 那么就出现了ChannelOption, 要配置数据库就可能会出现DatabaseOption(这一个
是为了让大家更好的理解常量的含义, 笔者臆想的)
于是ChannelOption表示用于Channel配置信息中的key, 我们看到这个ChannelOption的源码, 一开始出现了
一个pool, 池子, 就是本文的核心主题, 常量池, 这个池子的作用是用来保存常量的, 假设我有许多的Channel
配置, 那么我自然就需要有许多的ChannelOption, 由于配置的key一定是固定的, 那么在使用的时候, 如果要
更改配置信息, 总不能每次都new一个ChannelOption来表示配置信息的key吧, 于是, 这些key就被保存到了常量
池中, ConstantPool就是用来保存这些配置对应的key的, ConstantPool是一个抽象类, 里面就一个
ConcurrentMap来保存, ConcurrentMap的key一定是字符串, value是泛型, 泛型为ChannelOption的时候, 表
示这个常量池中保存的是ChannleOption, 泛型为DatabaseOption, 表示这个常量池中保存的是
DatabaseOption, 这个应该好理解, 不同类型的常量对象, 只需要在其类中创建一个ConstantPool就好了, 父
类的ConcurrentMap是公共需要的, 但是ConcurrentMap里面存的值却需要子类来定义, 同时实现newConstant
方法表示这个池子中常量的创建, 不知道说到这里大家会不会觉得有点绕, 我们以SO_RCVBUF这个ChannelOption
的创建过程及存储过程的源码进行一下分析吧
public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF"), 通过valueOf作为
入口进行创建, 可以联想到, 这个SO_RCVBUF就是常量池中ConcurrentMap的key, 与此同时会创建一个
ChannelOption, 这个ChannelOption的name也是SO_RCVBUF, 我们再来看看ConstantPool的valueOf源码
public T valueOf(String name) {
checkNotNullAndNotEmpty(name);
return getOrCreate(name);
}
private T getOrCreate(String name) {
T constant = constants.get(name);
if (constant == null) {
final T tempConstant = newConstant(nextId(), name);
constant = constants.putIfAbsent(name, tempConstant);
if (constant == null) {
return tempConstant;
}
}
return constant;
}
其实很简单, constants就是那个ConcurrentMap, 通过key为SO_RCVBUF去这个map查找对应的ChannelOption
如果没找到, 那么就调用newConstant创建一个该常量, 并且放入到ConcurrentMap中, newConstant由子类来
觉得创建的是哪个类型的常量, ChannelOption有一个匿名内部类是ConstantPool的子类, 之前我们也看过了,
其创建的是ChannelOption
总结:
任何配置都是一个key-value, ConstantPool用来保存任何配置中的key的对象表示形式, 其实就是用一个
ConcurrentMap来保存的, ConstantPool的子类来觉得这个Map中的value存储的是什么类型值, Channel的配置
对应的key用ChannelOption来表示, ChannelOption里面有一个name用来存储这个配置的字符串表示,
ChannelOption中利用一个匿名内部类继承于ConstantPool, 利用泛型指明了ConcurrentMap中保存的是
ChannelOption类型, 对于Channel中的SO_RCVBUF这个配置来说, 会创建一个ChannelOption,
ChannelOption中的name就是SO_RCVBUF, 与此同时将这个ChannelOption保存到常量池中
ChannelConfig
ChannelConfig, 故名思意了, 保存了Channel的配置, 比如说接收缓冲区的大小等, Netty中的Channel有很多类型,
对于Nio的是NioServerSocketChannel以及NioSocketChannel, 对于Bio的OioServerSocketChannel以及
OioSocketChannel, 不同类型的Channel配置自然就不一样, 如下图所示, 就是ChannelConfig的简单类图, 可以清
晰的看到, 在红线的两边刚好分为了两块, 一个是Nio的ChannelConfig, 一个是Bio的ChannelConfig, 都是用来存
储对应的Channel的配置信息的, 那到底怎么存呢?其实也没那么复杂, 就用一个Map存就好了, 比如说对于服务端需要
存储SO_RCVBUF这个配置信息, 正常情况应该是map.put( “SO_RCVBUF”, 1024 ), 但是在Netty中, 用
ChannelOption对象的方式来表示这个配置的key, 于是就变成了map.put( ChannelOption.SO_RCVBUF, 1024 ),
到此为止, 我们就将ChannelConfig - ChannelOption - ConstantPool的关系给描述完毕了
DefaultAttributeMap及AttributeKey
前面我们熟悉了ConstantPool与ChannelOption之间的关系, 以及ChannelConfig与ChannelOption之间的关系, 此
时再来看ConstantPool-AttributeKey-DefaultAttributeMap就会非常轻松了
我们在使用Netty的时候, 对ChannelHandler肯定是不会陌生的, 一个客户端与服务器的数据交互, 当数据到达了服
务端Netty程序的时候, 必然是通过多个ChannelHandler进行处理的, 比如先由LengthFieldPrepender这个
ChannelHandler对数据进行解码, 然后传给下一层级的ChannelHandler, 当数据写回客户端的时候, 同样会经过多
个ChannelHandler, 最后经过LengthFieldBasedFrameDecoder这个ChannelHandler进行编码并write回客户端,
这一整个生命周期中, 都是对一个SocketChannel进行操作, 那么假设想要从前一个ChannelHandler传递一些数据到
后一个ChannelHandler, 就需要将放置的数据存在到一个地方中, 所有的ChannelHandler都能够访问这块空间
DefaultAttributeMap就是这一块空间, 我们使用的NioServerSocketChannel就是这个类的子类,
NioServerSocketChannel之后的文章我们会进行分析, 那知道这两者关系的情况下, 我们就可以想到, 所有的
ChannelHandler中, 获取到对应的Channel, 就能访问这个Channel的共享空间DefaultAttributeMap, 可以往里面
写入key-value, 或者根据key读取value, 而这个key就是AttributeKey, value是我们自定义的值
总结
总体来说,如果你想转行从事程序员的工作,Java开发一定可以作为你的第一选择。但是不管你选择什么编程语言,提升自己的硬件实力才是拿高薪的唯一手段。
如果你以这份学习路线来学习,你会有一个比较系统化的知识网络,也不至于把知识学习得很零散。我个人是完全不建议刚开始就看《Java编程思想》、《Java核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。
这些视频如果需要的话,可以无偿分享给大家,点击这里即可免费领取
a核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。
这些视频如果需要的话,可以无偿分享给大家,点击这里即可免费领取