从头开始实现一个小型spring框架——手写Spring之集成Tomcat服务器

写在前面

最近学习了一下spring的相关内容,所以也就想要照猫画虎地记录和实现一下spring的框架,通过阅读这些也希望能够消除对Spring框架的恐惧,其实细心阅读框架也很容易理解。

这是手写Spring系列的第二篇文章,本篇文章将会对web容器对http请求的处理流程进行简答的介绍,然后具体介绍在mini-spring框架中集成tomcat容器的步骤,一步步完善我们的mini-spring框架。
在阅读这篇文章之前,希望你已经看过了上一篇我们对starter启动器的实现,然后继续集成tomcat到我们的mini-spring中。

项目的源码我放在了github上:源码地址

我会在这里整理文章的系列目录:

  1. 从头开始实现一个小型spring框架——手写Spring之实现SpringBoot启动
  2. 从头开始实现一个小型spring框架——手写Spring之集成Tomcat服务器
  3. 从头开始实现一个小型spring框架——控制器controller的实现
  4. 从头开始实现一个小型spring框架——实现Bean管理(IOC与DI)

一、Web服务模型及servlet

在正式实现之前我们需要了解tomcat服务器具体都做了哪些事情,了解web容器对数据的处理流程,才能够更好地吃透我们的min-spring框架。

1.1 Web服务器

  • 监听一个TCP端口,如tomcat默认监听的8080端口,浏览器默认的80端口。
    根据操作系统和计算机网络的相关知识,在传输层实现端到端的通信需要通过端口,每个端口又被应着的应用程序监听着,当有消息通过网络一层层寻址,找到相应ip下相应的端口,便把数据放到相应的端口,进而由应用程序解析相应的内容。
  • 转发请求,回复相应
  • 本身没有业务逻辑,仅负责连接操作系统和应用程序代码

1.2 请求流程

客户端从浏览器发出请求,通过网络将数据bit流传输至Web服务器(细节方面涉及计算机网络的分层模型和数据传输过程,这里不过多介绍),其中还包含了数据请求的端口以及定义的各种协议信息,网卡拿到网络中传来的bit流,将数据转换为字节流,并交给相应的端口处理(本篇采用的是8899端口)。而我们的web容器如tomcat,就在监听着这些端口,并将传来的请求移交给相应的代码处理,随时处理网络中传来的数据。但具体处理方式对于web容器来说是一个黑盒容器并不关系具体的处理过程是怎样的,他只负责接收请求和发回相应两件事情。

二、实现

要在mini-spring项目中集成我们的tomcat,首先需要在gradle中添加相应的依赖。

framework的build.gradle中的dependences添加tomcat的embed版本的相关依赖(因为只是简单实现,所以选择这个版本)
包结构(web包下增加server包和servlet包)

我们需要在framework的依赖中添加这么一段:


    // https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core
    compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.23'
    

整体的代码

plugins {
    id 'java'
}

group 'com.qcby'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    // https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core
    compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.23'
}

在web包下新建一个server包用于保存容器相关的代码

新建TomcatServer类用于实现我们的Tomcat

package com.qcby.web.server;

import com.qcby.web.servlet.TestServlet;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;

/**
 * @author kevinlyz
 * @ClassName TomcatServer
 * @Description 集成Tomcat服务器
 * @Date 2019-06-05 13:10
 **/
public class TomcatServer {
    private Tomcat tomcat;
    private String[] agrs;

    public TomcatServer(String[] agrs) {
        this.agrs = agrs;
    }

    public void startServer() throws LifecycleException {
        //实例化tomcat
        tomcat = new Tomcat();
        tomcat.setPort(8899);
        tomcat.start();
        //实例化context容器
        Context context = new StandardContext();
        context.setPath("");
        context.addLifecycleListener(new Tomcat.FixContextListener());
        TestServlet testServlet = new TestServlet();
        Tomcat.addServlet(context,"testServlet",testServlet).setAsyncSupported(true);
        
        //添加URL映射
        context.addServletMappingDecoded("/test.json","testServlet");
        tomcat.getHost().addChild(context);

        //设置守护线程防止tomcat中途退出
        Thread awaitThread = new Thread("tomcat_await_thread."){
            @Override
            public void run() {
                TomcatServer.this.tomcat.getServer().await();
            }
        };
        //设置为非守护线程
        awaitThread.setDaemon(false);
        awaitThread.start();
    }
}

servlet包下新建TestServlet用于响应请求

package com.qcby.web.servlet;

import javax.servlet.*;
import java.io.IOException;

/**
 * @author kevinlyz
 * @ClassName TestServlet
 * @Description 
 * @Date 2019-06-05 13:28
 **/
public class TestServlet implements Servlet {
    @Override
    public void init(ServletConfig config) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        res.getWriter().write("test");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

最后在MiniApplication中实例化我们的Tomcat容器

package com.qcby.starter;

import com.qcby.web.server.TomcatServer;
import org.apache.catalina.LifecycleException;

/**
 * @author kevinlyz
 * @ClassName MiniApplication
 * @Description 框架的入口类
 * @Date 2019-06-04 19:21
 **/
public class MiniApplication {
    public static void run(Class<?> cls,String[] args){
        System.out.println("Hello mini-spring application!");
        TomcatServer tomcatServer = new TomcatServer(args);
        try {
            tomcatServer.startServer();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
    }
}

然后gradle build

输入命令:java -jar test/build/libs/test-1.0-SNAPSHOT.jar
看到控制台打印8899端口的启动

打开浏览器

看到response返回的test字符串!

喜大普奔,大功告成!

三、小结

这是手写Spring系列的第二篇文章,本篇文章在第一篇实现SpringBoot启动的基础上进一步将Tomcat容器集成在了我们的mini-spring框架中。首先对容器处理请求的逻辑进行了简单介绍,分析了tomcat容器对请求的处理和响应,对于内部数据究竟是怎样处理的,web容器并不关心。然后对tomcat的集成做了具体实现,我们新添加了TomcatServer和TestServlet类分别实例化我们的容器和测试我们的Servlet请求拦截,当TestServlet正确匹配到我们请求的/test.json,并成功返回一个test字符串,说明我们的Servelt是有效的。

P.S. 在TomcatServer实例化容器的时候,我们设置了一个守护线程,以防止容器的中途退出

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值