深入剖析tomcat之一个简单的servlet容器

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介:

上一篇,我们讲解了如果开发一个简单的Http服务器,这一篇,我们扩展一下,让我们的服务器具备servlet的解析功能。

简单介绍下Servlet接口
如果我们想要自定义一个Servlet,那么我们必须继承Servlet,并且实现下面几个重要的方法


public void init(ServletConfig config) throws ServletException
public void service(ServletRequest request,ServletResponse response) throws ServletException,java.io.IOException
public void destroy()
public ServletConfig getServletConfig()
public String getServletInfo() 

五个方法中,init,destroy,service都是和servlet的生命周期相关的方法。当实例化某个servlet类之后,servlet会调用init进行初始化,当servlet的请求到达之后,就会调用service方法,并将servletRequest和servletResponse对象作为参数传入,前者包含客户端的Http请求的信息,后者包含服务器的响应信息。

这个简单的Servlet容器的流程如下

  • 等待http请求
  • 对应的servletRequest对象和servletResponse对象,
  • 判断请求的类型,如果是请求静态资源,则找到静态资源的文件,返回给客户端
  • 如果是Servlet请求,载入servlet类,调用service()方法,传入servletRequest对象和servletResponse对象

涉及到的主要的类

  • SimpleServletContainerServer
  • Request
  • Response
  • Servlet
  • PrimitiveServlet
  • StaticProcessor
  • ServletProcessor

关于Request和Response的定义在上一篇幅有定义,这里我们稍微扩展了一下,碍于篇幅,不在这里展示。

PrimitiveServlet类,继承自Servlet,Servlet请求的处理类
类定义:


package servletContainer;

import java.io.IOException;

import base.Request;
import base.Response;
import base.ServletConfig;
import interf.Servlet;

public class PrimitiveServlet implements Servlet {

    @Override
    public void init(ServletConfig config) {
        System.out.println("PrimitiveServlet init");
    }

    @Override
    public void service(Request request, Response response) throws IOException {
        response.getOutput().write("Primitive Servlet".getBytes());
    }

    @Override
    public void destroy() {
        
    }

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

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

}

SimpleServletContainerServer 类
功能:程序入口,监听Http请求,并且负责创建Request和Response
类定义


package servletContainer;

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


import base.Request;
import base.Response;
import servletContainer.processor.ServletProcessor;
import servletContainer.processor.StaticProcessor;

public class SimpleServletContainerServer {
    private static final String SHUT_DOWN = "/SHUTDOWN";
    
    private boolean shutdown = false;
    
    private ServletProcessor servletProcessor = new ServletProcessor();
    
    private StaticProcessor staticProcessor = new StaticProcessor();
    
    public static void main(String args[]){
        SimpleServletContainerServer server = new SimpleServletContainerServer();
        server.init();
        server.await();
    }
    
    public void init(){
        servletProcessor.init();
        staticProcessor.init();
    }
    
    public void await(){
        
        ServerSocket serverSocket = null;
        int port = 8080;
        
        try{
            serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
        }
        catch (IOException e){
            e.printStackTrace();
            System.exit(-1);
        }
        
        while(!shutdown){
            Socket socket = null;
            InputStream input = null;
            OutputStream output = null;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
            try{
                
                socket = serverSocket.accept();
                input = socket.getInputStream();
                output = socket.getOutputStream();
                
                Request request = new Request(input);
                request.parse();
                
                Response response = new Response(output);
                response.setRequest(request);
                
                if(request.getUri().startsWith("/servlet/")){
                    servletProcessor.process(request, response);
                }
                else{
                    staticProcessor.process(request, response);
                }
                
                socket.close();
                shutdown = request.getUri().equals(SHUT_DOWN);
                
            }
            catch (Exception e){
                e.printStackTrace();
                System.exit(1);
            }
        }
        
    }
    
}

我们引入了StaticProcessor和ServletProcessor进行逻辑的处理,我们看下这两个类的定义
首先这两个类都继承自IProcessor接口


package servletContainer.processor;

import base.Request;
import base.Response;

public interface IProcessor {
    
    public void init();
    
    public void process(Request request,
            Response response);
}

StaticProcessor类主要是处理静态资源请求
类定义


package servletContainer.processor;

import base.Request;
import base.Response;

public class StaticProcessor implements IProcessor {

    @Override
    public void process(Request request, Response response) {
        try{
            response.sendStaticResource();
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void init() {
        
    }
    
}

ServeletProcessor主要负责处理Servlet请求,初始化的时候,初始化所有的Servlet子类,接收到servlet的http请求之后,根据请求名称,调用对应的service函数。
类定义


package servletContainer.processor;

import java.util.HashMap;
import java.util.Map;

import base.Request;
import base.Response;
import interf.Servlet;
import servletContainer.PrimitiveServlet;

public class ServletProcessor implements IProcessor {
    
    private Map<String,Servlet> map = new HashMap<String,Servlet>();
    
    public ServletProcessor() {
        
    }
    
    public void init(){
        PrimitiveServlet servlet = new PrimitiveServlet();
        servlet.init(null);
        map.put("PrimitiveServlet", servlet);
    }

    @Override
    public void process(Request request, Response response) {
        String uri = request.getUri();
        String servletName = uri.substring(uri.lastIndexOf("/") + 1);
        
        Servlet servlet = map.get(servletName);
        try{
            if(servlet != null){
                servlet.service(request, response);
            }
            else{
                String errorMessage = "HTTP/1.1 404 File Not Found\r\n" + 
                        "Content-Type: text/html\r\n" +
                        "Content-Length:23\r\n" +
                        "\r\n" + 
                        "<h1>File Not Found</h1>";
                response.getWriter().print(errorMessage.getBytes());
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        catch (Throwable e){
            e.printStackTrace();
        }
        
    }
    
}

结果
我们在eclipse里运行结果

运行结果1

运行结果2


相关文章
|
2月前
|
Java 应用服务中间件 Apache
浅谈Tomcat和其他WEB容器的区别
Tomcat是一款轻量级的免费开源Web应用服务器,常用于中小型系统及并发访问量适中的场景,尤其适合开发和调试JSP程序。它不仅能处理HTML页面,还充当Servlet和JSP容器。相比之下,物理服务器是指具备处理器、硬盘等硬件设施的服务器,如云服务器,其设计目标是在处理能力、稳定性和安全性等方面提供高标准服务。简言之,Tomcat专注于运行Java应用,而物理服务器则提供基础计算资源。
|
3月前
|
Java 应用服务中间件 Maven
JavaWeb基础5——HTTP,Tomcat&Servlet
JavaWeb技术栈、HTTP、get和post区别、响应状态码、请求响应格数据式、IDEA使用Tomcat、报错解决、Servlet的体系结构、IDEA使用模板创建Servlet
JavaWeb基础5——HTTP,Tomcat&Servlet
|
5月前
|
弹性计算 运维 应用服务中间件
容器的优势,在Docker中运行Tomcat
摘要:了解Docker与虚拟机的区别:虚拟机使用Hypervisor创建完整操作系统,而容器通过namespace和cgroup实现轻量级隔离,共享主机内核。Docker启动快、资源利用率高,适合快速部署和跨平台移植。但安全性相对较低。示例介绍了如何通过Docker搜索、拉取官方Tomcat镜像并运行容器,最后验证Tomcat服务的正常运行。
|
5月前
|
前端开发 Java 应用服务中间件
Spring Boot 2.x 嵌入式 Servlet 容器
Spring Boot使用内嵌Tomcat,默认端口8080,可通过`application.properties`配置端口、上下文路径等。配置方式有两种:1) 直接在配置文件中添加`server.port`和`server.servlet.context-path`;2) 创建`WebServerFactoryCustomizer` Bean来自定义配置,如设置端口`factory.setPort(8083)`,这种方式优先级更高。
|
6月前
|
Java 应用服务中间件 Maven
|
6月前
|
前端开发 应用服务中间件
|
6月前
|
Java 应用服务中间件 容器
手写SpringBoot(二)之动态切换Servlet容器
我们在切换serlvet容器的时候,会将SpringBoot默认的tomcat jar包给排除掉,换上我们需要的jar包,比如jetty。
41 0
|
6月前
|
XML 数据格式
|
6月前
|
XML Java 应用服务中间件
|
15天前
|
Kubernetes Cloud Native Docker
云原生时代的容器化实践:Docker和Kubernetes入门
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性和效率的关键。本篇文章将引导读者了解如何利用Docker进行容器化打包及部署,以及Kubernetes集群管理的基础操作,帮助初学者快速入门云原生的世界。通过实际案例分析,我们将深入探讨这些技术在现代IT架构中的应用与影响。
56 2