0.为什么来了解它
(背景-冲突-疑问-答案)
作为go/c++的程序员,学习java后,后端部署web应用基本都会碰到tomcat,查阅后说是web容器,跟nginx也不是一个品类,一下子还不太好理解这玩意儿。
以往的认知中(go/c++)通常后端服务不是直接启动就可以了吗,启动后应用程序监听端口接收协议请求,处理请求后返回,为什么到了java还要一个web容器???
仔细研究后发现,从设计解偶考虑,才有了web容器(tomcat/jetty等),具体逻辑看以下步骤:
1.没有web容器会怎么写java web程序
- 监听端口接收客户端请求并建立连接
- 接收客户端数据包请求,根据http协议解析消息
- 根据消息类型,将请求路由到指定的业务处理代码
- 业务代码处理业务逻辑并返回结果
- 根据http协议组装返回的数据包
- 通过链接返回给客户端
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请求的转发。