本篇博客主要介绍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()在每次请求的处理中都会被调用一次。