Tomcat基础

一.什么是Tomcat?

1.tomcat是一个由Java编写的程序,启动tomcat,其实就是启动BootStrap 中的main方法(任何java程序的启动入口都是main方法);
2.tomcat是一个web服务器软件,用于部署web包/jar包,让客户端能够访问部署在tomcat中的web包/jar包中的静态资源及动态资源;
3.我们常说的servlet技术,filter技术, IO模型(NIO和BIO), 应用层协议(HTTP/HTTPS),都是在tomcat中实现的;
4.关键类:Servlet、ServletContent、ServletConfig、Cookie、Session、Filter

二.Tomcat的目录

目录目录下文件说明 bin / 存放Tomcat的启动、停止等批处理脚本文件 startup.bat , startup.sh 用于在windows和linux下的启动脚本 shutdown.bat , shutdown.sh 用于在windows和linux下的停止脚本 conf / 用于存放Tomcat的相关配置文件 Catalina 用于存储针对每个虚拟机的Context配置 context.xml 用于定义所有web应用均需加载的Context配 置,如果web应用指定了自己的context.xml ,该文件将被覆盖 catalina.properties Tomcat 的环境变量配置 catalina.policy Tomcat 运行的安全策略配置 logging.properties Tomcat 的日志配置文件, 可以通过该文件修 改Tomcat 的日志级别及日志路径等 server.xml Tomcat 服务器的核心配置文件 tomcat-users.xml 定义Tomcat默认的用户及角色映射信息配置 web.xml Tomcat 中所有应用默认的部署描述文件, 主 要定义了基础Servlet和MIME映射。 lib / Tomcat 服务器的依赖包 logs / Tomcat 默认的日志存放目录 webapps / Tomcat 默认的Web应用部署目录 work / Web 应用JSP代码生成和编译的临时目录

三.为什么说Tomcat是个容器?

Tomcat 本质上就是一款 Servlet 容器;不同的Servlet 处理不同的请求;
在这里插入图片描述
在这里插入图片描述

Tomcat 本质上就是一款 Servlet 容器, 因此Catalina 才是 Tomcat 的核心 , 其他模块
都是为Catalina 提供支撑的。 比如 : 通过Coyote 模块提供链接通信,Jasper 模块提供
JSP引擎,Naming 提供JNDI 服务,Juli 提供日志服务。

在这里插入图片描述
Catalina 各个组件的职责:
在这里插入图片描述
server.xml中的配置也可以体现server下的层级结构:
1个server代表1个tomcat服务,一般server只有1个;
1个server中可以配置多个service;
1个service中可配置多个Connector连接器;
1个service中可配置多个容器Container;
Container包括:是Engine、Host、Context和Wrapper。这4种容器不是平
行关系,而是父子关系。, Tomcat通过一种分层的架构,使得Servlet容器具有很好的灵活性。

四.Tomcat 启动流程

在这里插入图片描述

步骤 :
1) 启动tomcat , 需要调用 bin/startup.bat (在linux 目录下 , 需要调用 bin/startup.sh), 在startup.bat 脚本中, 调用了catalina.bat。
2) 在catalina.bat 脚本文件中,调用了BootStrap 中的main方法。
3)在BootStrap 的main 方法中调用了 init 方法 , 来创建Catalina 及 初始化类加载器。
4)在BootStrap 的main 方法中调用了 load 方法 , 在其中又调用了Catalina的load方法。
5)在Catalina 的load 方法中 , 需要进行一些初始化的工作, 并需要构造Digester 对象, 用于解析 server.xml,解析出Server对象。
6)在调用server的init方法,然后一级一级往下调用init方法,对各个组件进行初始化。

其中在Connector初始化的时候会根据server.xml配置的 Connector标签中的 protocol属性,生成ProtocolHandler协议处理器,ProtocolHandler具体实现类有Http11NioProtocol、Http11Nio2Protocol、AjpNioProtocol等,这些具体实现类的构造方法中回去创建AbstractEndpoint的实现类,AbstractEndpoint类的具体实现有NioEndpoint、Nio2Endpoint等;

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               maxParameterCount="1000"
               />

7) init完之后,会调用 Catalina的start方法,来一级级启动各个组件,启动到ProtocolHandler中的endpoint 时,会去创建线程池,开启socket的监听,时刻准备接收客户的请求;

五.Tomcat 支持的的IO模型和应用层协议

Tomcat 支持的IO模型(在 8.0 之前 , Tomcat 默认采用的I/O方式为 BIO,自8.5/9.0 版本起,Tomcat 移除了 对 BIO 的支持):
在这里插入图片描述
Tomcat支持的应用层协议 :
在这里插入图片描述

六.Tomcat中的Jasper 简介

原理总结:
Jsp文件其实就是一个servlet,用户在访问XXX.jsp资源时,会映射到web.xml中配置的JspServlet,通过JspServlet找到对应的.jsp文件进行编译,编译好之后将请求交给其处理,servle会对每一行的静态内容(HTML) , 调用 out.write 输出。所以反馈给用户看到的其实是一个HTML页面;

Jasper 即Jsp引擎,对于基于JSP 的web应用来说,我们可以直接在JSP页面中编写 Java代码,添加第三方的标签库,以及使用EL表达式。但是无论经过何种形式的处理,最终输出到客户端的都是
标准的HTML页面(包含js ,css…),并不包含任何的java相关的语法。 也就是说, 我
们可以把jsp看做是一种运行在服务端的脚本。 那么服务器是如何将 JSP页面转换为
HTML页面的呢?
Jasper模块是Tomcat的JSP核心引擎,我们知道JSP本质上是一个Servlet。Tomcat使用
Jasper对JSP语法进行解析,生成Servlet并生成Class字节码,用户在进行访问jsp时,会
访问Servlet,最终将访问的结果直接响应在浏览器端 。另外,在运行的时候,Jasper还
会检测JSP文件是否修改,如果修改,则会重新编译JSP文件。
JSP 编译方式:
1.运行时编译
Tomcat 并不会在启动Web应用的时候自动编译JSP文件, 而是在客户端第一次请求时,
才编译需要访问的JSP文件。
2.编译过程:
Tomcat 在默认的web.xml 中配置了一个org.apache.jasper.servlet.JspServlet,用于处
理所有的.jsp 或 .jspx 结尾的请求,该Servlet 实现即是运行时编译的入口。
3.编译结果:
1) 如果在 tomcat/conf/web.xml 中配置了参数scratchdir , 则jsp编译后的结果,就会
存储在该目录下 。

在这里插入图片描述
2) 如果没有配置该选项, 则会将编译后的结果,存储在Tomcat安装目录下的
work/Catalina(Engine名称)/localhost(Host名称)/Context名称 。 假设项目名称为
jsp_demo
4.预编译:
除了运行时编译,我们还可以直接在Web应用启动时, 一次性将Web应用中的所有的JSP
页面一次性编译完成。在这种情况下,Web应用运行过程中,便可以不必再进行实时编
译,而是直接调用JSP页面对应的Servlet 完成请求处理, 从而提升系统性能。
Tomcat 提供了一个Shell程序JspC,用于支持JSP预编译,而且在Tomcat的安装目录下提
供了一个 catalina-tasks.xml 文件声明了Tomcat 支持的Ant任务, 因此,我们很容易使
用 Ant 来执行JSP 预编译 。(要想使用这种方式,必须得确保在此之前已经下载并安装
了Apache Ant)。

七.Tomcat支持HTTPS

1) 生成秘钥库文件。

keytool ‐genkey ‐alias tomcat ‐keyalg RSA ‐keystore tomcatkey.keystore

输入对应的密钥库密码, 秘钥密码等信息之后,会在当前文件夹中出现一个秘钥库文件:tomcatkey.keystore
2) 将秘钥库文件 tomcatkey.keystore 复制到tomcat/conf 目录下。
3) 配置tomcat/conf/server.xml

<Connector port="8443"
protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" schema="https" secure="true" SSLEnabled="true">
<SSLHostConfig certificateVerification="false">
<Certificate
certificateKeystoreFile="D:/DevelopProgramFile/apache‐tomcat‐8.5.42‐
windows‐x64/apache‐tomcat‐8.5.42/conf/tomcatkey.keystore"
certificateKeystorePassword="itcast" type="RSA" />
</SSLHostConfig>
</Connector>

4)访问Tomcat ,使用https协议。

八.Tomcat 性能优化

1.JVM内存参数:
找到Tomcat根目录下的bin目录,设置catalina.sh文件中JAVA_OPTS变量即可
2.Tomcat性能调优:
找到Tomcat根目录下的conf目录,修改server.xml文件的内容
在这里插入图片描述

 <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               maxParameterCount="1000"
               />

九.Tomcat是如何启动SSM项目的(spring及springmvc的bean加载我们都知道是在项目启动中就完成的)?

tomcat在一启动时会去加载web.xml

<!-- spring容器一加载就读取的配置文件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationcontext.xml,classpath:spring-security.xml</param-value>
  </context-param>
  <!-- LOG4J2的配置文件 -->
  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j2.xml</param-value>
  </context-param>
  <!-- 一个小时检查一次日志配置文件 -->
  <context-param>
    <param-name>log4jRefreshInterval</param-name>
    <param-value>3600000</param-value>
  </context-param>
   
     <!--ContextLoaderListener 实现 ServletContextListener接口
        ServletContextListener监听ServletContext对象创建与销毁
        ServletContext对象创建时机?
          服务器启动创建

        ContextLoaderListener在服务器启动时就执行内部的初始化方法
        在该初始化方法内部做什么?spring容器的创建 创建容器时 加载spring的配置文件
    -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

1.tomcat先启动spring容器
tomcat在启动ServletContext容器的时候会发布ServletContextEvent事件。web.xml中设置的监听器ContextLoaderListener实现了ServletContextListener接口,对ServletContext启动监听,在initWebApplicationContext方法中:
1.会生成web容器(XmlWebApplicationContext),并调用refresh()方法,根据web.xml中配置的contextConfigLocation中配置的spring的配置文件扫描包路径,加载bean(SpringIOC容器完成启动);
2.通过ServletContext获取上面配置的contextConfigLocation参数将其赋值给web容器对象;
3.将创建web容器对象,赋值给servletContext对象的 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性中@11

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

		servletContext.log("Initializing Spring root WebApplicationContext");
		Log logger = LogFactory.getLog(ContextLoader.class);
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			//创建web容器XmlWebApplicationContext
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					
					if (cwac.getParent() == null) {
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					//执行web容器的refresh()方法
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			//将web容器对象,赋值给servletContext对象的 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性中
	servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
			}

			return this.context;
		}
		catch (RuntimeException | Error ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
	}

2.tomcat是如何启动SpringMVC的?
SpringIOC容器启动之后,再启动SpringMVC容器;

tomcat配置文件web.xml中的配置了DispatcherServlet,Tomcat中默认情况下只在请求匹配到Servlet后才会初始化Servlet(也就是项目启动后第一次进行请求的时候,如果想让项目启动时就初始化Servlet,则配置load-on-startup属性为1),初始化时会调DispatcherServlet的父类HttpServletBean中的init()方法,init()方法中就实现了SpringMVC的整个启动流程。下面看下源码:
web.xml配置:



 <!--配置DispatcherServlet 核心其实就是一个servlet  重点: springmvc的前端控制器-->
  <servlet>
    <servlet-name>SpringMVCCore</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <!-- 配置springmvc的配置文件 如果不配置 默认加载/WRB_INF/servlet名称—serlvet.xml-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:SpringMVC.xml</param-value>
    </init-param>
    <!-- 配置 servlet 的对象的创建时间点:应用加载时创建。
     取值只能是非 0 正整数,表示启动顺序 -->
    <load-on-startup>1</load-on-startup>
 </servlet>

在HttpServletBean中实现了init()方法->initServletBean()->org.springframework.web.servlet.FrameworkServlet#initServletBean:

if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}

在SpringMVC容器初始化的时候,如果有springIOC容器,会将SpringIOC容器设置为mvc的父容器:
springIOC容器的获取是从ServletContext对象的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性中获取,详见@11处

if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}

在这里插入图片描述

正是因为tomcat是先启动spring容器在启动springmvc容器的,所以springmvc容器中的controller实例再初始化时所依赖的service已经存在。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值