文章目录
- 1、启动阶段
- 2、监听阶段:
- 3、请求处理阶段:
- 4、发送请求处理后的响应
当涉及到Java Web应用程序的部署和运行,Apache Tomcat无疑是一个备受欢迎的选择。Tomcat作为一个开源的、轻量级的Java Servlet容器和JavaServer Pages (JSP) 容器,扮演着连接用户和应用程序的重要角色。本篇文将向大家阐述Tomcat的执行流程,从启动到请求处理再到关闭,帮助您更好地理解Tomcat在Java Web开发中的作用。
1、启动阶段
在启动阶段,他会扫描webapp目录下的所有子目录和子文件,然后把这个目录下的 .class 文件挑选出来,拿到每个java类的类路径(全限定名),然后for循环遍历这些类路径,通过反射,得到相应的类对象,然后从Class类信息里去获取注解信息,拿到注解信息之后看哪些java的类信息里面有@WebServlet注解,把这些java文件挑选出来。通过newInstance() 生成类的实例,它代替了new操作,为什么这些环节没办法使用直接new的方式而是要用newInstance() 呢,使用 new 的前提是要知道类的名字和它的包路径,很可惜tomcat的开发者事先不知道使用者创建的 servlet 叫什么名字以及所在包是什么,所以 new是不能使用的,才有了newInstance() 的方式生成实例。 生成servlet实例的时候,又进一步通过方法的实例拿到了它里面的 method实例,method实例里面挑选出 doGet 和 doPost实例,同时上面也拿到了注解里的路径值,然后把它们放到了 hashMap 中,其中 key值就是注解里的路径,value值就是对象实例信息。除了method实例,servlet实例也存在hashMap中,method实例和servlet实例对应的key值都是注解的路径。
2、监听阶段:
- Tomcat根据配置的Connector信息创建并启动监听网络连接的组件,如HTTP Connector。
Tomcat进入等待状态,开始监听来自客户端的HTTP请求。
3、请求处理阶段:
当有HTTP请求到达时并不是立即开启线程处理的,而是把它放入线程池中。每个线程在工作时,首先把通过端口传过来的 http字符信息封装成 httpRequest 对象和 httpResponce对象,同时提取出请求的 URL ,把URL中的 ip、端口、项目名字去掉剩下的就是要请求的servlet地址或前端地址。如果请求的是前端资源那么会根据请求路径去相应的目录下找前端文件,之后按照相应的编码读取里面的字符串返回给前端;如果请求的是servlet 那么会根据URL去之前启动阶段的 hashMap里匹配Class对象,提取出来之后执行代理。tomcat通过invoke()方法完成了 servlet的调用。
4、发送请求处理后的响应
在业务逻辑处理完成后,Servlet或JSP生成HTTP响应,包括状态码、响应头和响应体。这些内容被封装在HTTP响应对象中,返回给客户端。
Tomcat在启动时,
会先加载配置文件:
读取配置文件中的参数如服务器的端口、线程池核心线程数、最大线程数、非核心线程存活时间、存活时间单位、每个线程的前缀名等。我将这些参数以参数名为key,参数值为value,保存在一个 map集合中,用于创建连接器和线程池时做配置。
创建连接器:
根据配置文件中的连接器设置,Tomcat会创建连接器,这些连接器负责监听指定的端口,接收来自客户端的HTTP请求。
创建线程池:
线程池的参数我在手写时放在了一个单独的线程池工具类中,每个属性都初始了默认值,定义一个无参构造器,如果配置文件中声明了参数的值,那么对应属性就使用配置文件里参数的值,否则还使用默认值.
然后初始化servlet容器:
先将项目中所有以.java结尾的文件扫描出来,保存每个java文件的全限定名。然后遍历这些全限定名,每遍历一个全类名,先通过Class.forName(“全限定名”)反射获得类对象,得到类对象我们就可以获得该类的所有类信息,然后判断这个类是否标记了@WebServlet注解,如果标记了这个注解,通过newInstance() 生成servlet实例(因为事先不知道开发人员创建的servlet叫什么以及所在包是什么)
以@WebServlet注解中路径为Key,servlet实例为value put进一个装路径和servlet实例的hashMap中
生成servlet实例的时候,又进一步通过方法的实例拿到了它里面的 method实例,method实例里面挑选出 doGet 和 doPost实例,同时上面也拿到了注解里的路径值,然后把它们放到了装方法实例的 hashMap 中,其中 key值就是注解里的路径,value值就是对象实例信息。
遍历完成后,servlet容器初始化完成。
接下来是请求转发阶段:
tomcat负责接收来自网络的请求,通过socket监听端口.当接收到HTTP请求后,并不是立即开启线程去处理请求,而是把请求放入线程池中.由里面的线程去处理.
每个线程的工作是,首先把通过端口传过来的 http字符信息封装成 httpRequest 对象和 httpResponse对象,同时提取出请求的 URL ,把URL中的 ip、端口、项目名字去掉剩下的就是要请求的servlet地址或前端地址。如果请求的是前端资源那么会根据请求路径去相应的目录下找前端文件,之后按照相应的编码读取里面的字符串返回给前端;如果请求的是servlet 那么会根据URL去之前启动阶段的 hashMap里匹配 servlet实例和 method对象,提取出来之后执行代理, method.invoke(obj,HttpServletRequest, HttpServletResponse)
tomcat通过invoke()方法完成了 servlet的调用。doGet() 和doPost() 方法都是void方法没有返回值,统一将返回内容写入httpResponse对象中,然后返回给浏览器.