0.为什么来了解它

(背景-冲突-疑问-答案)

作为go/c++的程序员,学习java后,后端部署web应用基本都会碰到tomcat,查阅后说是web容器,跟nginx也不是一个品类,一下子还不太好理解这玩意儿。

以往的认知中(go/c++)通常后端服务不是直接启动就可以了吗,启动后应用程序监听端口接收协议请求,处理请求后返回,为什么到了java还要一个web容器???

仔细研究后发现,从设计解偶考虑,才有了web容器(tomcat/jetty等),具体逻辑看以下步骤: 

1.没有web容器会怎么写java web程序

  1. 监听端口接收客户端请求并建立连接
  2. 接收客户端数据包请求,根据http协议解析消息
  3. 根据消息类型,将请求路由到指定的业务处理代码
  4. 业务代码处理业务逻辑并返回结果
  5. 根据http协议组装返回的数据包
  6. 通过链接返回给客户端

2.会有什么问题

1)请求到来后,服务器怎么知道要调用哪个Java类的哪个方法呢?

最直接的做法是在HTTP服务器代码里写一大堆if else逻辑判断,这样HTTP服务器的代码跟业务逻辑耦合在一起了,如果新加一个业务方法还要改HTTP服务器的代码。

2)连接管理、协议解析、数据接收发送部分基本不变,而业务处理常变,变与不变耦合在一起

3.如何优化,如何解偶(servlet规范)

面向接口编程是解决耦合问题的法宝,于是有人就定义了一个接口,各种业务类都必须实现这个接口,这个接口就叫Servlet接口,我们也把实现了Servlet接口的业务类叫作Servlet。从而将不变部分(3、6、8)与常变部分(7)分开。

但是这里还有一个问题,对于特定的请求,HTTP服务器如何知道由哪个Servlet来处理呢?Servlet又是由谁来实例化呢?显然HTTP服务器不适合做这个工作,否则又和业务类耦合了。

于是,又有了Servlet容器,Servlet容器用来加载和管理业务类。HTTP服务器不直接跟业务类打交道,而是把请求交给Servlet容器去处理,Servlet容器会将请求转发到具体的Servlet,如果这个Servlet还没创建,就加载并实例化这个Servlet,然后调用这个Servlet的接口方法。因此Servlet接口其实是Servlet容器跟具体业务类之间的接口

Servlet本质上是一个接口,实现了Servlet接口的业务类也叫Servlet。Servlet接口其实是Servlet容器跟具体Servlet业务类之间的接口。Servlet接口跟Servlet容器这一整套规范叫作Servlet规范,而Servlet规范使得程序员可以专注业务逻辑的开发。

引入了Servlet规范后,你不需要关心Socket网络通信、不需要关心HTTP协议,也不需要关心你的业务类是如何被实例化和调用的,因为这些都被Servlet规范标准化了,你只要关心怎么实现的你的业务逻辑。

4.有了web容器后,后端怎么编写和部署java web程序

首先,要以Web应用程序的方式来部署Servlet,那么根据Servlet规范,Web应用程序有一定的目录结构,在这个目录下分别放置了Servlet的类文件、配置文件以及静态资源,Servlet容器通过读取配置文件,就能找到并加载Servlet。

1)创建一个Java类去继承HTTPServlet类,并重写doGet方法

import java.io.IOException;

import java.io.PrintWriter;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;



public class HelloTomcat extends HttpServlet {

    @Override

    protected void doGet(HttpServletRequest request, HttpServletResponse response)

            throws ServletException, IOException {

        PrintWriter out = response.getWriter();

        response.setContentType("text/html;charset=utf-8");

        out.println("Hello Tomcat!");

    }

}

2)将Java文件编译成Class文件

需要把Tomcat lib目录下的servlet-api.jar拷贝到当前目录下,这是因为servlet-api.jar中定义了Servlet接口,而我们的Servlet类实现了Servlet接口,因此编译Servlet类需要这个JAR包

```

javac -cp ./servlet-api.jar HelloTomcat.java

```

3)配置并建立目录结构

Servlet是放到Web应用部署到Tomcat的,而Web应用具有一定的目录结构,所有我们按照要求建立Web应用文件夹:

```

myweb/WEB-INF/web.xml

myweb/WEB-INF/classes/HelloTomcat.class

```

然后在web.xml中配置Servlet(配置Servlet的名字(myServlet)和具体的类(HelloTomcat),以及这个Servlet对应的URL路径(/hello)):

<?xml version="1.0" encoding="UTF-8"?>

<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_4_0.xsd"

  version="4.0"

  metadata-complete="true">



    <description> Servlet Example. </description>

    <display-name> MyServlet Example </display-name>

    <request-character-encoding>UTF-8</request-character-encoding>



    <servlet>

      <servlet-name>myServlet</servlet-name>

      <servlet-class>HelloTomcat</servlet-class>

    </servlet>



    <servlet-mapping>

      <servlet-name>myServlet</servlet-name>

      <url-pattern>/hello</url-pattern>

    </servlet-mapping>



</web-app>

4)部署与启动

以Tomcat为例,到官网(Apache Tomcat® - Apache Tomcat 9 Software Downloads)下载最新的版本,然后解压:

/bin:启动和关闭Tomcat的脚本文件
/conf:各种全局配置文件,其中最重要的是server.xml
/lib:存放Tomcat以及所有Web应用都可以访问的JAR文件
/logs:存放Tomcat执行时产生的日志文件
/work:存放JSP编译后产生的Class文件
/webapps:Tomcat的Web应用目录,默认情况下把Web应用放在这个目录下

部署:将我们的应用目录(myweb)拷贝到Tomcat的安装目录下的webapps目录即可

启动:然后找到Tomcat安装目录下的bin目录,执行startup.sh/startup.bat启动Tomcat

验证:浏览器访问 http://localhost:8080/myweb/hello,你会看到:Hello Tomcat!

5.其它

Servlet规范里定义了ServletContext这个接口来对应一个Web应用。Web应用部署好后,Servlet容器在启动时会加载Web应用,并为每个Web应用创建唯一的ServletContext对象。你可以把ServletContext看成是一个全局对象,一个Web应用可能有多个Servlet,这些Servlet可以通过全局的ServletContext来共享数据,这些数据包括Web应用的初始化参数、Web应用目录下的文件资源等。由于ServletContext持有所有Servlet实例,你还可以通过它来实现Servlet请求的转发。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐