zl程序教程

您现在的位置是:首页 >  工具

当前栏目

【手写Tomcat】9.实现游览器访问我们自定义的MyServlet

Tomcat 实现 自定义 访问 我们 手写
2023-09-14 09:14:18 时间

        在上一篇文章中,我们已经可以通过程序来对web.xml中的配置项进行读取了,现在,我们通过我们自己定义的MyHttpRequest得到访问的URI,然后通过map里面的映射关系,实现游览器对Servlet的访问。

思路分析

        我们创建一个MyServletThread类,这个类实现了Runnable接口,这个类里面有一个Socket,这个socket在类创建时必须传入一个Socket,我们通过这个socket就可以得到InputStream和OutputStream,通过这2个流我们就能够创建MyHttpRequest对象和MyHttpResponse对象,其中MyHttpRequest对象可以获取到访问的URI,通过URI和我们在上一篇文章中的map就能够实现对MyServlet的定位。

        我们的思路差不多就是这个样子,废话就不说了,直接开始撸代码。


代码实现

       首先创建类,实现Runnable接口。

public class MyServletThread implements Runnable{
}

        定义Socket成员属性,并在构造器中对Socket进行赋值

    //定义一个socket对象
    private Socket socket = null;

    //在初始化对象时传入socket
    public MyServletThread(Socket socket) {
        this.socket = socket;
    }

         实现run方法

    @Override
    public void run() { 
    }

         我们具体的逻辑就在run方法中进行编写,按照上面的图,我们首先获取到InputStream和OutputStream,并且通过这2个流创建MyHttpRequest和MyHttpResponse

    //获取输入流,并且通过输入流创建MyHttpRequest对象
    MyHttpRequest request = new MyHttpRequestImpl(socket.getInputStream());
    //获取输出流,并且通过输出流创建MyHttpResponse对象
    MyHttpResponse response = new MyHttpResponseImpl(socket.getOutputStream());

        我们通过MyHttpRequest得到访问的URI

    //得到访问的URI
    String uri = request.getUri() == null ? "": request.getUri();

         下面,我们就通过URI来进行一系列的判断,判断游览器访问的MyServlet是否存在,是不是第一次访问MyServlet等

    //判断是否已经创建过MyServlet实例
    if(MyServletHandler.PATTERN_CLASS.containsKey(uri)){
        //如果已经创建过了,直接调用该MyServlet的service方法
        MyServlet myServlet = MyServletHandler.PATTERN_CLASS.get(uri);
        myServlet.service(request,response);
    }else if (MyServletHandler.PATTERN_NAME.containsKey(uri)){
        //如果是第一次访问,我们就创建MyServlet实例,并且将该实例存入map中
        String name = MyServletHandler.PATTERN_NAME.get(uri);
        String classPath = MyServletHandler.NAME_CLASS.get(name);
        MyServlet myServlet = (MyServlet) Class.forName(classPath).newInstance();
        MyServletHandler.PATTERN_CLASS.put(uri,myServlet);
        //第一次访问是调用init方法和service方法
        myServlet.init();
        myServlet.service(request,response);
    }else {
        //如果MyServlet不存在,我们直接返回404 NOT FOUND
        response.setStatus(404);
        response.write("404 NOT FOUND");
    }

        完整代码如下

import com.clucky.myTomcat.MyServletHandler;
import com.clucky.myTomcat.myHttp.MyHttpRequest;
import com.clucky.myTomcat.myHttp.MyHttpRequestImpl;
import com.clucky.myTomcat.myHttp.MyHttpResponse;
import com.clucky.myTomcat.myHttp.MyHttpResponseImpl;
import com.clucky.myTomcat.myServlet.MyServlet;

import java.net.Socket;

public class MyServletThread implements Runnable{

    //定义一个socket对象
    private Socket socket = null;

    //在初始化对象时传入socket
    public MyServletThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            //获取输入流,并且通过输入流创建MyHttpRequest对象
            MyHttpRequest request = new MyHttpRequestImpl(socket.getInputStream());
            //获取输出流,并且通过输出流创建MyHttpResponse对象
            MyHttpResponse response = new MyHttpResponseImpl(socket.getOutputStream());
            //得到访问的URI
            String uri = request.getUri() == null ? "": request.getUri();
            //判断是否已经创建过MyServlet实例
            if(MyServletHandler.PATTERN_CLASS.containsKey(uri)){
                //如果已经创建过了,直接调用该MyServlet的service方法
                MyServlet myServlet = MyServletHandler.PATTERN_CLASS.get(uri);
                myServlet.service(request,response);
            }else if (MyServletHandler.PATTERN_NAME.containsKey(uri)){
                //如果是第一次访问,我们就创建MyServlet实例,并且将该实例存入map中
                String name = MyServletHandler.PATTERN_NAME.get(uri);
                String classPath = MyServletHandler.NAME_CLASS.get(name);
                MyServlet myServlet = (MyServlet) Class.forName(classPath).newInstance();
                MyServletHandler.PATTERN_CLASS.put(uri,myServlet);
                //第一次访问是调用init方法和service方法
                myServlet.init();
                myServlet.service(request,response);
            }else {
                //如果MyServlet不存在,我们直接返回404 NOT FOUND
                response.setStatus(404);
                response.write("404 NOT FOUND");
            }
            //关闭流
            socket.close();
        } catch (Exception e) {
            System.out.println("创建servlet发生异常...");
        }
    }
}

        现在,我们的每一个Servlet都编写好了,我们来创建我们的第3个版本的tomcat。创建一个类,这个类的名字就是MyTomcat03,这个类就是我们的主类,对8080端口进行监听,并负责创建MyServletThread线程对象,开启线程。

        代码很简单,下面就直接给出了。

import com.clucky.myTomcat.thread.MyServletThread;

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

public class MyTomcat03{

    public void startUp(){
        MyServletHandler.init();
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            System.out.println("服务器在8080端口监听");
            while (!serverSocket.isClosed()){
                Socket socket = serverSocket.accept();
                System.out.println("创建了一个线程");
                MyServletThread myServletHandler = new MyServletThread(socket);
                new Thread(myServletHandler).start();
            }
        } catch (IOException e) {
            System.out.println("启动服务器异常");
        }
    }

    public static void main(String[] args) {
        new MyTomcat03().startUp();
    }
}

运行MyTomcat进行测试 

        我们启动MyTomcat03这个类,游览器访问http://localhost:8080/dog ,显示如下


         我们再访问http://localhost:8080/cat,显示如下


        我们可能忘记了MyDogServlet和MyCatServlet的内容,这2个MyServlet的内容如下

        我们发现游览器显示的和我们再doGet中写的一样,说明我们就已经实现了游览器访问MyServlet。


        经过漫长的版本迭代和大量的代码,我们终于实现了游览器访问我们自己定义的MyServlet,我们成功啦!!!

        现在我们的tomcat已经可以访问MyServlet了,但是还缺少一个重要功能,就是游览器访问我们的静态资源,我将在下一篇文章中实现这个功能,下一篇文章也是【手写Tomcat】的最后一篇文章,相信大家在看完后也一定对Tomcat的运行机制有了更加深入的理解。

        下一篇文章中将会实现一个相对比较完善的迷你Tomcat,我也会在下一篇文章中给出所有的源代码,感谢大家的游览!!!