Java设计模式的应用场景学习

1.第一种:单例模式

  饿汉式

 

比如在Dubbo中的这段代码,创建一个配置管理器。
public class ConfigManager {
    private static final ConfigManager configManager = new ConfigManager(); 
    private ConfigManager() {}
    public static ConfigManager getInstance() {
        return configManager;
    }
}
又或者在RocketMQ中,创建一个MQ客户端实例的时候。
public class MQClientManager {

    private static MQClientManager instance = new MQClientManager();
    private MQClientManager() {}
    public static MQClientManager getInstance() {
        return instance;
    }
}

懒汉式

我们来看Seata框架中,通过这种方式来创建一个配置类。
public class ConfigurationFactory{

    private static volatile Configuration CONFIG_INSTANCE = null;
    public static Configuration getInstance() {
        if (CONFIG_INSTANCE == null) {
            synchronized (Configuration.class) {
                if (CONFIG_INSTANCE == null) {
                    CONFIG_INSTANCE = buildConfiguration();
                }
            }
        }
        return CONFIG_INSTANCE;
    }
}

静态内部类

可以看到,通过双重检查锁的方式来创建单例对象,还是比较复杂的。又是加锁,又是判断两次,还需要加volatile修饰的。
使用静态内部类的方式,可以达到双重检查锁相同的功效,但实现上简单了。
在Seata框架中,创建RM事件处理程序器的时候,就使用了静态内部类的方式来创建单例对象。
public class DefaultRMHandler extends AbstractRMHandler{ protected DefaultRMHandler() { initRMHandlers(); } private static class SingletonHolder { private static AbstractRMHandler INSTANCE = new DefaultRMHandler(); } public static AbstractRMHandler get() { return DefaultRMHandler.SingletonHolder.INSTANCE; } }

 

 

2.第二种:工厂模式

  简单工厂:  

定义:
简单工厂,的确比较简单,它的作用就是把对象的创建放到一个工厂类中,通过参数来创建不同的对象。
示例:
在分布式事务框架Seata中,如果发生异常,则需要进行二阶段回滚。
它的过程是,通过事务id找到undoLog记录,然后解析里面的数据生成SQL,将一阶段执行的SQL给撤销掉。
问题是SQL的种类包含了比如INSERT、UPDATE、DELETE,所以它们反解析的过程也不一样,就需要不同的执行器去解析。
在Seata中,有一个抽象的撤销执行器,可以生成一条SQL。
public abstract class AbstractUndoExecutor{ //生成撤销SQL protected abstract String buildUndoSQL(); } 然后有一个获取撤销执行器的工厂,根据SQL的类型,创建不同类型的执行器并返回。 public class UndoExecutorFactory { public static AbstractUndoExecutor getUndoExecutor(String dbType, SQLUndoLog sqlUndoLog) { switch (sqlUndoLog.getSqlType()) { case INSERT: return new MySQLUndoInsertExecutor(sqlUndoLog); case UPDATE: return new MySQLUndoUpdateExecutor(sqlUndoLog); case DELETE: return new MySQLUndoDeleteExecutor(sqlUndoLog); default: throw new ShouldNeverHappenException(); } } } 使用的时候,直接通过工厂类获取执行器。 AbstractUndoExecutor undoExecutor = UndoExecutorFactory.getUndoExecutor(dataSourceProxy.getDbType(),sqlUndoLog); undoExecutor.executeOn(conn); 总结:
简单工厂模式的优点,想必各位都能领会,我们不再赘述。但它还有个小小的缺点: 一旦有了新的实现类,就需要修改工厂实现,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。

工厂方法

定义:
工厂方法模式解决了上面那个问题。它可以创建一个工厂接口和多个工厂实现类,
这样如果增加新的功能,只需要添加新的工厂类就可以,不需要修改之前的代码。
另外,工厂方法模式还可以和模板方法模式结合一起,
将他们共同的基础逻辑抽取到父类中,其它的交给子类去实现。
示例:
在Dubbo中,有一个关于缓存的设计完美的体现了工厂方法模式
+模板方法模式。
首先,有一个缓存的接口,它提供了设置缓存和获取缓存两个方法。
public interface Cache { void put(Object key, Object value); Object get(Object key); } 然后呢,还有一个缓存工厂,它返回一个缓存的实现。 public interface CacheFactory { Cache getCache(URL url, Invocation invocation); } 由于结合了模板方法模式,所以Dubbo又搞了个抽象的缓存工厂类,它实现了缓存工厂的接口。 public abstract class AbstractCacheFactory implements CacheFactory { //具体的缓存实现类 private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>(); @Override public Cache getCache(URL url, Invocation invocation) { url = url.addParameter(Constants.METHOD_KEY, invocation.getMethodName()); String key = url.toFullString(); Cache cache = caches.get(key); if (cache == null) { //创建缓存实现类,交给子类实现 caches.put(key, createCache(url)); cache = caches.get(key); } return cache; } //抽象方法,交给子类实现 protected abstract Cache createCache(URL url); } 在这里,公共的逻辑就是通过getCahce()创建缓存实现类,那具体创建什么样的缓存实现类,就由子类去决定。 所以,每个子类都是一个个具体的缓存工厂类,比如包括: ExpiringCacheFactory、JCacheFactory、LruCacheFactory、ThreadLocalCacheFactory。 这些工厂类,只有一个方法,就是创建具体的缓存实现类。 public class ThreadLocalCacheFactory extends AbstractCacheFactory { @Override protected Cache createCache(URL url) { return new ThreadLocalCache(url); } } 这里的ThreadLocalCache就是具体的缓存实现类,比如它是通过ThreadLocal来实现缓存功能。 public class ThreadLocalCache implements Cache { private final ThreadLocal<Map<Object, Object>> store; public ThreadLocalCache(URL url) { this.store = new ThreadLocal<Map<Object, Object>>() { @Override protected Map<Object, Object> initialValue() { return new HashMap<Object, Object>(); } }; } @Override public void put(Object key, Object value) { store.get().put(key, value); } @Override public Object get(Object key) { return store.get().get(key); } } 那在客户端使用的时候,还是通过工厂来获取缓存对象。 public static void main(String[] args) { URL url = URL.valueOf("http://localhost:8080/cache=jacache&.cache.write.expire=1"); Invocation invocation = new RpcInvocation(); CacheFactory cacheFactory = new ThreadLocalCacheFactory(); Cache cache = cacheFactory.getCache(url, invocation); cache.put("java","java"); System.out.println(cache.get("java")); }
总结: 这样做的好处有两点。 第一,如果增加新的缓存实现,只要添加一个新的缓存工厂类就可以,别的都无需改动。 第二,通过模板方法模式,封装不变部分,扩展可变部分。 提取公共代码,便于维护。 另外,在Dubbo中,注册中心的获取也是通过工厂方法来实现的。

抽象工厂

定义:
抽象工厂模式,它能创建一系列相关的对象, 而无需指定其具体类。
工厂方法模式和抽象工厂模式,它们之间最大的区别在于: 工厂方法模式只有一个抽象产品类,具体工厂类只能创建一个具体产品类的实例; 抽象工厂模式有多个抽象产品类,具体工厂类可以创建多个具体产品类的实例。 我们拿上面缓存的例子来继续往下说。 如果我们现在有一个数据访问程序,需要同时操作缓存和数据库,那就需要多个抽象产品和多个具体产品实现。 缓存相关的产品类都已经有了,我们接着来创建数据库相关的产品实现。
实现:
首先,有一个数据库接口,它是抽象产品类。
public interface DataBase { void insert(Object tableName, Object record); Object select(Object tableName); } 然后,我们创建两个具体产品类MysqlDataBase和OracleDataBase。 public class MysqlDataBase implements DataBase{ Map<Object,Object> mysqlDb = new HashMap<>(); @Override public void insert(Object tableName, Object record) { mysqlDb.put(tableName,record); } @Override public Object select(Object tableName) { return mysqlDb.get(tableName); } } public class OracleDataBase implements DataBase { Map<Object,Object> oracleDb = new HashMap<>(); @Override public void insert(Object tableName, Object record) { oracleDb.put(tableName,record); } @Override public Object select(Object tableName) { return oracleDb.get(tableName); } } 其次,创建抽象的工厂类,它可以返回一个缓存对象和数据库对象。 public interface DataAccessFactory { Cache getCache(URL url); DataBase getDb(); } 最后是具体的工厂类,可以根据实际的需求,任意组合每一个具体的产品。 比如我们需要一个基于ThreadLocal的缓存实现和基于Mysql的数据库对象。 public class DataAccessFactory1 implements DataAccessFactory { @Override public Cache getCache(URL url) { return new ThreadLocalCache(url); } @Override public DataBase getDb() { return new MysqlDataBase(); } } 如果需要一个基于Lru的缓存实现和基于Oracle的数据库对象。 public class DataAccessFactory2 implements DataAccessFactory { @Override public Cache getCache(URL url) { return new LruCache(url); } @Override public DataBase getDb() { return new OracleDataBase(); } } 总结:
可以看到,抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。
由于这种隔离,更换一个具体工厂就变得相对容易,
所有的具体工厂都实现了抽象工厂中定义的那些公共接口,
因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

 

 

3.第三种:模板方法模式  

定义:
  在模板模式中,一个抽象类公开定义了执行它的方法的方式/模板。
它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 简单来说,有多个子类共有的方法,且逻辑相同,可以考虑作为模板方法。

示例:

    在上面Dubbo缓存的例子中,我们已经看到了模板方法模式的应用。

    但那个是和工厂方法模式结合在一块的,我们再单独找找模板方法模式的应用。

  我们知道,当我们的Dubbo应用出现多个服务提供者时,
服务消费者需要通过负载均衡算法,选择其中一个服务来进行调用。


    首先,有一个LoadBalance接口,返回一个Invoker。

   public interface LoadBalance {
       <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
   }

    然后定义一个抽象类,AbstractLoadBalance,实现LoadBalance接口。

   public abstract class AbstractLoadBalance implements LoadBalance {

      @Override
      public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
         if (invokers == null || invokers.isEmpty()) {
            return null;
         }
         if (invokers.size() == 1) {
            return invokers.get(0);
         }
        return doSelect(invokers, url, invocation);
     }
     //抽象方法,由子类选择一个Invoker
     protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);
   }

   这里的公共逻辑就是两个判断,判断invokers集合是否为空或者是否只有一个实例。如果是的话,就无需调用子类,直接返回就好了。

   具体的负载均衡实现有四个:

      基于权重随机算法的 RandomLoadBalance

      基于最少活跃调用数算法的 LeastActiveLoadBalance

      基于 hash 一致性的 ConsistentHashLoadBalance

      基于加权轮询算法的 RoundRobinLoadBalance

    public class RandomLoadBalance extends AbstractLoadBalance {
       @Override
       protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
          //省略逻辑....
          return invokers.get(ThreadLocalRandom.current().nextInt(length));
       }
    }

    它们根据不同的算法实现,来返回一个具体的Invoker对象。

 

 

 

4.第四种:构造器模式   

定义:
  构造器模式使用多个简单的对象一步一步构建成一个复杂的对象。
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式,常见于在构建一个复杂的对象,里面可能包含一些业务逻辑,
比如检查,属性转换等。如果都在客户端手动去设置,那么会产生大量的冗余代码。
那么这时候,我们就可以考虑使用构造器模式。

示例:
比如,在Mybatis中,MappedStatement的创建过程就使用了构造器模式。
我们知道,XML文件中的每一个SQL标签就要生成一个MappedStatement对象,
它里面包含很多个属性,我们要构造的对象也是它。
public final class MappedStatement {
    private String resource;
    private Configuration configuration;
    private String id;
    private SqlSource sqlSource;
    private ParameterMap parameterMap;
    private List<ResultMap> resultMaps;
    //.....省略大部分属性
}
然后有一个内部类Builder,它负责完成MappedStatement对象的构造。
首先,这个Builder类,通过默认的构造函数,先完成对MappedStatement对象,部分的构造。
public static class Builder {

    private MappedStatement mappedStatement = new MappedStatement();
   
    public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
        mappedStatement.configuration = configuration;
        mappedStatement.id = id;
        mappedStatement.sqlSource = sqlSource;
        mappedStatement.statementType = StatementType.PREPARED;
        mappedStatement.resultSetType = ResultSetType.DEFAULT;
        //.....省略大部分过程
    }
}
然后,通过一系列方法,可以设置特定的属性,并返回这个Builder类,这里的方法适合处理一些业务逻辑。
public static class Builder {

    public Builder parameterMap(ParameterMap parameterMap) {
      mappedStatement.parameterMap = parameterMap;
      return this;
    }
   
    public Builder resultMaps(List<ResultMap> resultMaps) {
      mappedStatement.resultMaps = resultMaps;
      for (ResultMap resultMap : resultMaps) {
        mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
      }
      return this;
    }
   
    public Builder statementType(StatementType statementType) {
      mappedStatement.statementType = statementType;
      return this;
    }

    public Builder resultSetType(ResultSetType resultSetType) {
      mappedStatement.resultSetType = resultSetType == null ? ResultSetType.DEFAULT : resultSetType;
      return this;
    }
}
最后呢,就是提供一个build方法,返回构建完成的对象就好了。
public MappedStatement build() {
    assert mappedStatement.configuration != null;
    assert mappedStatement.id != null;
    assert mappedStatement.sqlSource != null;
    assert mappedStatement.lang != null;
    mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
    return mappedStatement;
}
ps:在客户端使用的时候,先创建一个 Builder,然后链式的调用一堆方法,
最后再调用一次 build() 方法,我们需要的对象就有了,这就是构造器模式的应用。

MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
    .resource(resource)
    .fetchSize(fetchSize)
    .timeout(timeout)
    .statementType(statementType)
    .keyGenerator(keyGenerator)
    .keyProperty(keyProperty)
    .keyColumn(keyColumn)
    .databaseId(databaseId)
    .lang(lang)
    .resultOrdered(resultOrdered)
    .resultSets(resultSets)
    .resultMaps(getStatementResultMaps(resultMap, resultType, id))
    .resultSetType(resultSetType)
    .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
    .useCache(valueOrDefault(useCache, isSelect))
    .cache(currentCache);

ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
return statement;

 

 5.第五种:适配器模式   

定义:
  适配器模式是作为两个不兼容的接口之间的桥梁。
这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。 适配器模式一般用于屏蔽业务逻辑与第三方服务的交互,或者是新老接口之间的差异。

示例:

    我们知道,在Dubbo中,所有的数据都是通过Netty来负责传输的,

    然后这就涉及了消息编解码的问题。


    所以,首先它有一个编解码器的接口,负责编码和解码。

    @SPI
    public interface Codec2 {

        @Adaptive({Constants.CODEC_KEY})
        void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException;
   
        @Adaptive({Constants.CODEC_KEY})
        Object decode(Channel channel, ChannelBuffer buffer) throws IOException;
   
        enum DecodeResult {
             NEED_MORE_INPUT, SKIP_SOME_INPUT
        }
    }

    然后,有几个实现类,比如DubboCountCodec、DubboCodec、ExchangeCodec等。

    但是,当我们打开这些类的时候,就会发现,他们都是Dubbo中普通的类,

    只是实现了Codec2接口,其实不能直接作用于Netty编解码。

    这是因为,Netty编解码需要实现ChannelHandler接口,这样才会被声明成Netty的处理组件。

    比如像MessageToByteEncoder、ByteToMessageDecoder那样。

    鉴于此,Dubbo搞了一个适配器,专门来适配编解码器接口。

  final public class NettyCodecAdapter {

    private final ChannelHandler encoder = new InternalEncoder();
    private final ChannelHandler decoder = new InternalDecoder();
    private final Codec2 codec;
    private final URL url;
    private final org.apache.dubbo.remoting.ChannelHandler handler;
   
    public NettyCodecAdapter(Codec2 codec, URL url, org.apache.dubbo.remoting.ChannelHandler handler) {
        this.codec = codec;
        this.url = url;
        this.handler = handler;
    }
    public ChannelHandler getEncoder() {
        return encoder;
    }
    public ChannelHandler getDecoder() {
        return decoder;
    }
   
    private class InternalEncoder extends MessageToByteEncoder {
        @Override
        protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
            org.apache.dubbo.remoting.buffer.ChannelBuffer buffer = new NettyBackedChannelBuffer(out);
            Channel ch = ctx.channel();
            NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
            codec.encode(channel, buffer, msg);
        }
    }
    private class InternalDecoder extends ByteToMessageDecoder {
        @Override
        protected void decode(ChannelHandlerContext ctx, ByteBuf input, List<Object> out) throws Exception {
            ChannelBuffer message = new NettyBackedChannelBuffer(input);
            NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
            //解码对象
            codec.decode(channel, message);
            //省略部分代码...
        }
    }
}
总结:
上面的代码中,我们看到,NettyCodecAdapter类适配的是Codec2接口,
通过构造函数传递实现类,然后定义了内部的编码器实现和解码器实现,同时它们都是ChannelHandler。
     这样的话,在内部类里面的编码和解码逻辑,真正调用的还是Codec2接口。

最后我们再来看看,该适配器的调用方式。
 //通过SPI方式获取编解码器的实现类,比如这里是DubboCountCodec
 Codec2 codec = ExtensionLoader.getExtensionLoader(Codec2.class).getExtension("dubbo");
 URL url = new URL("dubbo", "localhost", 22226);
 //创建适配器
 NettyCodecAdapter adapter = new NettyCodecAdapter(codec, url, NettyClient.this);
 //向ChannelPipeline中添加编解码处理器
 ch.pipeline()
    .addLast("decoder", adapter.getDecoder())
    .addLast("encoder", adapter.getEncoder())

  以上,就是Dubbo中关于编解码器对于适配器模式的应用。

 

 

6.第六种:责任链模式  

定义:
  责任链模式为请求创建了一个接收者对象的链。
允许你将请求沿着处理者链进行发送。收到请求后,
每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。

示例:
我们来看一个Netty中的例子。我们知道,在Netty中服务端处理消息,
就要添加一个或多个ChannelHandler。那么,
承载这些ChannelHandler的就是ChannelPipeline,它的实现过程就体现了责任链模式的应用。

ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
    protected void initChannel(NioSocketChannel channel) {
        channel.pipeline()
            .addLast(new ChannelHandler1())
            .addLast(new ChannelHandler2())
            .addLast(new ChannelHandler3());
    }
});

说明:
(1)
需要知道的是,在 Netty 整个框架里面,一条连接对应着一个 Channel,
每一个新创建的 Channel 都将会被分配一个新的 ChannelPipeline。

     (2)ChannelPipeline里面保存的是ChannelHandlerContext对象,

          它是Channel相关的上下文对象,里面包着我们定义的处理器ChannelHandler。


     (3)根据事件的起源,IO事件将会被 ChannelInboundHandler或者ChannelOutboundHandler处理。

         随后,通过调用ChannelHandlerContext 实现,它将被转发给同一超类型的下一个ChannelHandler。


实现过程:
第一步:ChannelHandler
首先,我们来看责任处理器接口,Netty中的ChannelHandler
它充当了所有处理入站和出站数据的应用程序逻辑的容器。

public interface ChannelHandler {
    //当把 ChannelHandler 添加到 ChannelPipeline 中时被调用
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;
    //当从 ChannelPipeline 中移除 ChannelHandler 时被调用
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
    //当处理过程中在 ChannelPipeline 中有错误产生时被调用
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
然后 Netty 定义了下面两个重要的 ChannelHandler 子接口:
(1)ChannelInboundHandler,处理入站数据以及各种状态变化;
public interface ChannelInboundHandler extends ChannelHandler {
    //当 Channel 已经注册到它的 EventLoop 并且能够处理 I/O 时被调用
    void channelRegistered(ChannelHandlerContext ctx) throws Exception;
    //当 Channel 从它的 EventLoop 注销并且无法处理任何 I/O 时被调用
    void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
    //当 Channel 处于活动状态时被调用;Channel 已经连接/绑定并且已经就绪
    void channelActive(ChannelHandlerContext ctx) throws Exception;  
    //当 Channel 离开活动状态并且不再连接它的远程节点时被调用
    void channelInactive(ChannelHandlerContext ctx) throws Exception; 
    //当从 Channel 读取数据时被调用
    void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;  
     //当 Channel上的一个读操作完成时被调用
    void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
    void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
(2)ChannelOutboundHandler,处理出站数据并且允许拦截所有的操作;
public interface ChannelOutboundHandler extends ChannelHandler {
   
    //当请求将 Channel 绑定到本地地址时被调用
     void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
    //当请求将 Channel 连接到远程节点时被调用
    void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,SocketAddress localAddress, ChannelPromise promise) throws Exception;
    //当请求将 Channel 从远程节点断开时被调用
    void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    //当请求关闭 Channel 时被调用
    void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
     //当请求将 Channel 从它的 EventLoop 注销时被调用
    void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    //当请求从 Channel 读取更多的数据时被调用
    void read(ChannelHandlerContext ctx) throws Exception;
    //当请求通过 Channel 将数据写到远程节点时被调用
     void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
    //当请求通过 Channel 将入队数据冲刷到远程节点时被调用
     void flush(ChannelHandlerContext ctx) throws Exception;
}

第二步:ChannelPipeline
既然叫做责任链模式,那就需要有一个“链”,在Netty中就是ChannelPipeline

ChannelPipeline 提供了 ChannelHandler 链的容器,
并定义了用于在该链上传播入站和出站事件流的方法,另外它还具有添加删除责任处理器接口的功能。

public interface ChannelPipeline{
    ChannelPipeline addFirst(String name, ChannelHandler handler);
    ChannelPipeline addLast(String name, ChannelHandler handler);
    ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);
    ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler);
    ChannelPipeline remove(ChannelHandler handler);
    ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler);
    @Override
    ChannelPipeline fireChannelRegistered();
    @Override
    ChannelPipeline fireChannelActive();
    @Override
     ChannelPipeline fireExceptionCaught(Throwable cause);
     @Override
     ChannelPipeline fireUserEventTriggered(Object event);
     @Override
    ChannelPipeline fireChannelRead(Object msg);
     @Override
    ChannelPipeline flush();
     //省略部分方法.....
}
然后我们看它的实现,默认有两个节点,头结点和尾结点。
并在构造函数中,使它们首尾相连。这就是标准的链式结构。
public class DefaultChannelPipeline implements ChannelPipeline {

    final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;
    private final Channel channel;
   
     protected DefaultChannelPipeline(Channel channel) {
         this.channel = ObjectUtil.checkNotNull(channel, "channel");
        tail = new TailContext(this);
        head = new HeadContext(this);
         head.next = tail;
        tail.prev = head;
     }
}
当有新的ChannelHandler被添加时,则将其封装为ChannelHandlerContext对象,然后插入到链表中。
private void addLast0(AbstractChannelHandlerContext newCtx) {
     AbstractChannelHandlerContext prev = tail.prev;
     newCtx.prev = prev;
     newCtx.next = tail;
     prev.next = newCtx;
    tail.prev = newCtx;
}

第三步:ChannelHandlerContext
ChannelHandlerContext 代表了 ChannelHandler 和 ChannelPipeline 之间的关联,
每当有 ChannelHandler 添加到 ChannelPipeline 中时,都会创建 ChannelHandlerContext

ChannelHandlerContext 的主要功能是管理它所关联的 ChannelHandler 和
在同一个 ChannelPipeline 中的其他 ChannelHandler 之间的交互。

public interface ChannelHandlerContext{
    Channel channel();
    EventExecutor executor();
    ChannelHandler handler();
    ChannelPipeline pipeline();
    @Override
    ChannelHandlerContext fireChannelRegistered();
    @Override
    ChannelHandlerContext fireChannelUnregistered();
    @Override
    ChannelHandlerContext fireChannelActive();
    @Override
    ChannelHandlerContext fireChannelRead(Object msg);
    @Override
    ChannelHandlerContext read();
    @Override
    ChannelHandlerContext flush();
     //省略部分方法...
}
说明:(1)ChannelHandlerContext负责在链上传播责任处理器接口的事件。
(2)它有两个重要的方法,查找Inbound类型和Outbound类型的处理器。
(3)值得注意的是,如果一个入站事件被触发,
它将被从ChannelPipeline的头部开始一直被传播到ChannelPipeline的尾端;
一个出站事件将从ChannelPipeline的最右边开始,然后向左传播。


abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
   
     volatile AbstractChannelHandlerContext next;
     volatile AbstractChannelHandlerContext prev;
   
     //查找下一个Inbound类型的处理器,左 > 右
    private AbstractChannelHandlerContext findContextInbound(int mask) {
        AbstractChannelHandlerContext ctx = this;
        EventExecutor currentExecutor = executor();
        do {
            ctx = ctx.next;
        } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
        return ctx;
    }
     //查找下一个Outbound类型的处理器,右 > 左
     private AbstractChannelHandlerContext findContextOutbound(int mask) {
        AbstractChannelHandlerContext ctx = this;
        EventExecutor currentExecutor = executor();
         do {
             ctx = ctx.prev;
         } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_OUTBOUND));
        return ctx;
    }
}
ps:个人觉得是链表式结构,和队列同步器类似

第四步:处理流程
当我们向服务端发送消息的时候,将会触发read方法。
public abstract class AbstractNioByteChannel extends AbstractNioChannel {
    public final void read() { 
        //从Channel中获取对应的ChannelPipeline
         final ChannelPipeline pipeline = pipeline();
        //数据载体
        ByteBuf byteBuf = allocHandle.allocate(allocator);
        //传递数据
        pipeline.fireChannelRead(byteBuf);
    }
}
上面的代码中,就会调用到ChannelPipeline,它会从Head节点开始,根据上下文对象依次调用处理器。
public class DefaultChannelPipeline implements ChannelPipeline {
    public final ChannelPipeline fireChannelRead(Object msg) {
         AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }
}
因为第一个节点是默认的头结点HeadContext,所以它是从ChannelHandlerContext开始的。
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
    //找到下一个ChannelHandler并执行
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
        return this;
    }
   
}
然后在我们自定义的ChannelHandler中,就会被调用到。
public class ChannelHandler1 extends ChannelInboundHandlerAdapter {
    public void channelRead(ChannelHandlerContext ctx, Object msg){
         System.out.println("ChannelHandler1:"+msg);
        ctx.fireChannelRead(msg);
    }
}

     如果消息有多个ChannelHandler,你可以自由选择是否继续往下传递请求。


     比如,如果你认为消息已经被处理且不应该继续往下调用,把上面的ctx.fireChannelRead(msg);注释掉就终止了整个责任链。

 

 

 

 

学习来源:https://juejin.im/post/6859160350692999181

posted @ 2020-09-30 12:32  小窝蜗  阅读(512)  评论(0编辑  收藏  举报