2-1-2(上) 手写Tomcat

一、 手写实现迷你版Tomcat思路分析

在这里插入图片描述
在这里插入图片描述

二、MyTomcat 1.0 版本

2.1 手写实现迷你版Tomcat V1.0开发
  • 创建一个maven工程
    在这里插入图片描述
  • maven 加上一个编译插件
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>utf-8</encoding>
                </configuration>

            </plugin>
        </plugins>
    </build>


  • 1.0 的代码
package com.lagou.server;

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * My tomcat 1.0
 */

public class Bootstrap {

    // 配置的端口号
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }


    /**
     *  MyTomcat 启动需要初始化展开的一些操作
     */
    public void start() throws IOException {

        /**
         * 完成MyTomcat 1.0 版本
         * 需求:浏览器请求 http:// localhost:8080 ,返回一个固定的字符串到页面 "Hello MyTomcat!!"
         */
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("====>>> My Tomcat" + port);

        while (true){
            Socket socket = serverSocket.accept();

            // 有了socket,接收到请求,获取输入流
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("Hello MyTomcat!!".getBytes());

            socket.close();
        }

    }


    /**
     * My Tomcat 的程序入口
     */
    public static void main(String[] args) {


    }


}


2.2 测试遇到的问题分析
  • 测试程序

    /**
     * My Tomcat 的程序入口
     */
    public static void main(String[] args) {

        Bootstrap bootstrap = new Bootstrap();

        try {
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
  • 测试结果

在这里插入图片描述

  • 分析
    在这里插入图片描述
    response应该 加上一个 类似上面的信息
2.3 手写实现迷你版Tomcat V1.0开发完成

/**
 * http 协议工具类,主要是提供 响应头对应的信息,这里我们只提供 200 和 404 的情况
 */
public class HttpProtocolUtil {

 private static String str404 = "<h1>404 not found</h1>";
    /**
     * 设置 请求为 200 的 head信息
     * @param contentLength
     * @return
     */
    public static String getHttpHead200(Long contentLength){

        return "HTTP/1.1 200 OK \n"+
                "Content-Type:text/html \n"+
                "Content-Length"+contentLength+"\n"+
                "\r\n";

    }

    /**
     * 设置 请求为 404 的 head信息

     * @return
     */
    public static String getHttpHead404(){

        return "HTTP/1.1 404 NOT Found \n"+
                "Content-Type:text/html \n"+
                "Content-Length"+str404.length()+"\n"+
                "\r\n" + str404;
    }


}


    /**
     *  MyTomcat 启动需要初始化展开的一些操作
     */
    public void start() throws IOException {

        /**
         * 完成MyTomcat 1.0 版本
         * 需求:浏览器请求 http:// localhost:8080 ,返回一个固定的字符串到页面 "Hello MyTomcat!!"
         */
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("====>>> My Tomcat" + port);

        while (true){
            Socket socket = serverSocket.accept();

            // 有了socket,接收到请求,获取输入流
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello MyTomcat!!";
            outputStream.write((HttpProtocolUtil.getHttpHead200(data.length()) + data).getBytes()) ;

            socket.close();
        }

    }

三、My Tomcat 2.0

3.1 流程分析

在这里插入图片描述

2.2、 手写实现迷你版Tomcat V2.0 Request封装

import java.io.IOException;
import java.io.InputStream;

/**
 * 把请求信息封装成Request对象(根据InputStream输入流封装)
 */
public class Request {

    private String method;   //请求方式,比如 GET/POST
    private String url;     // 例如

    private InputStream inputStream;


    public Request(InputStream inputStream) throws IOException {
        // 将传入的流 转化成字符串
        this.inputStream = inputStream;
        int count= 0 ;
        while (count== 0){
            count = inputStream.available();
        }

        byte[] bytes = new byte[count];
        inputStream.read(bytes);
        String inputStr = new String(bytes);

// 解析字符串
        // 解析第一行请求头信息
        String firstHeadStr = inputStr.split("\\n")[0];

        String[] strings = firstHeadStr.split(" ");

        this.method = strings[0];
        this.url = strings[1];

        System.out.println("====>>>method :" +this.method);
        System.out.println("====>>>url :" +this.url);


    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }
}


2.3、手写实现迷你版Tomcat V2.0封装Response

import com.lagou.utils.HttpProtocolUtil;
import com.lagou.utils.StaticResourceUtil;

import java.io.*;

/**
 * 封装Response 对象 ,需要依赖于OutputStream
 * 该对象 需要提供 核心方法 输出html
 */
public class Response {

    private OutputStream outputStream;

    public Response(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public OutputStream getOutputStream() {
        return outputStream;
    }

    public void setOutputStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    /**
     *
     *
     * @param path
     */
    public void outputHtml(String path) throws IOException {
        // 获取静态资源文件的绝对路径
        String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);

        File file = new File(absoluteResourcePath);
        if(file.exists()){

            StaticResourceUtil.outputStaticResource(new FileInputStream(file),outputStream);
        }else {
            // 输出 404
            output(HttpProtocolUtil.getHttpHead404());
        }

    }

    private void output(String content) throws IOException {
        outputStream.write(content.getBytes());
    }


}

2.4、手写实现迷你版Tomcat V2.0封装静态资源工具类

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class StaticResourceUtil {

    /**
     * 获取资源文件的绝对路径
     */
    public static String getAbsolutePath(String path){

        String absolutePath = StaticResourceUtil.class.getResource("/").getPath();

        return absolutePath.replaceAll("\\\\","/")+path;
    }


    /**
     * 读取静态资源文件输入流,通过输出流输出
     * @param inputStream
     * @param outputStream
     */
    public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {

        int count = 0;
        while (count == 0){
            count = inputStream.available();
        }

        int resourceSize = count;

        // 第一步 :输出http 请求头,
        outputStream.write(HttpProtocolUtil.getHttpHead200(resourceSize).getBytes());

        // 第二步 :读取输出具体内容
        long written = 0 ; // 已经读取的内容长度
        int byteSize = 1024;  // 计划每次缓冲的长度
        byte[] bytes = new byte[byteSize];

        while (written < resourceSize){
            if(written+ byteSize > resourceSize){    // 判断是否剩余未读取的长度大小不足一个1024 长度,那就按真是长度处理
               byteSize = (int) (resourceSize - written);  // 剩余的文件内容长度
                bytes = new byte[byteSize];
            }

            inputStream.read(bytes);
            outputStream.write(bytes);

            outputStream.flush();
            written += byteSize;
        }
    }


}

2.5、 手写实现迷你版Tomcat V2.0测试
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Hello Minicat-static resource!!!

</body>
</html>
import com.lagou.pojo.Request;
import com.lagou.pojo.Response;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Bootstrap {

    private Integer port = 8081;

    /**
     * 2.0 tomcat 的版本
     * @throws IOException
     */
    public void start() throws IOException {

        ServerSocket serverSocket = new ServerSocket(port);

        while (true){
            Socket socket = serverSocket.accept();
            // 通过输入流 获取请求信息
            InputStream inputStream = socket.getInputStream();

            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            response.outputHtml(request.getUrl());

            socket.close();
        }

    }


    public static void main(String[] args) {

        Bootstrap bootstrap = new Bootstrap();

        try {
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

在这里插入图片描述

8080 的解析
在这里插入图片描述

在这里插入图片描述

三 手写实现迷你版Tomcat V3.0定义Serlvet

3.1、 Serlvet

import com.lagou.pojo.Request;
import com.lagou.pojo.Response;

public interface Servlet {

    void init() throws Exception;

    void destory() throws Exception;

    void service(Request request, Response response) throws Exception;

}


import com.lagou.pojo.Request;
import com.lagou.pojo.Response;

public abstract class HttpServlet implements Servlet {

    public abstract void doGet(Request request, Response response) throws Exception;

    public abstract void doPost(Request request, Response response) throws Exception;

    @Override
    public void service(Request request, Response response) throws Exception {

        if("GET".equalsIgnoreCase(request.getMethod())){
            doGet(request,response);
        }else {
            doPost(request,response);
        }
    }

}



import com.lagou.pojo.Request;
import com.lagou.pojo.Response;
import com.lagou.utils.HttpProtocolUtil;

public class LagouServlet extends HttpServlet {
    @Override
    public void doGet(Request request, Response response) throws Exception {

        String content ="<h1> LagouServlet get</h1>" ;
        try {
            response.output(HttpProtocolUtil.getHttpHead200(content.length())+content);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(Request request, Response response) throws Exception {
        String content ="<h1> LagouServlet post</h1>" ;
        try {
            response.output(HttpProtocolUtil.getHttpHead200(content.length())+content);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void init() throws Exception {
        System.out.println("init方法");
    }

    @Override
    public void destory() throws Exception {
        System.out.println("destory方法");
    }
}


3.2 配置

web.xml

<?xml version="1.0" encoding="UTF-8" ?>
<web-app>
    <servlet>
        <servlet-name>lagou</servlet-name>
        <servlet-class>com.lagou.servlet.LagouServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>lagou</servlet-name>
        <url-pattern>/lagou</url-pattern>
    </servlet-mapping>

</web-app>

3.3 解析


import com.lagou.pojo.Request;
import com.lagou.pojo.Response;
import com.lagou.servlet.HttpServlet;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BootStrap {

    private int port = 8082;

    private Map<String, HttpServlet> servletMap = new HashMap<String,HttpServlet>();   // servlet 容器

    public void loadServlet(){
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");

        SAXReader saxReader = new SAXReader();

        try{
            Document document = saxReader.read(resourceAsStream);

            Element rootElement = document.getRootElement();

            List<Element> servlets = rootElement.selectNodes("//servlet");

            for (int i = 0; i < servlets.size(); i++) {

                Element element = servlets.get(i);

                // <servlet-name>lagou</servlet-name>
                Element servletNameEle = (Element)element.selectSingleNode("servlet-name");
                String servletName= servletNameEle.getTextTrim();

                //    <servlet-class>com.lagou.server.lagouServlet</servlet-class>
                Element servletClassEle = (Element)element.selectSingleNode("servlet-class");
                String servletClass= servletClassEle.getTextTrim();


                // 根据 servletName 找到 url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" +servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                servletMap.put(urlPattern,(HttpServlet) Class.forName(servletClass).newInstance());

            }


        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * My Tomcat 3.0
     * @throws IOException
     */
    public void start() throws Exception {

        loadServlet();

        ServerSocket serverSocket = new ServerSocket(port);

        while (true){

            Socket socket = serverSocket.accept();
            Request request = new Request(socket.getInputStream());
            Response response = new Response(socket.getOutputStream());

            // 静态资源
            if(servletMap.get(request.getUrl()) == null){
                response.outHtml(request.getUrl());
            }else {
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request,response);
            }
            socket.close();
        }


    }

    public static void main(String[] args) {

        BootStrap bootStrap = new BootStrap();

        try {
            bootStrap.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}


3.4、手写实现迷你版Tomcat V3.0开发完成及测试
   /**
     * My Tomcat 3.0
     * @throws IOException
     */
    public void start() throws Exception {

        loadServlet();

        ServerSocket serverSocket = new ServerSocket(port);

        while (true){

            Socket socket = serverSocket.accept();
            Request request = new Request(socket.getInputStream());
            Response response = new Response(socket.getOutputStream());

            // 静态资源
            if(servletMap.get(request.getUrl()) == null){
                response.outHtml(request.getUrl());
            }else {
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request,response);
            }
            socket.close();
        }


    }

    public static void main(String[] args) {
        BootStrap bootStrap = new BootStrap();
        try {
            bootStrap.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


在这里插入图片描述

四、My Tomcat 多线程的实现

4.1、 手写实现迷你版Tomcat多线程改造(不使用线程池)


import com.lagou.pojo.Request;
import com.lagou.pojo.Response;
import com.lagou.servlet.HttpServlet;

import java.net.Socket;
import java.util.Map;

public class RequestProcessor1 extends Thread {

    private Socket socket;

    private Map<String, HttpServlet> servletMap ;

    public  RequestProcessor1(Socket socket,Map<String, HttpServlet> servletMap){
        this.socket = socket;
        this.servletMap = servletMap;
    }

    @Override
    public void run() {

        try{

            Request request = new Request(socket.getInputStream());

            Response response = new Response(socket.getOutputStream());

            // 静态资源
            if(servletMap.get(request.getUrl()) == null){
                Thread.sleep(100000);
                response.outputHtml(request.getUrl());
            }else {
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request,response);
            }

            socket.close();

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

    public void start() throws Exception {

        ServerSocket serverSocket = new ServerSocket(port);

        loadServlet();

        while (true){
            Socket socket = serverSocket.accept();

            RequestProcessor1 requestProcessor1 = new RequestProcessor1(socket, servletMap);
            requestProcessor1.start();

        }


    }

在这里插入图片描述

4.2、手写实现迷你版Tomcat多线程改造(使用线程池)

    public void start() throws Exception {

        ServerSocket serverSocket = new ServerSocket(port);
        loadServlet();

//        int corePoolSize,
//        int maximumPoolSize,
//        long keepAliveTime,
//        TimeUnit unit,
//        BlockingQueue<Runnable> workQueue,
//                ThreadFactory
//        threadFactory,
//                RejectedExecutionHandler handler

        int corePoolSize = 10;
        int maximumPoolSize = 50 ;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);


        while (true){
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);

            // 多线程 1 ===>不使用 线程池
//            requestProcessor1.start();

            // 多线程 2 ===>使用 线程池
            threadPoolExecutor.execute(requestProcessor);
        }

    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值