…
…
一、预备知识
这部分内容时理解 Tomcat 的基础,主要介绍服务器的概念。
1、软件架构
1. C/S:客户端/服务器端
2. B/S:浏览器/服务器端
2、资源分类
1. 静态资源:所有用户访问后,得到的结果都是一样的,称为静态资源.静态资源可以直接被浏览器解析
* 如: html,css,JavaScript
2. 动态资源:每个用户访问相同资源后,得到的结果可能不一样。称为动态资源。动态资源被访问后,需要先转换为静态资源,在返回给浏览器
* 如:servlet/jsp,php,asp....
3、网络通信三要素
1. IP:电子设备(计算机)在网络中的唯一标识。
2. 端口:应用程序在计算机中的唯一标识。 0~65536
3. 传输协议:规定了数据传输的规则
1. 基础协议:
1. tcp:安全协议,三次握手。 速度稍慢
2. udp:不安全协议。 速度快
4、服务器软件:
- 服务器就是安装了服务器软件的计算机,它的硬盘比较大,内存比较多,CPU性能好。
- 比如电脑安装了 MySQL 服务器软件,那么这台电脑就可以作为服务器,因为它可以让别的电脑访问到然后操作 MySQL 里的数据。
- 那么安装了 Web 服务器软件,那么这台电脑就可以作为 Web 服务器,让别的电脑访问到。
服务器软件的作用就是可以接收用户的请求,处理请求,做出响应。
5、web 服务器软件
在web服务器软件
中,可以部署 web 项目,让用户通过浏览器来访问这些项目。
之前我们的角色是用户,将来我们的角色内容提供者,让别人通过浏览器来访问我们写的项目。
动态资源不能够自动运行,他们只能运行在 Web 服务器软件中。
所以也被称为 Web容器,也就是说动态资源必须在容器中才能运行。
而我们要学习的 Tomcat
就是这样一款 Web 服务器软件。
二、Tomcat 的使用
1、下载
http://tomcat.apache.org/
2、安装
解压压缩包即可。
- 注意:安装目录建议不要有中文和空格
解压完之后如图所示:
每个文件夹内容:
3、卸载
删除目录就行了
4、启动
1、`bin/startup.bat` ,双击运行该文件即可
2、访问:
浏览器输入:
http://localhost:8080 访问自己
http://别人的ip:8080 访问别人
5、Tomcat 启动可能遇到的问题
- 黑窗口一闪而过
- 原因: 没有正确配置JAVA_HOME环境变量
- 解决方案:正确配置JAVA_HOME环境变量
- 启动报错
暴力法:找到占用的端口号,并且找到对应的进程,杀死该进程
netstat -ano
温柔法:修改自身的端口号
在下面的文件中修改
conf/server.xml
<Connector port="8888" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8445" />
一般会将tomcat的默认端口号修改为80。
80端口号是http协议的默认端口号。
好处:在访问时,就不用输入端口号
6、关闭
1、正常关闭:
bin/shutdown.bat
ctrl+c
2、强制关闭:
点击启动窗口的×
7、配置
这里才是最重要的
部署项目的方式:
- 直接将项目放到 webapps 目录下:
将项目打成一个war包,再将
war包
放置到webapps
目录下。war包会自动解压缩
- 配置 conf/server.xml 文件:
在标签体中配置
<Context docBase="D:\hello" path="/hehe" />
//docBase是项目存放目录
//hehe是虚拟目录
- 在
conf\Catalina\localhost
创建任意名称的xml
文件
在文件中编写:
<Context docBase="D:\hello" />
//虚拟目录:xml文件的名称
java动态项目的目录结构:
-- 项目的根目录
-- WEB-INF目录:
-- web.xml:web项目的核心配置文件
-- classes目录:放置字节码文件的目录
-- lib目录:放置依赖的jar包
三、Tomcat 集成 intelliJ IDEA
1、首先新建一个项目,选择 java EE
选择 Tomcat Server
:
选择本地的包,选最外层的文件夹就可以了,选好了之后下面的 Tomcat base directory
他会自动生成:
Application Server 选择你本地解压的 Tomcat 的包的位置。
注意这里要勾上
当然不勾也可以,我们也可以手动创建这些包,但是自动生成会方便一些。
2、创建完成
创建完成之后的目录结构应该是这样的:
你可以看到,该有的包都已经自动导入了。
3、配置 Tomcat
选择 run 下面的 Edit Configurations
点击Template
拓展窗口
找到 Tomcat
,选择local
,
然后配置到你本地的 Tomcat 地址,端口什么的保持原样就可以了
然后点击 Apply OK 关掉就可以了。
4、启动项目
配置好了之后选择你的Tomcat
,然后启动:
我这里稍微改造了一下 index.html
界面,如图所示:
贴出来代码:
<%--
Created by IntelliJ IDEA.
User: wsuo
Date: 2020/2/26
Time: 16:39
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>18103330219-王硕</title>
<style>
#d1 {
width: 600px;
height: 400px;
background-color: antiquewhite;
text-align: center;
border-radius: 10%;
margin-top: 140px;
margin-left: 450px;
position: absolute;
}
.font {
color: darkorange;
font-weight: bolder;
text-align: center;
}
</style>
</head>
<body>
<div id="d1">
<h1 class="font">计算机182 ** 1810333**** 男</h1>
<%
String a = "abc";
System.out.println(a);
out.println(a + "d");
%>
</div>
</body>
</html>
运行在浏览器端是这样子的:
这样你的IDEA
集成 Tomcat
就成功了!
5、虚拟目录
这里稍微多讲一点,关于
虚拟目录
如果你细心的话会发现我的项目网址名称很难看:
其中的这个wsProject_war_exploded
就是你的虚拟目录
什么意思呢,就是你要在路径名称上加上这句才可以访问到,哪有的同学说你这个太难看了,我可以换个吗,当然可以,你可以随时打开如下图的配置:
点击之后选择 Deployment
然后看下图:
其中这个 Application Context
就是你的虚拟目录,你想改成什么都可以,这样你的访问路径就会发生相应的变化:
比如我如果设置成这样:
那么我访问的时候浏览器的地址就应该是:
http://localhost:8080/index.jsp
如果设置成abcd
那么我访问的时候浏览器的地址就应该是:
http://localhost:8080/abcd/index.jsp
6、端口
再来聊一下端口
细心的你会发现,我们的地址栏冒号后面有个8080
,那为什么是8080呢,我填8089行不行?
答案是不行的,但是我们可以配置:
将这个数字改成你想要的的都可以,但是注意可能会有端口占用的问题,这样的话你可以选择我们之前提到的暴力法或者温柔法解决它。
注意一点就是如果你设置成 80
端口,那么你就不用填写端口了:
比如上面的地址可以进一步简化为:
http://localhost/index.jsp
因为 80
端口是浏览器的默认端口!
就好像你在 计182
班级里面不用加 计182 的前缀一样,因为大家都知道你是182班的。
7、从一般到特殊
OK,我们回过来看这个页面:
路径:
http://localhost:8080/wsProject_war_exploded/index.jsp
我们已经知道端口
和虚拟目录
了,那么后面的index.jsp
是什么鬼呢???
可以换吗?
当然可以,看图:
从图中可以看出,index
是直属于web
文件夹的,而 web 是一个 webapp 的核心,所以index
就是直接在项目根目录下的。
如果我们把index
放进一个文件夹里比如file
里,那么访问路径就要改为:
http://localhost:8080/wsProject_war_exploded/file/index.jsp
那如果我们不想访问index.jsp
呢,比如想访问 hello.html
:
贴出来代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body>
<h1>
你好我是Hello.html!
</h1>
</body>
</html>
我们新建了web/file/hello.html
,于是可以在浏览器输入地址访问:
http://localhost:8080/wsProject_war_exploded/file/hello.html
还有就是 index.jsp
,本身比较特殊,就算我们不写也可以访问到,这是因为名字特殊,是一个默认值,这在后面学习 web.xml
中会讲到.
8、IDEA 的热部署
如果你跟着我一起做了,相信你一定深有体会,改变文件内容之后一定要重新启动服务器,不然改动效果不会生效。这在开发过程中是很烦的,为了解决这个问题,我们可以使用以下办法(如果你的IDEA使用本来就很卡顿不建议这么做):
修改为上面在每次新建文件的时候就可以不用每次都重启服务器了。
如果你改为:
这样你以后再写 java 代码的时候也可以不用重启服务器了,但是并不推荐这样做,因为java代码的修改次数比较频繁。
由于篇幅原因,有关 Tomecat 的原理部分请看我的另外一篇文章。
四、IDEA 集成 Tomcat 分析
IDEA会为每一个 Tomcat 部署的项目单独建立一份配置文件。
那么这个配置文件在哪呢?
我们先启动一个项目:
观察控制台的输出:
观察到有 Using CATALINA_BASE
,这个的意思就是使用的工作目录,也就是 工作空间项目。
C:\Users\13343\.IntelliJIdea2019.2\system\tomcat\Tomcat_8_5_31_wsProject
然后我们顺着这个路径去电脑上面查看:
在 conf 里面有一大推配置文件:
这里有很多的配置文件,我们可以打开进行修改端口什么的,但是现在都是 IDEA 磅我们生成好了,通过图形化界面自动生成好了,所以IDEA简化了我们的开发,这就好像计算机指令与图形化界面的关系一样。
然后我们继续往下看:
打开 \conf\Catalina\localhost
发现有一个文件,还是以我们的项目名称命名的!
打开看看:
发现有一个路径,这是Tomcat部署的web项目:
D:\develop\IDEA_2017\IdeaProjects\lsu_study\wsProject\out\artifacts\wsProject_war_exploded
在电脑中打开:
这就是我们之前所说的第三种项目的部署方式
,这里IDEA又帮我们自动生成了。
就是这个:
在
conf\Catalina\localhost
创建任意名称的xml
文件
-
他就相当于在 webapps 目录下放了一个 war 包。
-
你工作空间的
out
目录下面就是Tomcat的项目部署的地方。 -
这个才是 Tomcat 真正访问的项目。
而 Tomcat部署的web项目 目录下会有一个 classes ,这里的文件从哪里来呢?
从你的项目的 src 目录下来的。例子中的项目 src 目录为空,所以没有 classes 文件夹。
仔细看上面两个目录,你会发现,web目录其实就是 Tomcat部署的web项目 的根目录!
五、Tomcat 原理分析
1、Tomcat 的组成
如下图:
Server: Tomcat 封装的、对外提供完整的、基于组件的 web 服务,包含 Connectors、Container 两个核心组件,以及多个功能组件,各个 Service 之间是独立的,但是共享 同一 JVM 的资源;
Connector: Tomcat 与外部世界的连接器,监听固定端口接收外部请求,传递给 Container,并将 Container 处理的结果返回给外部;
Container: Catalina,Servlet 容器,内部有多层容器组成,用于管理 Servlet 生命周期,调用 servlet 相关方法。
Loader: 封装了 Java ClassLoader,用于 Container 加载类文件;
Realm: Tomcat 中为 web 应用程序提供访问认证和角色管理的机制;
JMX: Java SE 中定义技术规范,是一个为应用程序、设备、系统等植入管理功能的框架,通过 JMX 可以远程监控 Tomcat 的运行状态;
Jasper: Tomcat 的 Jsp 解析引擎,用于将 Jsp 转换成 Java 文件,并编译成 class 文件。
Session: 负责管理和创建 session,以及 Session 的持久化(可自定义),支持 session 的集群。
Pipeline: 在容器中充当管道的作用,管道中可以设置各种 valve(阀门),请求和响应在经由管 道中各个阀门处理,提供了一种灵活可配置的处理请求和响应的机制。
Naming: 命名服务,JNDI, Java 命名和目录接口,是一组在 Java 应用中访问命名和目录服务的 API。命名服务将名称和对象联系起来,使得我们可以用名称访问对象,目录服务也是一种命名服务,对象不但有名称,还有属性。Tomcat 中可以使用 JNDI 定义数据源、配置信息,用于开发与部署的分离。
Container的组成:
Engine: Servlet
的顶层容器,包含一 个或多个 Host 子容器;
Host: 虚拟主机,负责 web 应用的部 署和 Context 的创建;
Context: Web 应用上下文,包含多个 Wrapper,负责 web 配置的解析、管理所有的 Web 资源;
Wrapper: 最底层的容器,是对 Servlet 的封装
,负责 Servlet 实例的创建、执行和销毁。
2、Tomcat 的启动流程
1、启动从 Tomcat 提供的 start.sh 脚本开始
我们点击这个:
shell 脚本会调用 Bootstrap 的 main
方法,实际调用了 Catalina
相应的 load、start 方法。
2、解析配置文件
然后 load 等方***读取 conf 文件夹下的配置文件:
在解析的过程中会根据 xml 中的关系 和配置信息来创建容器
,并设置相关的属性。接着 Catalina 会调用 StandardServer 的 init 和 start 方法进行容器的初始化和启动。
3、生命周期
按照 xml 的配置关系,server 的子元素是 service,service 的子元素是顶层容器 Engine,每层容器有持有自己的子容器,而这些元素都实现了生命周期管理的各个方法,因此就很容易的完成整个容器的启动、关闭等生命周期的管理。
4、监听端口
StandardServer 完成 init 和 start 方法调用后,会一直监听来自 8005
端口(可配置),如果接收到 shutdown 命令,则会退出循环监听,执行后续的 stop 和 destroy 方法,完成 Tomcat 容器的 关闭。同时也会调用 JVM 的 Runtime.getRuntime() .addShutdownHook
方法,在虚拟机意外退出的时候来关闭容器。
5、ContainerBase
所有容器都是继承自 ContainerBase
,基类中封装了容器中的重复工作,负责启动容器相关的组 件 Loader
、Logger
、Manager
、Cluster
、Pipeline
,启动子容器(线程池并发启动子容器,通过 线程池 submit 多个线程,调用后返回 Future 对象,线程内部启动子容器,接着调用 Future 对象 的 get 方法来等待执行结果)。
3、Web 应用的部署方式
catalina.home 是 安装目录
catalina.base 是 工作目录 即 webapps
Server.xml:
- 配置 Host 元素;
- 指定 appBase 属性;
- 配置 Context 元素;
- 指定 docBase 元素;
- 指定 web 应用的路径
HostConfig 监听了 StandardHost
容器的事件,在 start 方法中解析上述配置文件:
-
扫描并解析 webapps 下的所有文件夹和 war 包,解析各个应用的
META-INF / context.xml
,并创建StandardContext
,并将 Context 加入到 Host 的子容器中。
ContextConfig 解析 context.xml 顺序: -
先解析全局的配置
config/context.xml
-
然后解析 Host 的默认配置
EngineName/HostName/context.xml.default
-
最后解析应用的
META-INF/context.xml
ContextConfig 解析 web.xml 顺序: -
先解析全局的配置
config/web.xml
-
然后解析 Host 的默认配置
EngineName/HostName/web.xml.default
接着解析应用的WEB-INF/web.xml
-
扫描应用
WEB-INF/lib/
下的 jar 文件,解析其中的META-INF/web-fragment.xml
最后合并 xml 封装成WebXml
,并设置 Context
4、Servlet
Servlet 是用 Java 编写的服务器端程序
。其主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。
它由 wrapper
封装;生命周期如下:
- 请求到达 server 端,server 根据 url 映射到相应的 Servlet
- 判断 Servlet 实例是否存在,不存在则加载和实例化 Servlet 并调用 init 方法
- Server 分别创建 Request 和 Response 对象,调用 Servlet 实例的 service 方法(service 方法内部会根据 http 请求方法类型调用相应的 doXXX 方法)
- doXXX 方法内为业务逻辑实现,从 Request 对象获取请求参数,处理完毕之后将结果通过 response 对象返回给调用方
- 当 Server 不再需要 Servlet 时(一般当 Server 关闭时),Server 调用 Servlet 的 destroy() 方 法。
请求处理过程:
- 根据
server.xml
配置的指定的 connector 以及端口监听 http、或者 ajp 请求 - 请求到来时建立连接,解析请求参数,创建 Request 和 Response 对象,调用顶层容器 pipeline 的 invoke 方法
- 容器之间层层调用,最终调用业务 servlet 的 service 方法
- Connector 将 response 流中的数据写到 socket 中
Pipeline 与 Valve :
Pipeline 可以理解为现实中的管道,Valve 为管道中的阀门,Request 和 Response 对象在管道中 经过各个阀门的处理和控制。
- 每个容器的管道中都有一个必不可少的
basic valve
,其他的都是可选的,basic valve 在管道中最 后调用,同时负责调用子容器的第一个 valve。 - 各层容器对应的 basic valve 分别是
StandardEngineValve
、StandardHostValve
、StandardContextValve
、StandardWrapperValve
。
5、JSP 引擎
JSP 生命周期:
- 编译阶段:servlet 容器编译 servlet 源文件,生成 servlet 类
- 初始化阶段:加载与 JSP 对应的 servlet 类, 创建其实例,并调用它的初始化方法
- 执行阶段:调用与 JSP 对应的 servlet 实例的 服务方法
- 销毁阶段:调用与 JSP 对应的 servlet 实例的 销毁方法,然后销毁 servlet 实例
如图,JSP文件会被解析为 servlet
类。
这是随便拿的一个栗子,将上面的文件打开,可以看到有一部分文件正是将HTML标签
写入到页面上。
六、总结
看完了之后是不是感觉 Tomcat 不是那么神奇了?甚至感觉自己可以手写一个 Tomcat?
对,其实他就是一个服务器软件,我们在浏览器输入一个地址,完了之后能访问到页面,这里面 Tomcat 为我们做了很多工作,比如这个地址:
http://localhost:8080/abcd/index.jsp
模拟一下浏览器访问哈:
- 首先由
localhost
找到了对应的主机,也就是服务器; - 然后由
8080
找到了对应的应用程序,也就是 Tomcat ; - 通过
abcd
可以找到当前 Tomcat 下面部署的项目; - 最后那个
index.jsp
就是资源名称。