基于NettyServer的SpringMVC的实现

1,666 阅读3分钟

这一篇主要是写基于Netty的服务端实现SpringMVC的过程. 由于工程是基于Maven工程构建,所以第一步就是提前找好需要依赖的GAV坐标,构建一个maven工程,并在pom中加入以下依赖.这里使用了netty和spring的容器作为基础.

       <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
          <dependency>
              <groupId>io.netty</groupId>
              <artifactId>netty-all</artifactId>
              <version>4.1.36.Final</version>
          </dependency>

          <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-core</artifactId>
              <version>5.1.9.RELEASE</version>
          </dependency>

          <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-beans</artifactId>
              <version>5.1.9.RELEASE</version>
          </dependency>

          <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-context</artifactId>
              <version>5.1.9.RELEASE</version>
          </dependency>

第二步: 构建一个Server服务端类,Netty提供了基于服务端的构建,

     public static void main(String[] args)throws Exception {
        //启动Spring的容器
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
        //配置
        annotationConfigApplicationContext.scan("com.netty.mvc");
        annotationConfigApplicationContext.refresh();

        final DispatcherHandler dispatcherHandler = new DispatcherHandler(annotationConfigApplicationContext);

        //创建Even Loop Group
        //配置服务器的NIO线程组
        //两个Reactor 一个用于服务器接收客户端的连接  一个用于经行SocketChannel的网络读写
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            //创建ServerBootStrap
            ServerBootstrap b = new ServerBootstrap();
            //指定所使用的NIO传输Channle
            b.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
                    .localAddress(8080)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) {
                            //如果ServerHandler被注为@Shareable的时候,则可以总是使用同样的实例
                            socketChannel.pipeline()
                                    .addLast(new HttpRequestDecoder())
                                    .addLast(new HttpResponseEncoder())
                                    .addLast(new WebServerHandler(dispatcherHandler));
                        }
                    });
            //异步的绑定服务器,调用sync===方法阻塞,直到绑定完成
            ChannelFuture f = b.bind().sync();
            System.out.println("netty服务端启动成功");
            //获取Channel的CloseFuture,并阻塞当前线程直到它完成
            f.channel().closeFuture().sync();
        }finally {
            //关闭EvenLoopGroup,释放所有资源
            bossGroup.shutdownGracefully().sync();
            workerGroup.shutdownGracefully().sync();
        }
    }

第二步: 构建一个WebServerHandler类来处理i 来处理请求.

@ChannelHandler.Sharable
public class WebServerHandler extends ChannelInboundHandlerAdapter{

    private AsciiString contentType = HttpHeaderValues.TEXT_PLAIN;

   //请求分发器
    DispatcherHandler dispatcherHandler;

    public WebServerHandler(DispatcherHandler dispatcherHandler){
        this.dispatcherHandler = dispatcherHandler;
    }
    /**
     * 每个信息入站都会调用
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        Object result = "";
        if(msg instanceof HttpRequest){
            result  = dispatcherHandler.handle((HttpRequest)msg);
        }
        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                HttpResponseStatus.OK,
                Unpooled.wrappedBuffer(result.toString().getBytes())); // 2

        HttpHeaders heads = response.headers();
        heads.add(HttpHeaderNames.CONTENT_TYPE, contentType + "; charset=UTF-8");
        heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); // 3
        heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        //将接受到的消息写给发送者
        ctx.write(response);
    }


    /**
     * 通知处理器最后的channelread是当前批处理中的最后一条信息调用
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将未决消息冲刷到远程节点,并关闭该Channel
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();  //打印异常栈追踪
        ctx.close(); //关闭该channel
    }
}

第四步: 就是构建一个DispatcherHandler作为请求处理份发器,

@Configuration
public class DispatcherHandler implements WebHandler, ApplicationContextAware {

    //url-->hanlder的映射
    private List<HandlerMapping> handlerMappings;
    //处理器的适配器
    private List<HandlerAdapter> handlerAdapters;

    /**
     * Create a new {@code DispatcherHandler} for the given {@link ApplicationContext}.
     * @param applicationContext the application context to find the handler beans in
     */
    public DispatcherHandler(ApplicationContext applicationContext) {
        initStrategies(applicationContext);
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        initStrategies(applicationContext);
    }

    protected void initStrategies(ApplicationContext context) {

        Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, HandlerMapping.class, true, false);

        ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
        AnnotationAwareOrderComparator.sort(mappings);
        this.handlerMappings = Collections.unmodifiableList(mappings);

        Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, HandlerAdapter.class, true, false);

        this.handlerAdapters = new ArrayList<>(adapterBeans.values());
        AnnotationAwareOrderComparator.sort(this.handlerAdapters);

    }

    @Override
    public Object handle(HttpRequest httpRequest) {
      //请求处理器
        for(HandlerMapping handlerMapping : handlerMappings){
           Object handler = handlerMapping.getWebHandler(httpRequest);
           if(handler != null){
               for(HandlerAdapter adapter: handlerAdapters){
                    if(adapter.support(handler)){
                      return  adapter.handle(httpRequest,handler);
                    }
               }
           }
        }
        return null;
    }

}

第五步: 构建SprintMVC中最核心的HandlerMapping中映射处理器的注册,这里是借助了Spring的ComponentScan组件实现对于自定义@Controller的注解的扫描.

@Configuration
public class DefaultWebHandler implements WebHandler {


    public DefaultWebHandler(ApplicationContext applicationContext) {
        registerHandler(applicationContext);
    }

    /**
     * url -> Method对应
     */
    private Map<String, Method> handlerMap = new LinkedHashMap<>();

    /**
     * method—>controller的对应
     */
    private Map<Method, Object> controllerMap = new HashMap<>();

    private void registerHandler(ApplicationContext context) {

        Map<String, Object> annotationControllerClasses = context.getBeansWithAnnotation(Controller.class);

        Set<Class<?>> handlerTypes = new LinkedHashSet<>();
        Class<?> specificHandlerType = null;

        for (Object targetType : annotationControllerClasses.values()) {
            if (!Proxy.isProxyClass(targetType.getClass())) {
                specificHandlerType = ClassUtils.getUserClass(targetType);
                handlerTypes.add(specificHandlerType);
            }
            final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : targetType.getClass());

            ReflectionUtils.doWithMethods(specificHandlerType, method -> {
                String url = "";
                if (targetClass.isAnnotationPresent(RequestMapping.class)) {
                    url = targetClass.getAnnotation(RequestMapping.class).value();
                }
                Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                if (specificMethod.isAnnotationPresent(RequestMapping.class)) {
                    url += specificMethod.getAnnotation(RequestMapping.class).value();
                }
                handlerMap.put(url, specificMethod);
                controllerMap.put(specificMethod, targetType);
            }, ReflectionUtils.USER_DECLARED_METHODS);
        }
    }

    @Override
    public Object handle(HttpRequest httpRequest) {
        Method method = handlerMap.get(httpRequest.uri());

        if(method != null){
            try {
                return method.invoke(controllerMap.get(method));
            }catch (IllegalAccessException e){
                e.printStackTrace();
            }catch (InvocationTargetException e){
                e.printStackTrace();
            }
        }
        return null;
    }
}

第六步构建测试用例

@Controller
@RequestMapping(value = "/test")
@Configuration
public class TestController {

    @RequestMapping(value = "/get",method = RequestMethod.GET)
    public String test(){
        return "Hi Netty SpringMVC";
    }

}

测试结果

代码也放在github上的这个地址了,欢迎下载阅读

总结
今天主要实现了基于Netty服务端和Spring容器来实现SpringMVC的功能, 虽然还不是很完善 但是基本功能已经实现.希望给读者领悟到实现的思路,后面会继续优化.