栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

netty的反射工厂模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

netty的反射工厂模式

在看 netty 的反射工厂之前,我们先来看一下实际业务项目中,工厂模式是如何使用的。类图交互关系如下图:

实际业务开发可能会用到 Spring 框架,Spring提供了两个特别的接口:ApplicationContextAware 和 InitializingBean。

public interface ApplicationContextAware extends Aware {
    void setApplicationContext(ApplicationContext var1) throws BeansException;
}

BeanFactory 是根接口,也就是我们熟知的管理Bean的工厂,ListableBeanFactory 进一步继承了 BeanFactory,提供了如下一个方法:

     Map getBeansOfType(Class var1) throws BeansException;

该方法可以根据类型获取对应的Bean实例。

而 ApplicationContext 继承的众多接口之一,就是继承了 ListableBeanFactory。ApplicationContext 也称作是 Spring 上下文。这样来看,文章开头的代码就是将 Spring 上下文注入,也就是注入到我们的 TaskManagerFactory 。


另一个 Spring 接口 InitializingBean,只要实现了该接口的 Bean(我们的 TaskFactory 就通过注解配置成了一个 Bean,比如 @Component),就会在属性初始化之后调用

void afterPropertiesSet() throws Exception;

所以经过上述两个接口的方法设置,我们就成功的在 TaskManagerFactory 初始化的时候将实现了 TaskManager 接口的两个对象:TaskManagerImplA和 TaskManagerImplB 添加到了 TaskManagerFactory 的 Map 中。

使用者是需要注入 TaskManagerFactory ,并调用 getTaskManger 方法即可。如果要新增一个 任务处理器,只需要在枚举类中添加数值,然后新建一个 TaskManager 的实现即可。


我们常见的例子是工厂类的get方法中,会通过 if else 判断要获取哪个对象,然后实力化后返回。本质上和上面的案例差不多。

那么 netty 上面是如何运用工厂模式,有什么特别的呢?

反射,没错就是用反射的方法来解耦。反射损耗性能x x x,不过不能脱离环境,我们来看经典的 echo server 代码。

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final EchoServerHandler serverHandler = new EchoServerHandler();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
              // 方法内部创建反射工厂
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     if (sslCtx != null) {
                         p.addLast(sslCtx.newHandler(ch.alloc()));
                     }
                     p.addLast(serverHandler);
                 }
             });

            // 方法内部使用反射工厂创建 ServerSocketChannel 对象
            ChannelFuture f = b.bind(PORT).sync();

其中,.channel(NioServerSocketChannel.class) 方法内部实现为:

// 这里的 C 就是 Channel 接口   
public B channel(Class channelClass) {
        return channelFactory(new ReflectiveChannelFactory(
                ObjectUtil.checkNotNull(channelClass, "channelClass")
        ));
}

执行完之后,就是将这个实力化的反射工厂对象 ReflectiveChannelFactory 作为成员变量设置到了 ServerBootstrap中。

而 NioServerSocketChannel 就是 Channel 接口的一种实现类,另外的实现类有 EpollServerSocketChannel 、OioServerSocketChannel(@Deprecated) 等。

在 new ReflectiveChannelFactory 时,我们看下内部实现:

public class ReflectiveChannelFactory implements ChannelFactory {
    private final Constructor constructor;

    public ReflectiveChannelFactory(Class clazz) {
        ObjectUtil.checkNotNull(clazz, "clazz");
        try {
            this.constructor = clazz.getConstructor();
        } 
      ...
    }
}

可见,创建反射工厂时,就已经将 NioServerSocketChannel 的构造器设置进去了。


那么当程序执行 b.bind(PORT).sync(); 这条语句时,我们看下 bind 内部是如何利用之前设置的构造器的?

// bind内部调用的一个方法    
final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
          // 这一步调用方法
            channel = channelFactory.newChannel();
            init(channel);
        }
}

channelFactory.newChannel(); 这句的 channelFactory 就是之前初始化进去的。然后 newChannel 方法如下:

    public T newChannel() {
        try {
            return constructor.newInstance();
        }
    }

可见,直接利用了上面注入的 constructor,也就是 NioServerSocketChannel 类的构造器来创建具体对象的。


这样的好处是,当我们想要新增一个新的channel类型时,比如新增 KQueueServerSocketChannel ,只需要开发这个新的channel实现,然后在 echoServer 里面修改一条语句(这里先忽略上面的 EventLoopGroup 的修改):

//将
.channel(NioServerSocketChannel.class)
//修改为:
.channel(KQueueServerSocketChannel.class)  

满足开闭原则。


netty 追求高性能,而反射可能会降低性能,二者矛盾吗?不矛盾,这里的 ServerSocketChannel 相当于是服务器初始化过程中监听的channel,而不是每个连接接入时建立的 channel。对性能没有影响,且应用和维护起来简单。

既然如此,反射工厂模式和文章开头的案例能互换吗?可以,但是看从什么角度考量。


最后,再简单回顾下抽象工厂模式。抽象工厂相当于建立对工厂的抽象,客户端通过实力化不同的工厂,来创建不同种类的对象。如果理解了工厂模式,就可以把抽象工厂当作是工厂模式之上的对工厂的又一层封装。也就不必记忆产品簇产品种类什么的。

感兴趣的可以看看 netty 的源码,简单追踪下即可理解反射工厂的应用,从而对这类设计模式理解更加深刻。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/489472.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号