0.Tomcat源码构建
下载导入源码
下载完毕源码后导入idea直接运行BootStrap程序。
BootStrap程序启动参数
VM虚拟机处理参数:
-Dcatalina.home=D:\JavaProject\apache-tomcat-8.5.78-src\source
-Dcatalina.base=D:\JavaProject\apache-tomcat-8.5.78-src\source
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=D:\JavaProject\apache-tomcat-8.5.78-src\source\conf\logging.properties
添加对Jasper的解析程序
class ContextConfig{
configureStart(){
context.addServletContainerInitializer(new JasperInitializer(), null);
}
}
项目部署到Tomcat服务器的三种方式:
- 打war包放入到webapps目录下
- 直接编译好的应用程序放在,webapps目录下
- 在server.xml中添加Context标签,通过docBase指定项目路径进行部署
1.Tomcat整体架构和处理请求流程解析
参考链接:Tomcat 整体架构分析 - 程序员自由之路
Pipeline
一个管道里面有多个阀门List
Engine
用于管理HostList
hosts;
Pipeline pipe;List
Host
虚拟主机:通过域名进行区分List
contexts
Pipeline pipe;List
Context
List
servlets;
Pipeline pipe;List
;
Wapper --- servlet类 --- servlet
一个Wapper对应一个Servlet类,一个Servlet类对应多个Servlet实例List
servlets;
Pipeline pipeline --- valve..servlet.service(reqeustFacade, responseFacade);
自定义阀门测试
public class TestValue extends RequestFilterValve{
@Override
public void invoke(Request request, Response response) throws IOException, ServletException{
System.out.println("test valve");
getNext().invoke(request, response);
}
@Override
protected Log getLog(){ return null; }
}
配置:在server.xml中Host下添加一个Valve标签,访问该虚拟主机的所有请求都会被Valve配置的类处理。
Filter
class FilterDemo extend Filter{
@Override
public void doFilter(servletRequest, servletResponse, filterChain){
//...
filterChain.doFilter();
//...
}
}
TCP
由操作系统(Windows, Linux, MacOS)实现TCP协议
优点:可靠,安全,数据不丢失
同一台主机或不同主机不同应用程序之间产生的数据,可能需要传输
TCP数据交换模式:
- BIO(Tomcat7默认使用)
- NIO(Http11NioProtocol这个类处理NIO情况)
- APR----Aapche, 10(Apache组织实现的一个类似于NIO的模型)
TCP连接三次握手程序:
操作系统(Linux)中的源码文件中tcp_output.c文件
- A---SYN--->B
- A<---SYN_ACK---B
- A---ACK--->B
请求--->Request
Tomcat:8080
数据--->Request
服务器A--->服务器B
数据+IP
应用程序
1.生成程序
2.建立TCP连接---->Java---->TCP
3.发送数据
TCP协议----传输----操作系统----tcp_connect()
Http协议---->应用层协议---->数据的格式(使用什么方式传输数据不关心)
Http
socket----Http协议----解析---->Request
浏览器发送一个Post请求,到客户段接收到请求的流程:
- 按照http协议将Post请求生成HTTP请求协议格式的文本,HTTP协议文本格式参考:HTTP协议格式详解
- 浏览器作为应用程序没有发送数据的能力,数据发送由操作系统实现TCP协议来完成。
- 操作系统实现TCP协议于服务器建立连接,通过三次握手。
- 通过Socket接口将数据发送过去。
- 服务器通过Socket接口接收客户端发送的数据,判断数据格式,根据协议(https)将数据解析成Request对象,方便对数据内容进行操作。
Tomcat整体架构和处理请求流程解析:
1.接收到socket
2.将socket交给线程池
3.一个线程处理一个socket连接
4.开始从socket中获取数据
5.解析请求行
6.解析请求头
7.将解析过的数据放入到request中
8.根据请求头解析Connection对应的值是keepalive还是close
9.将Request对象交给容器进行处理
10.容器最终会交给对应的servelt进行处理
11.servlet中可以获取请求各种信息,包括获取请求体
12.servlet中也可以使用response对象向客户端返回响应---复杂
13.servlet中的代码都执行完成后,相当于容器中已经处理完了请求,相当于请求的核心逻辑已经执行完了
14.处理InputBuffer中的pos和lastValid,以便能够处理下一个请求
Request
method
url
header
应用层:
RequestFacade ----> connector.Request---->
底层:框架内部使用
coyote.Request
method
uri
2.Tomcat中关于长连接的底层原理与源码实现
3.Tomcat中关于请求处理的原理与源码实现
4.Tomcat中分块传输与响应请求的原理与源码实现
5.Tomcat中使用nio模型处理请求原理与源码实现
6.Tomcat中自定义加载器的使用与源码实现
6.1 类加载器
Java中的类遵循按需加载。
所谓类加载器,就是用于加载Java类到Java虚拟机中的组件,它负责读取Java字节码,并转换成java.lang.Class类的一个实例,使字节码.class文件得以运行。一般类加载器负责根据一个指定的类找到对应的字节码,然后根据这些字节码定义一个Java类。另外,它还可以加载资源,包括图像文件和配置文件。
类加载器在实际使用中给我们带来的好处是,它可以使Java类动态地加载到JVM中并运行,即可在程序运行时再加载类,提供了很灵活的动态加载方式。
- 启动类加载器(Bootstap ClassLoader): 加载对象是Java核心库,把一些核心的Java类加载进JVM中,这个加载器使用原生代码(C/C++)实现,并不是继承java.lang.ClassLoader,它是所有其他类加载器的最终父加载器,负责加载
/jre/lib目录下JVM指定的类库 ,其实它属于JVM整体的一部分,JVM一启动就将这些指定的类加载到内存中,避免以后过多的I/O操作,提高系统的运行效率,启动类加载器无法被Java程序直接使用。 - 扩展类加载器(Extension ClassLoader): 加载的对象为Java的扩展库,即加载
/jre/lib/ext目录里面的类。 这个类又启动类加载器加载,但因为启动类加载器并非用Java实现,已经脱离了Java体系,所以如果尝试调用扩展类加载器的getParent()方法获取父类加载器会得到null。然而,它的父类加载器是启动类加载器。 - 应用程序类加载器(Application ClassLoader): 亦叫系统类加载器(System ClassLoader),它负责加载用户类路径(CLASSPATH)指定的类库,如果程序没有自己定义类加载器,就默认使用应用程序类加载器,它也由启动类加载器加载,但它的父加载类被设置成了扩展类加载器。如果要使用这个加载器,可通过ClassLoader.getSystemClassLoader()获取。
a.类加载器处于的位置
b.双亲委派
双拼委派模型会在类加载器加载类时首先委托给父类加载器,除非父类加载器不能加载才自己加载。
c.应用程序启动参数加载
"C:\Program Files\Java\jdk-11.0.6\bin\java.exe"
"-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2.4\lib\idea_rt.jar=51177:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2.4\bin"
-Dfile.encoding=UTF-8
-classpath
D:\JavaProject\apache-tomcat-8.5.78-src\target\classes;
C:\Users\water.m2\repository\org\easymock\easymock\3.4\easymock-3.4.jar;
C:\Users\water.m2\repository\org\objenesis\objenesis\2.2\objenesis-2.2.jar;
C:\Users\water.m2\repository\org\apache\ant\ant\1.7.0\ant-1.7.0.jar;
C:\Users\water.m2\repository\org\apache\ant\ant-launcher\1.7.0\ant-launcher-1.7.0.jar;
C:\Users\water.m2\repository\wsdl4j\wsdl4j\1.6.2\wsdl4j-1.6.2.jar;
C:\Users\water.m2\repository\javax\xml\jaxrpc-api\1.1\jaxrpc-api-1.1.jar;
C:\Users\water.m2\repository\org\eclipse\jdt\core\compiler\ecj\4.5.1\ecj-4.5.1.jar;
C:\Users\water.m2\repository\javax\xml\soap\javax.xml.soap-api\1.4.0\javax.xml.soap-api-1.4.0.jar
com.luban.initialize.demo.Test
d.ClassLoader源码
package java.lang;
public abstract class ClassLoader {
private final ClassLoader parent;
// class loader name
private final String name;
// Maps class name to the corresponding lock object when the current
// class loader is parallel capable.
// Note: VM also uses this field to decide if the current class loader
// is parallel capable and the appropriate lock object for class loading.
private final ConcurrentHashMap parallelLockMap;
public Class> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class> c = findLoadedClass(name);
//name对应的类没有被加载过
if (c == null) {
long t0 = System.nanoTime();
try {
//通过递归完成双亲委派模型
if (parent != null) {
//存在父加载器,就优先使用父类加载器加载
c = parent.loadClass(name, false);
} else {
//ExtClassLoader的父类为BootstrapClassLoader
//但是ExtClassLoader属性parent中的值为null
//ExtClassLoader无法得到BootstrapClassLoader
//如果父类加载器为null,使用BootstrapClassLoader进行加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//找不到,直接抛异常
c = findClass(name);
// this is the defining class loader; record the stats
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
//给个className对应一个锁,保证不会被重复加载
protected Object getClassLoadingLock(String className) {
Object lock = this;
if (parallelLockMap != null) {
Object newLock = new Object();
//如果parallelLockMap中存在className返回对应的value,不存在添加进去
lock = parallelLockMap.putIfAbsent(className, newLock);
if (lock == null) {
lock = newLock;
}
}
return lock;
}
protected final Class> findLoadedClass(String name) {
if (!checkName(name)) //判断名字是否正常
return null;
return findLoadedClass0(name);
}
private final native Class> findLoadedClass0(String name);
protected Class> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
}
e.自定义类加载器
package com.luban.initialize.demo;
import java.io.*;
public class MyClassLoader extends ClassLoader{
private String name;
public MyClassLoader(ClassLoader parent, String name){
super(parent);
this.name = name;
}
@Override
public String toString() {
return this.name;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
byte[] data1 = getBytes("C:\\Users\\water\\Desktop\\Person.class");
Class clazz = this.defineClass(name, data1, 0, data1.length);
return clazz;
}
@Override
public Class> loadClass(String name) throws ClassNotFoundException {
/**
* 重写loadClass方法会打破双亲委派模型。
*/
// return super.loadClass(name);
ClassLoader system = getSystemClassLoader();
Class clazz = null;
try{
clazz = system.loadClass(name);
}catch (Exception e){
}
if(clazz != null){
return clazz;
}
clazz = findClass(name);
/**
* 每个类默认继承java.lang.Object,所以会先加载java.lang.Object类,
* 在重写的findClass中并不会加载Object类,
* 会抛出异常java.lang.SecurityException: Prohibited package name: java.lang
* 不允许自定义的类加载器,去加载java.xxx下的类
*
* 先用系统类加载器加载目标类,加载不到,再使用自定义类加载器进行加载
*/
return clazz;
}
public static void main(String[] args) {
MyClassLoader loader1 = new MyClassLoader(MyClassLoader.class.getClassLoader(), "MyClassLoader1");
MyClassLoader loader2 = new MyClassLoader(MyClassLoader.class.getClassLoader(), "MyClassLoader2");
try{
//BootstrapClassLoader ---> ExtClassLoader ---> AppClassLoader ---> MyClassLoader
/**
* 如果AppClassLoader可以从ClassPath中加载到Person,就直接加载。
* 如果MyClassLoader不能够加载到Person,会调用ClassLoader的findClass(),会后去获取Person
* MyClassLoader中重写了findClass()中的方法,最终会调用MyClassLoader.findClass(),使用自定义逻辑加载指定Person。
*/
Class clazz1 = loader1.loadClass("com.luban.initialize.demo.Person");
Class clazz2 = loader2.loadClass("com.luban.initialize.demo.Person");
//应用程序保护,不允许自定义java.xxx包
// Class clazz = loader.loadClass("java.xxx");
System.out.println(clazz1.hashCode());
System.out.println(clazz2.hashCode());
System.out.println(clazz1.getClassLoader());
System.out.println(clazz2.getClassLoader());
//157627094
//932607259
//MyClassLoader1
//MyClassLoader2
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private byte[] getBytes(String path){
InputStream is = null;
byte[] data = null;
try{
File f = new File(path);
is = new FileInputStream(f);
data = new byte[(int)f.length()]; //创建合适文件大小的数组
is.read(data);
is.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
}
6.2 Tomcat自定义类加载器
类加载器之间的继承关系
Tomcat --- 自定义类加载器
应用1 --- 自定义类加载器 --- WebappClassLoader实例 --- 目录
com.luban.Test
应用2 --- 自定义类加载器 --- WebappClassLoader实例 --- set目录 --- loadClass()
com.luban.Test
应用3
Context
name.
WebappClassLoader classLoader = new WebappClassLoader();
classLoader.loader()