JSP 热部署 源码解析

概述

我们知道在开发工程的时候jsp文件是即修改即生效的。Tomcat jsp热部署的实现原理是每个JSP页面从上次访问到下次访问总是有默认几秒的缓存时间的,也就说并不是严格的即修改即生效,tocmat7默认是有4秒的缓存延迟的。这个默认的缓存延迟是在类 EmbeddedServletOptions 的 private intmodificationTestInterval = 4; 这个属性定义的。如果过了4秒缓存时间即失效,这个时候tomcat就会读取jsp的modified时间戳和work目录下编译好的class文件的modified的时间戳作对比。如果相等则class文件没有过期,则不会重新编译jsp文件,如果过期了则重新将jsp编译成java,并进一步编译成class。同时创建一个新的JasperLoader来重新加载这个有jsp编译好的class文件。

热部署原理

一个class只能由classloader加载一次,如果再次加载将会导致类冲突。但是JVM表示一个类是否是同一个类有两个条件。

  1. 看这个类的完整类名是否一样(包名和类名)
  2. 加载这个类的ClassLoader是否是同一个,也就是说这个ClassLoader是否是同一个实例。

如果是同一个ClassLoader的不同的实例加载同一个类,那么就可以实现热更新部署了。


下面具体分析一下这个过程:

JSP文件请求时序图

JspServlet.serviceJspFile() 方法

  1. 判断当前jsp页面的JspServletWrapper对象是否存在,如果不存在,则创建并存放到 jsps缓存中。
private Map<String, JspServletWrapper> jsps = new ConcurrentHashMap<String, JspServletWrapper>();

2.调用JspServletWrapper.service() 方法

JspServletWrapper.service() 方法

  1. 判断该jsp是否删除
  2. 判断该 jsp 是否可用
  3. 判断当前项目是不是development模式运行,如果是则每次都执行JspCompilationContext.compile() 方法
  4. 判断是否是首次请求,如果首次请求则也执行JspCompilationContext.compile() 方法。

development 模式配置

JspServlet 模式是development模式,可以通过web.xml中把development模式关闭 development=false

JspCompilationContext.compile() 方法

  1. 根据class文件(或java文件,通过一个参数可以选择)的最后修改时间,判断文件是否更新过,如果更新过,
  2. 删除之前编译过的文件信息
  3. 把jspLoader置空,需要重新创建一个ClassLoader,来达到热加载的目的。
  4. 调用Compiler.compile()重新把jsp转换成servlet,并编译servlet成class文件。
  5. 把JspServletWrapper.reload 修改为true。后面getServlet() 根据这个参数判断是否重新加载该servlet。

Compiler.isOutDated()方法

判断上次请求的时候+4秒的时候,是否大于当前时间,也就是说,距离上次检查文件更新时间是否有4秒的时间,如果小于4秒则不进行检查文件是否更新,不重新加载编译jsp文件。

根据上次最后修改时间,和这次获得的文件最后修改时候做对比,来判断文件是否更新过。

ctxt.getOptions().getModificationTestInterval() 默认的时间为4秒

Compiler.compile() 方法

  1. 把jsp文件转换成java(servlet)文件
  2. 把java文件编译成class文件。

JspServletWrapper.getServlet() 方法

  1. 通过reload判断是否需要重新加载Servlet
  2. 如果需要重新加载,则先销毁之前的Servlet
  3. ctx.getJspLoader() 当前这个ClassLoader在上面已经赋值为null,在这个方法里又重新创建了一个Classloader实例
  4. 初始化新创建的Servlet
  5. 把reload赋值为 false

getJspLoader() 方法

调用Servlet.service() 方法

在JspServlet.serviceJspFile() 方法 获取Jsp对应的Servlet实例后,然后调用Servlet的Service方法。


本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员同行者

python3模块: os

1123
来自专栏芋道源码1024

精尽 Dubbo 原理与源码专栏( 已经完成 69+ 篇,预计总共 75+ 篇 )

本小节,我们将 《精尽 Dubbo 源码解析》 和 《Dubbo 用户指南》 做一次映射,方便大家直接找到感兴趣的功能的具体源码实现。当然,如果有整理不到位的地...

7573
来自专栏Python绿色通道

使用Python开发你的第一个服务器程序

声明:本文是用Py3.6版本,而且从此以后我的系列文章优先使用Py3.6版本,为什么说优先使用Py3.6版本呢?因为有的时候Py3.6版本确实有些问题,那我只能...

2.1K2
来自专栏JetpropelledSnake

SNMP学习笔记之Python的netsnmp和pysnmp的性能对比

用python获取snmp信息有多个现成的库可以使用,其中比较常用的是netsnmp和pysnmp两个库。网上有较多的关于两个库的例子。

3642
来自专栏同步博客

浅谈PHP异常处理

  PHP中的异常的独特性,即PHP中的异常不同于主流语言C++、java中的异常。在Java中,异常是唯一的错误报告方式,而在PHP中却不是这样,而是把所有不...

1393
来自专栏偏前端工程师的驿站

Java魔法堂:打包知识点之jar

一、前言                                    通过eclipse导出jar包十分方便快捷,但作为码农岂能满足GUI的便捷呢?所...

2377
来自专栏技术之路

翻译qmake文档(三) Creating Project Files

原英文文档:http://qt-project.org/doc/qt-5/qmake-project-files.html 创建项目文件      项目文件...

2016
来自专栏码洞

深入Python多进程通信原理与实战——图文

继上节使用原生多进程并行运行,基于Redis作为消息队列完成了圆周率的计算,本节我们使用原生操作系统消息队列来替换Redis。

912
来自专栏芋道源码1024

精尽 Dubbo 原理与源码专栏( 已经完成 69+ 篇,预计总共 75+ 篇 )

本小节,我们将 《精尽 Dubbo 源码解析》 和 《Dubbo 用户指南》 做一次映射,方便大家直接找到感兴趣的功能的具体源码实现。当然,如果有整理不到位的地...

4952
来自专栏python3

python3--队列Queue,管道Pipe,进程之间的数据共享,进程池Pool,回调函数callback

既打印了主进程put的值,也打印了子进程put的值,在进程中使用队列可以完成双向通信

5301

扫码关注云+社区

领取腾讯云代金券