最近在一个使用mina框架的项目中总是出现一些莫名其妙的问题,加入多个filter进行解码的时候,解码顺序等一直出现不可预测的问题。苦苦研究了 mina源码,终于找到问题所在。

  1. public class ProtocolCodecFilter extends IoFilterAdapter { 
  2. 50      /** A logger for this class */ 
  3. 51      private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolCodecFilter.class); 
  4. 52   
  5. 53      private static final Class<?>[] EMPTY_PARAMS = new Class[0]; 
  6. 54      private static final IoBuffer EMPTY_BUFFER = IoBuffer.wrap(new byte[0]); 
  7. 55   
  8. 56      private final AttributeKey ENCODER = new AttributeKey(ProtocolCodecFilter.class, "encoder"); 
  9. 57      private final AttributeKey DECODER = new AttributeKey(ProtocolCodecFilter.class, "decoder"); 
  10. 58      private final AttributeKey DECODER_OUT = new AttributeKey(ProtocolCodecFilter.class, "decoderOut"); 
  11. 59      private final AttributeKey ENCODER_OUT = new AttributeKey(ProtocolCodecFilter.class, "encoderOut"); 

根据以上源码得知:凡是继承ProtocolCodecFilter类的filter会有几个AttributeKey。这些AttributeKey最终会被放入session中作为key。

我本来以为不同的类继承 ProtocolCodecFilter后实例化出来的AttributeKey在session的map中会被视为不同的key。事实并非如此。

我们再看下AttributeKey 的hashcode方法

 

  1.  @Override 
  2. 71      public int hashCode() { 
  3. 72          int h = 17 * 37 + ((name == null) ? 0 : name.hashCode()); 
  4. 73          return h; 
  5. 74      } 

这个方法被重写了。

所以 结果就是导致几个继承ProtocolCodecFilter类filter在session的attribute中以上key其实都共享了一个value。

想想就会出现不可预测的问题。果然。再看一段ProtocolCodecFilter代码

 

  1.  private static class ProtocolDecoderOutputImpl extends 
  2. 418             AbstractProtocolDecoderOutput { 
  3. 419         public ProtocolDecoderOutputImpl() { 
  4. 420             // Do nothing 
  5. 421         } 
  6. 422  
  7. 423         public void flush(NextFilter nextFilter, IoSession session) { 
  8. 424             Queue<Object> messageQueue = getMessageQueue(); 
  9. 425              
  10. 426             while (!messageQueue.isEmpty()) { 
  11. 427                 nextFilter.messageReceived(session, messageQueue.poll()); 
  12. 428             } 
  13. 429         } 
  14. 430     } 

ProtocolDecoderOutputImpl 被多个filter共享在ENCODER_OUT key中。

其中的ProtocolDecoderOutputImpl 中 messageQueue同时也被多个filter共享。

好了问题出现了:

第一个filter 中的decode 解码成功 放入messageQueue一个对象 然后用out.write()方法调用下一个filter。

 

  1.  while (!messageQueue.isEmpty()) {  
  2. 427                 nextFilter.messageReceived(session, messageQueue.poll());  
  3. 428             }  

循环中第二个filter 中的decode 拿到对象,解码成功,放入messageQueue一个对象。

问题出现了:

!messageQueue.isEmpty() 这个再次出现 true结果 ,因为第二个filter中的decode放入了一个对象。

当再次执行nextFilter.messageReceived(session, messageQueue.poll()); 立刻出现问题,因为messageQueue.poll()出来的对象是刚刚第二个filter 中的decode解码后放进去的。

 

总结: 不要用多个继承同一超类的filter。