最近在一个使用mina框架的项目中总是出现一些莫名其妙的问题,加入多个filter进行解码的时候,解码顺序等一直出现不可预测的问题。苦苦研究了 mina源码,终于找到问题所在。
- public class ProtocolCodecFilter extends IoFilterAdapter {
- 50 /** A logger for this class */
- 51 private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolCodecFilter.class);
- 52
- 53 private static final Class<?>[] EMPTY_PARAMS = new Class[0];
- 54 private static final IoBuffer EMPTY_BUFFER = IoBuffer.wrap(new byte[0]);
- 55
- 56 private final AttributeKey ENCODER = new AttributeKey(ProtocolCodecFilter.class, "encoder");
- 57 private final AttributeKey DECODER = new AttributeKey(ProtocolCodecFilter.class, "decoder");
- 58 private final AttributeKey DECODER_OUT = new AttributeKey(ProtocolCodecFilter.class, "decoderOut");
- 59 private final AttributeKey ENCODER_OUT = new AttributeKey(ProtocolCodecFilter.class, "encoderOut");
根据以上源码得知:凡是继承ProtocolCodecFilter类的filter会有几个AttributeKey。这些AttributeKey最终会被放入session中作为key。
我本来以为不同的类继承 ProtocolCodecFilter后实例化出来的AttributeKey在session的map中会被视为不同的key。事实并非如此。
我们再看下AttributeKey 的hashcode方法
- @Override
- 71 public int hashCode() {
- 72 int h = 17 * 37 + ((name == null) ? 0 : name.hashCode());
- 73 return h;
- 74 }
这个方法被重写了。
所以 结果就是导致几个继承ProtocolCodecFilter类filter在session的attribute中以上key其实都共享了一个value。
想想就会出现不可预测的问题。果然。再看一段ProtocolCodecFilter代码
- private static class ProtocolDecoderOutputImpl extends
- 418 AbstractProtocolDecoderOutput {
- 419 public ProtocolDecoderOutputImpl() {
- 420 // Do nothing
- 421 }
- 422
- 423 public void flush(NextFilter nextFilter, IoSession session) {
- 424 Queue<Object> messageQueue = getMessageQueue();
- 425
- 426 while (!messageQueue.isEmpty()) {
- 427 nextFilter.messageReceived(session, messageQueue.poll());
- 428 }
- 429 }
- 430 }
ProtocolDecoderOutputImpl 被多个filter共享在ENCODER_OUT key中。
其中的ProtocolDecoderOutputImpl 中 messageQueue同时也被多个filter共享。
好了问题出现了:
第一个filter 中的decode 解码成功 放入messageQueue一个对象 然后用out.write()方法调用下一个filter。
- while (!messageQueue.isEmpty()) {
- 427 nextFilter.messageReceived(session, messageQueue.poll());
- 428 }
循环中第二个filter 中的decode 拿到对象,解码成功,放入messageQueue一个对象。
问题出现了:
!messageQueue.isEmpty() 这个再次出现 true结果 ,因为第二个filter中的decode放入了一个对象。
当再次执行nextFilter.messageReceived(session, messageQueue.poll()); 立刻出现问题,因为messageQueue.poll()出来的对象是刚刚第二个filter 中的decode解码后放进去的。
总结: 不要用多个继承同一超类的filter。