本篇博客主要介紹Tomcat的簡單知識。
什麼是Tomcat?
Tomcat本質就是一個軟件。一款運行在用戶態(站在操作系統角度),運行在應用層(站在網絡層次)的軟件。它是用來溝通瀏覽器(用戶)和後臺服務(Servlet對象)之間的橋樑和中介。
我們可以稱Tomcat爲HTTPServer、Web容器或者Servlet容器。
Tomcat的安裝
Tomcat的安裝非常簡單,下載安裝包解壓即可。安裝包可去官網下載。
目錄結構
apache-tomcat-8.5.47\
bin/ 存放各種啓動、停止的腳本。*sh是在Linux下用的,*bat是在Windows下用的
startup.bat 啓動服務,雙擊即可運行
conf/ 相關的配置文件
lib/ 運行Tomcat需要的類庫
logs/ 運行時的日誌文件,有時需要查看日誌文件來定位問題
temp/ 臨時文件夾
webapps/ 存放我們要運行的web application的文件夾,最常用的一個文件夾
work/ Tomcat內部進行預編譯的文件夾
Tomcat的啓動
雙擊bin/startup.bat啓動
我們可以在瀏覽器輸入http://localhost:8080/
,訪問的是webapps\ROOT\文件夾下的應用。
我們來看一下webapps的目錄結構:
webapps\
docs\
examples\
host-manager\
manager\
ROOT\
webapps目錄下的每個文件夾都代表一個web應用,可以通過下列URL在瀏覽器中訪問:
http://localhost:8080/docs/
http://localhost:8080/examples/
http://localhost:8080/host-manager
Tomcat使用示例
不使用IDEA創建一個web應用
我們在webapps目錄下創建一個新的文件夾hello,並且創建好如下的目錄結構:
index.html中的內容如下:
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<h1>Hello,World!</h1>
</body>
</html>
web.xml中的內容如下:
<?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_3_1.xsd"
version="3.1"
metadata-complete="true">
<servlet>
<servlet-name>Hello</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Hello</servlet-name>
<url-pattern>/hello-world</url-pattern>
</servlet-mapping>
</web-app>
HelloServlet.java中的內容如下:
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 HelloServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter pw = response.getWriter();
pw.println("<h1>你好,世界!</h1>");
}
}
下面我們將HelloServlet.java編譯成HelloServlet.class。
javac -cp ..\..\..\..\lib\servlet-api.jar -encoding utf-8 HelloServlet.java
最後我們啓動Tomcat,然後在瀏覽器輸入:http://localhost:8080/hello/
,這裏默認訪問的是index.html文件
我們在瀏覽器輸入:http://localhost:8080/hello/hello-world
,這裏會去調用HelloServlet中的doGet方法
使用IDEA創建一個web應用
首先我們創建一個maven項目。然後將pom.xml修改爲如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 打war包 -->
<packaging>war</packaging>
<groupId>com.sss</groupId>
<artifactId>test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<encoding>UTF-8</encoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<!-- servlet 版本和 tomcat 版本有對應關係,切記 -->
<version>3.1.0</version>
<!-- 這個意思是我們只在開發階段需要這個依賴,部署到 tomcat 上時就不需要了 -->
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<!-- 指定最終包的名稱 -->
<finalName>hello</finalName>
<!-- 明確指定一些插件的版本,以免受到 maven 版本的影響 -->
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
當前目錄結構如下:
我們需要在src/main目錄下添加如下目錄結構:
其中HelloServlet.java、index.html、web.xml中的內容和前面一致。
我們也可以將web.xml中的以下內容去掉,改用@WebServlet("/hello-world")
註解的方式也是一樣的。
@WebServlet("/hello-world")
註解的使用方式如下:
然後,我們使用maven的package命令進行打包。
我們將該war包拷貝到webapps目錄下,啓動Tomcat即可。
我們在瀏覽器輸入:http://localhost:8080/hello/
,這裏默認訪問的是index.html文件
我們在瀏覽器輸入:http://localhost:8080/hello/hello-world
,這裏會去調用HelloServlet中的doGet方法
Tomcat相關概念
HttpServlet和Servlet的關係
Servlet是Java中的一個接口。
interface Servlet {
void init(ServletConfig var1) throws ServletException;
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
void destroy();
}
HttpServlet是Servlet接口的一個實現類,但它本身是個抽象類。所以當我們繼承HttpServlet,需要實現我們要支持的方法,如doGet、doPost等。
abstract class HttpServlet implements Servlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 根據request的method不同,調用不同的方法
}
// GET時調用
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ...
}
// POST時調用
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ...
}
// 其他HTTP協議支持的方法
}
Tomcat的基本原理
一個Tomcat實例可以有多個host,一個host可以有多個Context,一個Context中可以由多個Servlet對象。
每個host都有一組Context,webapps這個目錄下的每個文件夾都是一個Context。
一個HTTP請求從到達Tomcat開始,怎麼一步一步被分給我們寫好的一個具體的Servlet對象或者靜態文件?
- 首先,Tomcat根據HTTP請求中的URL中的域名,選擇將該請求交付給具體哪個host;
- 然後,Tomcat跟HTTP請求中的URL中的Context Path,選擇將該請求交付給具體的哪個Context,如果Context Path是/,則交付給ROOT這個比較特別的Context;
- 最後,Tomcat根據HTTP請求中的URL中去掉Context Path部分的路徑信息結合(web.xml和@WebServlet註解)和靜態文件的目錄結構,找到具體的Servlet對象或者靜態文件;
- 根據不同的HTTP請求方法,調用doGet或者doPost等方法。
如果項目中遇到了404,應該查哪些問題?
- 首先檢查你訪問的是你期望的Tomcat嗎,URL中的端口是否是Tomcat正在監聽的端口;
- 然後檢查訪問的URL是哪個host,是否存在;
- 根據URL中的Context Path,看下訪問的是哪個Context,是否存在;
- 根據URL中的剩餘路徑部分,結合web.xml和@WebServlet註解還有類文件,靜態文件,看看配置是否正確;
- 最後再看一下404是不是你的Servlet代碼中加的。
Servlet對象的生命週期?
- 每個Servlet對象,在其生命過程中,init()在啓動時被調用一次,destroy()在退出時被調用一次,service()在每次請求的處理中都會被調用一次。
Servlet對象工作多線程環境下:
- 因爲Tomcat內部是使用線程處理每個請求的,並且每個Servlet對象只會存在一個,所以我們覆寫的doGet等方法,是在多線程環境下運行的,需要考慮線程安全問題。
總結
- Tomcat就是一個Web Container,內部實現了一個HTTP服務器;
- 同時會根據不同的URL,區分出是靜態內容還是動態內容;
- 如果是動態內容,則根據web.xml中的配置找到對應的Servlet對象進行處理;
- 我們自己寫的代碼只是這個環節中的一個步驟,不再需要從main入口開始實現了;
- 我們在Servlet中寫的代碼,其實都是一個在多線程環境下運行的,要注意線程安全問題;
- 每個Servlet對象,在其生命過程中,init()在啓動時被調用一次,destroy()在退出時被調用一次,service()在每次請求的處理中都會被調用一次。