Tomcat JSP预编译

症状:

某日在编写一个.jsp文件。为了做一个测试我回拨了电脑的系统时钟,比如回拨到2006年12月25日,随即发现该.jsp文件被离奇地“锁定”——无论怎么改该.jsp文件都不失效。
一开始怀疑是IE缓存的问题:
  1. 按“F5”键刷新,故障依旧
  2. 关闭IE,重新打开IE,重新进入该.jsp文件,故障依旧
  3. 关闭IE,把IE的临时目录中的所有文件都删除,重新进入该.jsp文件,故障依旧
接着怀疑是Apache页面缓存的问题,重启Apache Tomcat 6服务器,故障依旧。
 

原因分析:

所有的jsp页面在第一次执行时,都要先被Tomcat或别的jsp容器预编译为servlet文件,这个过程被称为“预编译”,也有的人喜欢称为“翻译”,总之都是一个意思。servet是一个java的类,和别的所有java一样,一个servlet有它的源代码.java文件,也有被java编译器编译后的.class文件。最后,当用户在浏览器中键入网址时,浏览器把请求发给apache,apache再调用JVM执行.class文件。总之,一个页面生成的全过程 = 预编译+编译+执行
 
熟悉编程语言的人都清楚:一种语言从开发时的文本文件到运行时的二进制文件的编译过程(这个编译过程含预编译)一般是系统资源开销相对较大的过程;换句话说,编译过程时占用的系统资源一般在软件开发调试运行时占了很大的比例。如果一个开发环境在每次运行同样的代码时都要编译,那么浪费的时间和系统资源是相当惊人的!
 
为了避免多余的重复编译,jsp的容器一般采用如下机制:先将.jsp文件预编译成为.java文件,放在“[Tomcat 所在路径]\work\Catalina\localhost\[web应用程序的名称]”的路径下。由于这个.java文件里面的类默认是“org.apache.jsp.[jsp文件的相对路径名]”这样一个包名下,由于java的编译器会自动根据类的包名建立文件夹,并且把编译生成的.class文件默认放在文件夹下,所以为了方便起见,jsp容器实际上会把预编译生成的.java文件和编译生成的.class文件缓存在“[Tomcat 所在路径]\work\Catalina\localhost\[web应用程序的名称]\org\apache\jsp\[jsp文件的相对路径名]”下。每当用户在IE中试图访问某个jsp页面时,系统会自动将jsp页面的最后修改时间和缓存中的文件的最后修改时间相比较:如果前者晚于后者则预编译jsp文件+编译新的.java文件;如果前者早于后者则什么事情也不做。
 
由于在这个case中,由于我回拨了电脑的系统时钟,使得jsp页面的最后修改时间早于缓存中的.java文件和.class文件的最后修改时间,从而jsp不再进行预编译和编译工作,使得我不管再怎么刷新IE都显示的旧版本的页面。

解决方法:

  • 方法一:把系统时间调后,至少要保证系统时间在调前的时间,比如像这个case里的2006年12月25日之后——也就是2006年12月26日。但不建议使用此方法。
  • 方法二:删除“[Tomcat 所在路径]\work\Catalina\localhost\[web应用程序的名称]\org\apache\jsp\”下的.java文件和.class文件,也就是相当于清空.jsp预编译(翻译)的缓存。

深入讨论:

由于懒得做实验,不知道apache tomcat比较的是哪两者的日期,我猜测有如下几种可能:

  1. .jsp页面文件的最后修改时间 vs. 先前记录下来的.jsp页面最后修改时间
  2. .jsp页面文件的最后修改时间 vs. 缓存中.java文件的最后修改时间
  3. .jsp页面文件的最后修改时间 vs. 缓存中.class文件的最后修改时间

个人认为:上面的“3.”是最有可能性的。因为编译器只是跳过旧的,而不是跳过所有时间不相同的文件,因此可以排除“1.”——因为只有“1.”可以做到检验时间的戳不同,而不仅仅是比较时间戳的先后。又因为“2.”没有“3.”更reasonable,“2.”又可被排除。所以“3.”是最有可能性的。

posted @ 2020-04-30 10:18  DarJeely  阅读(470)  评论(0编辑  收藏  举报