Tomcat简介

Tomcat 服务器Apache软件基金会项目中的一个核心项目,是一个免费开源的轻量级 Web 应用服务器,在中小型系统和并发访问用户不大的场合下被普遍使用,是开发和调试 JSP 程序的首选。

Tomcat类加载

Tomcat整体的类加载图:

image

上面这张图中看到很多类加载器,除了Jdk自带的类加载器,我们尤其关心Tomcat自身持有的类加载器。仔细一点我们很容易发现:Catalina类加载器和Shared类加载器,他们并不是父子关系,而是兄弟关系。为啥这样设计,我们得分析一下每个类加载器的用途,才能知晓。

  • Common类加载器,负责加载Tomcat和Web应用的通用类

  • Catalina类加载器,负责加载Tomcat专用的类,而这些被加载的类在Web应用中将不可见

  • Shared类加载器,负责加载Tomcat下所有的Web应用程序的类,而这些被加载的类在Tomcat容器将不可见

  • WebApp类加载器,负责加载具体的某个Web应用程序的类,而这些被加载的类在Tomcat和其他的Web应用程序都将不可见

  • Jsp类加载器,每个jsp页面一个类加载器,不同的jsp页面有不同的类加载器,方便实现jsp页面的热插拔

Tomcat整体的类加载流程图:
image

双亲委派模型:如果收到一个类加载的请求,本身不会先加载此类,而是会先将此请求委派给父类加载器去完成,每个层次都是如此,直到启动类加载器中,只有父类都没有加载此文件,那么子类才会尝试自己去加载。

双亲委派模型的好处:保证核心类库不被覆盖。如果没有使用双亲委派模型,由各个类加载器自行加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的ClassPath中,那系统将会出现多个不同的Object类, Java类型体系中最基础的行为就无法保证。应用程序也将会变得一片混乱。

那么Tomcat为什么要自定义类加载器呢?

  • 隔离不同应用:部署在同一个Tomcat中的不同应用A和B,例如A用了Spring2.5。B用了Spring3.5,那么这两个应用如果使用的是同一个类加载器,那么Web应用就会因为jar包覆盖而无法启动。

  • 隔离服务器与不同应用:服务器需要尽可能地保证自身的安全不受部署的Web应用程序影响。一般来说,服务器所使用的类库应该与应用程序的类库互相独立。

  • 性能:部署在同一服务上的两个Web应用程序所使用的Java类库可以互相共享。

Tomcat自定义了WebAppClassLoader类加载器。打破了双亲委派的机制,即如果收到类加载的请求,会尝试自己去加载,如果找不到再交给父加载器去加载,目的就是为了优先加载Web应用自己定义的类。我们知道ClassLoader默认的loadClass方法是以双亲委派的模型进行加载类的,那么Tomcat既然要打破这个规则,就要重写loadClass方法,我们可以看WebAppClassLoader类中重写的loadClass方法。

Web应用默认的类加载顺序(打破了双亲委派规则)

  • 1、从本地缓存中查找是否加载过此类,如果已经加载即返回,否则继续下一步。

  • 2、检查 JVM 的缓存中是否已经加载,防止Web应用覆盖JRE的核心类

  • 3、从AppClassLoader中查找是否加载过此类,如果加载到即返回,否则继续下一步。

  • 4、判断是否设置了delegate属性,如果设置为true那么就按照双亲委派机制加载类

  • 5、默认是设置delegate是false的,那么就会先用WebAppClassLoader进行加载

  • 6、如果此时在WebAppClassLoader没找到类,那么就委托父类加载器(Common ClassLoader)去加载

Tomcat的基本架构是什么?

Tomcat基本架构图:
image

  • Tomcat中只有一个Server,一个Server可以有多个Service,一个Service可以有多个Connector和一个Container;

  • Server掌管着整个Tomcat的生死大权;

  • Service 是对外提供服务的;

  • Connector用于接受请求并将请求封装成Request和Response来具体处理;

  • Container用于封装和管理Servlet,以及具体处理request请求

Tomcat请求的处理流程

给定一个客户端访问的URL:http://localhost:8080/TestWeb/index.jsp.详细说一下该请求的处理流程.


  • 1.首先是请求发送给本机8080,被在那里监听HTTP/Connector获得.

  • 2.Connector将该请求发给它本身所在的 Service所在的Engine来处理,并等待Engine来回应.

  • 3.Engine匹配对应的Host和Context,并将请求交给对应的Servlet

  • 4.构造HttpServletRequest对象和HttpServletResponse对象作为参数,调用Servlet的doGet()方法和doPost()方法.

  • 5.Context把执行完后的HttpServletResponse对象返回给Host,再返回给Engine,Connector

  • 6.Connector把HttpServletResponse对象返回给浏览器的Browser.