重拾Servlet

1.引言

1.1 Web开发的复杂性

Web开发是一个涉及多个技术、工具、框架和标准的领域。随着Web的迅速发展,开发者们需要面对和解决越来越多的问题和挑战。以下是一些增加Web开发复杂性的因素:

  1. 多样性的客户端设备:Web开发者必须确保其网站或应用可以在各种设备上正常工作,从桌面计算机到移动设备,以及在各种屏幕大小和分辨率上都表现良好。
  2. 浏览器兼容性:不同的浏览器(和它们的不同版本)可能对Web技术的支持有所不同。开发者需要确保其网站或应用在所有主流浏览器上都可以正常工作。
  3. 持续的技术演进:Web技术正在持续演进。新的标准、工具和框架不断出现,同时旧的技术逐渐被淘汰。
  4. 安全性问题:随着网络攻击手段的不断演进,保护Web应用免受各种安全威胁变得日益重要和复杂。
  5. 高性能和可伸缩性要求:用户对网站的响应速度和可用性有很高的期望。开发者需要确保其应用即使在高流量下也能正常工作。
  6. 前后端分离:现代Web开发常常采用前后端分离的架构,这要求开发者掌握前端和后端两套技术栈。
  7. 多样的框架和库:从React、Vue和Angular的前端框架到Spring、Django和Express的后端框架,开发者有大量的选择,但也需要花时间学习和掌握这些工具。
  8. 状态管理和用户体验:为了提供丰富和流畅的用户体验,开发者需要管理复杂的应用状态、异步操作和用户界面更新。
  9. Web服务和APIs的集成:很多Web应用都依赖于第三方服务和APIs,这要求开发者理解和整合这些外部资源。
  10. 持续集成和部署:为了提高软件的质量和交付速度,开发者需要设置和管理自动化的测试、构建和部署流程。

1.2 为什么需要Servlet、Spring和Tomcat

Servlet 是 Java EE 规范中的一个组件,它用于处理Web应用中的请求。基本上,Servlet是Java Web开发的基石,用于接收客户端请求、处理这些请求,并返回相应的响应。通过Spring 是一个广泛使用的Java开发框架,它提供了一系列的工具和特性,从依赖注入到安全性、事务管理、数据访问,再到Web开发,使得Java开发更为简单、模块化和高效。Tomcat: Tomcat 是一个开源的Servlet容器,用于部署和运行基于Java的Web应用。它负责管理Servlet的生命周期,并确保请求正确地路由到相应的Servlet进行处理。

现在,让我们探讨为什么这些组件在Web开发中如此重要:

  1. 强大的请求处理机制: 传统的CGI(公共网关接口)方式存在效率问题,而Servlet为每个请求提供了一个轻量级的、多线程的处理方式。这意味着Servlet可以快速、高效地处理大量的并发请求。
  2. 结构化的Java Web开发: Servlet为Java开发者提供了一个结构化的方式来处理HTTP请求和响应,避免了直接处理底层的HTTP细节。
  3. 集成与扩展: Spring提供了与众多技术和库的集成,使得Java开发者可以轻松地实现复杂的功能,例如数据访问、事务管理、安全性和RESTful Web服务。
  4. 依赖注入和控制反转: Spring的核心是IoC容器,它使得对象的管理和依赖注入变得简单,从而促进了代码的解耦和模块化。
  5. 灵活的部署: Tomcat是一个轻量级的Web服务器和Servlet容器,它可以快速地启动并运行Web应用,无需完整的Java EE服务器。这使得它成为开发、测试和生产环境中的理想选择。
  6. 开源与社区支持: Servlet、Spring和Tomcat都是开源的,并拥有庞大的开发和用户社区。这确保了这些技术的持续进步,同时也为开发者提供了大量的资源、工具和插件。
  7. 跨平台性: 这三者都是基于Java的,这意味着你可以在任何支持Java的平台上开发、部署和运行你的应用。

2.Servlet简介

2.1 定义与主要功能

定义:

Servlet是Java编写的服务器端程序,它是Java EE规范的一部分,用于处理客户端的请求并返回响应。简而言之,Servlet可以看作是Web应用程序中处理HTTP请求的Java类。

纯Servlet项目

代码1

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <servlet>
        <servlet-name>HelloWorldServlet</servlet-name>
        <servlet-class>HelloWorldServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloWorldServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

或者

代码2

@WebServlet("/hello")
public class HelloWorldServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        out.println("Hello, World!");
        out.close();
    }
}

注:这里提一下Tomcat通过读取WEB-INF/web.xml文件,这是Web应用的部署描述符。在web.xml中,开发者会指定哪些Servlet类需要被加载,以及它们的URL映射。例如[代码1] ,具体怎么加载Tomcat再说

主要功能:

  • 请求处理: Servlet接收来自客户端的HTTP请求。还有
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String name = request.getParameter("name");
    response.getWriter().write("Hello, " + name);
}
  • 响应生成: 根据接收到的请求,Servlet可以生成HTML、XML或JSON等响应返回给客户端。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("application/json");
    PrintWriter out = response.getWriter();
    out.print("{\"message\":\"Hello World\"}");
    out.flush();
}
  • 生命周期管理: 包括初始化 (init 方法)、请求处理 (servicedoGet/doPost 方法) 以及销毁 (destroy 方法)。
public class ExampleServlet extends HttpServlet {
    public void init() throws ServletException {
        // Initialization code here...
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Handle request...
    }

    public void destroy() {
        // Cleanup resources...
    }
}
  • Session管理: Servlet支持会话管理机制,如HTTP sessions。
//这里简单写一下,在项目中可能遇到一些Session共享情况,可以重写HttpSession接口进行自定这个以后可以说
HttpSession session = request.getSession();
session.setAttribute("user", "John");
String user = (String) session.getAttribute("user");
response.getWriter().write("Session user: " + user);
  • 过滤器链: 通过Servlet过滤器,可以实现请求和响应的拦截和修改。

通过实现Filter接口实现项目中过滤,(``SpringSecurity鉴权拦截等也是基于Filter`接口实现)

public class ExampleFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
        // Initialization code...
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // Before processing request...
        chain.doFilter(request, response);  // Continue the request-response chain
        // After processing request...
    }

    public void destroy() {
        // Cleanup code...
    }
}

2.2 Servlet生命周期: 初始化、服务和销毁

Servlet的生命周期是由其三个主要方法控制的:init(), service(), 和 destroy()。让我们深入探讨这三个方法以及Servlet生命周期的各个阶段。

  1. 初始化 (init 方法):

    • 当Servlet第一次被请求或服务器启动时(取决于部署描述符中的配置),Servlet容器(例如Tomcat)决定加载Servlet。

    • Servlet容器会实例化Servlet并调用其init方法。

    • init方法只会被调用一次,通常用于执行一次性的初始化任务,如加载配置、建立数据库连接等。

    • 代码示例:

      javaCopy code
      public void init() throws ServletException {
          // Initialization code, e.g., open a database connection
      }
      
  2. 服务 (service 方法):

    • 对于每次到Servlet的请求,Servlet容器会调用service方法。

    • service方法会根据HTTP请求类型(如GET、POST)调用相应的doGet, doPost等方法。

    • 由于Servlet是多线程的,service方法或者它调用的其他doXXX方法可以同时被多个线程调用,因此必须保证Servlet的线程安全。

    • 代码示例:

      javaCopy code
      protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          // Call super to delegate to doGet, doPost, etc.
          super.service(req, resp);
      }
      
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          // Handle GET request
      }
      
  3. 销毁 (destroy 方法):

    • 当Servlet容器决定卸载Servlet,例如在服务器关闭或Web应用被重新部署时,destroy方法被调用。

    • 这是Servlet生命周期的最后阶段,通常用于释放资源和执行清理任务。

    • init方法一样,destroy方法在Servlet的生命周期中只会被调用一次。

    • 代码示例:

      javaCopy code
      public void destroy() {
          // Cleanup resources, e.g., close a database connection
      }
      

Servlet生命周期从init开始,通过多次的service调用,并最终在destroy中结束。开发者需要确保在这些方法中正确地管理资源和状态,以确保Servlet的高效和稳定运行。

2.3 如何处理HTTP请求

接收请求:容器的HTTP连接器(如Tomcat的Coyote)首先接收来自客户端的HTTP请求。

创建Request、Response对象:为每个请求,容器创建一个新的HttpServletRequest对象和一个HttpServletResponse对象。

URL解析:容器解析请求URL,确定上下文(也就是Web应用程序)并找出请求的资源路径。

匹配Servlet:容器使用URL的资源路径来确定哪个Servlet应该处理请求。它查看web.xml中的servlet-mapping条目来找出匹配的URL模式。

Servlet实例化:如果这是对指定Servlet的第一次请求,容器会实例化它(调用其默认构造函数)。如果Servlet已经在内存中,容器会复用现有的实例。

当一个 HTTP 请求到达 Web 服务器时,服务器会将该请求转发给相应的 Servlet。Servlet 通过实现 javax.servlet.Servlet 接口并重写 service 方法,来处理这些请求。

service源码分析

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    String method = req.getMethod();

    if (method.equals("GET")) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader("If-Modified-Since");
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals("HEAD")) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals("POST")) {
        doPost(req, resp);
        
    } // ... 其他 HTTP 方法的处理逻辑
}

从上面的源码可以看到,service 方法首先通过 req.getMethod() 获取 HTTP 请求的方法类型。然后,根据不同的请求类型,调用相应的 doXxx(get方法和post方法) 方法进行处理。

对于 GET 请求,Servlet 还会检查请求头中的 “If-Modified-Since” 信息,以确定是否需要重新发送数据。

web.xml的解析

web.xml,也称为部署描述符,是Servlet应用的核心配置文件。它定义了Servlet、Servlet的初始化参数、URL模式、监听器、过滤器等。

源码分析

当Tomcat启动或Web应用部署时,web.xml被读取和解析。这是一个简化的步骤概述:

  1. 文件位置识别:Tomcat首先确定web.xml的位置,通常在WEB-INF目录中。
  2. 解析XML:Tomcat使用SAX(简单API用于XML)解析器来解析web.xml文件。
  3. 填充数据结构:在解析过程中,Tomcat会填充内部数据结构(如StandardContext)与解析出的信息。
  4. URL模式到Servlet的映射:其中最重要的是URL模式到Servlet类的映射。当请求到达时,Tomcat使用这个映射来确定应该调用哪个Servlet。

例如,考虑以下的servlet-mapping片段:

<servlet-mapping>
	<servlet-name>HelloWorldServlet</servlet-name>
	<url-pattern>/hello</url-pattern>
</servlet-mapping>

当Tomcat解析这段内容时,它会知道任何请求路径为/hello的请求都应该由名为HelloWorldServlet的Servlet处理。

结论

Servlet 是处理 HTTP 请求的核心组件,它与 Servlet 容器紧密合作,共同完成请求的处理。web.xml 文件为这一过程提供了必要的配置,指导容器如何路由请求到指定的 Servlet。理解这一流程对于深入了解 Java Web 开发至关重要。后期再谈了Tomcat和servlet

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值