文章目錄
- 前言
- 一、JSP 文件編譯流程原理
- 二、創(chuàng)建并運(yùn)行待測(cè)試 JSP 頁(yè)面
- 三、查找 JSP 編譯文件輸出位置
- 3.1、打開(kāi)動(dòng)態(tài)項(xiàng)目運(yùn)行配置
- 3.2、查看 JSP 編譯文件輸出位置
- 3.3、查看 JSP 編譯輸出文件
- 四、JSP 編譯輸出 Servlet 的論證
- 五、訪問(wèn) JSP 文件的流程
- 總結(jié)
前言
相信大家都了解,JSP 頁(yè)面在請(qǐng)求的時(shí)候會(huì)先被 Tomcat 編譯為 Servlet(Servlet 是用 Java 語(yǔ)言編寫(xiě)的服務(wù)器端程序),然后再由 Java 編譯器編譯為以 .class 結(jié)尾的中間字節(jié)碼文件,最后再編譯為機(jī)器能識(shí)別的二進(jìn)制機(jī)器碼文件。我們通過(guò)使用 Eclipse 演示一個(gè)小案例,了解 JSP 編譯原理的同時(shí)來(lái)幫大家找到并剖析編譯后生成的 Servlet 的 Java 代碼文件。
一、JSP 文件編譯流程原理
JSP 頁(yè)面在請(qǐng)求的時(shí)候會(huì)先被 Tomcat 編譯為 Servlet(Servlet 是用 Java 語(yǔ)言編寫(xiě)的服務(wù)器端程序),然后再由 Java 編譯器編譯為以 .class 結(jié)尾的中間字節(jié)碼文件,最后再編譯為機(jī)器能識(shí)別的二進(jìn)制機(jī)器碼文件,整體流程如下圖所示:
二、創(chuàng)建并運(yùn)行待測(cè)試 JSP 頁(yè)面
我們先創(chuàng)建一個(gè)動(dòng)態(tài) Web 項(xiàng)目 JavaWebDemo_2020,并創(chuàng)建好一個(gè) JSP 頁(yè)面 Demo01.jsp,在 Tomcat 服務(wù)器下運(yùn)行一次。我們的測(cè)試代碼如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here
</title>
</head>
<body><h1>Hello,bailu!
</h1>
</body>
</html>
運(yùn)行結(jié)果如下圖所示:
三、查找 JSP 編譯文件輸出位置
現(xiàn)在我們的項(xiàng)目已經(jīng)在服務(wù)器運(yùn)行了一次,按照上面一中所述,JSP 頁(yè)面已經(jīng)完成了編譯流程并已經(jīng)輸出,那么我們?cè)趺礃硬拍苷业捷敵鑫募?#xff1f;
3.1、打開(kāi)動(dòng)態(tài)項(xiàng)目運(yùn)行配置
在當(dāng)前項(xiàng)目下點(diǎn)擊 Run As→Run Configurations…進(jìn)入運(yùn)行配置頁(yè)面,如下圖所示:
點(diǎn)擊你當(dāng)前使用的的服務(wù)器,我的是:Tomcat v9.0 Server at localhost,我們接著點(diǎn)擊 Arguments,如下圖所示:
3.2、查看 JSP 編譯文件輸出位置
根據(jù) Tomcat 虛擬機(jī)參數(shù)信息查看編譯文件輸出位置,第一條數(shù)據(jù) Dcatalina.base 即為 JSP 文件編譯后的輸出目錄,比如我的輸出目錄即為:D:\bailu\eclipse-jee-2019-09-R-win32-x86_64\eclipse\eclipse-workspace.metadata.plugins\org.eclipse.wst.server.core\tmp0,如下圖所示:
3.3、查看 JSP 編譯輸出文件
我們復(fù)制編譯文件輸出目錄,在“我的電腦”打開(kāi)該路徑,出現(xiàn)如下目錄結(jié)構(gòu),如下圖所示:
我們根據(jù) JSP 關(guān)于 Web 服務(wù)目錄的基礎(chǔ)知識(shí),可以明確,編譯輸出文件在 work 文件夾中,打開(kāi)該文件夾最底層文件夾,我們可以看到剛才在 Eclipse 中運(yùn)行的當(dāng)前項(xiàng)目 JavaWebDemo_2020 的輸出文件夾,如下圖所示:
我們順著項(xiàng)目文件夾逐級(jí)往下查看,就可以看到我們剛才運(yùn)行 Demo01.jsp 頁(yè)面的編譯輸出文件,一個(gè)是 JSP 初次編譯生成的 .java 文件,一個(gè)是 java 文件編譯后生成的 .class 中間字節(jié)碼文件,如下圖所示:
四、JSP 編譯輸出 Servlet 的論證
見(jiàn)證奇跡的時(shí)候到了!
我們使用 IDE 打開(kāi) JSP 頁(yè)面編譯生成的 .java 文件(.java 文件的可讀性與.class 文件強(qiáng)得多),一行一行與上面我們的 JSP 頁(yè)面對(duì)比,是不是一樣?這就直接可以說(shuō)明,該 java 文件就是 JSP 頁(yè)面編譯后生成的,具體代碼如下:
package org.apache.jsp.jsp;import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;public final class Demo01_jsp extends org.apache.jasper.runtime.HttpJspBaseimplements org.apache.jasper.runtime.JspSourceDependent,org.apache.jasper.runtime.JspSourceImports {private static final javax.servlet.jsp.JspFactory _jspxFactory
=javax.servlet.jsp.JspFactory.getDefaultFactory();private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants
;private static final java.util.Set<java.lang.String> _jspx_imports_packages
;private static final java.util.Set<java.lang.String> _jspx_imports_classes
;static {_jspx_imports_packages
= new java.util.HashSet<>();_jspx_imports_packages
.add("javax.servlet");_jspx_imports_packages
.add("javax.servlet.http");_jspx_imports_packages
.add("javax.servlet.jsp");_jspx_imports_classes
= null;}private volatile javax.el.ExpressionFactory _el_expressionfactory
;private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager
;public java.util.Map<java.lang.String,java.lang.Long> getDependants() {return _jspx_dependants
;}public java.util.Set<java.lang.String> getPackageImports() {return _jspx_imports_packages
;}public java.util.Set<java.lang.String> getClassImports() {return _jspx_imports_classes
;}public javax.el.ExpressionFactory _jsp_getExpressionFactory() {if (_el_expressionfactory
== null) {synchronized (this) {if (_el_expressionfactory
== null) {_el_expressionfactory
= _jspxFactory
.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();}}}return _el_expressionfactory
;}public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {if (_jsp_instancemanager
== null) {synchronized (this) {if (_jsp_instancemanager
== null) {_jsp_instancemanager
= org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());}}}return _jsp_instancemanager
;}public void _jspInit() {}public void _jspDestroy() {}public void _jspService(final javax.servlet.http.HttpServletRequest request
, final javax.servlet.http.HttpServletResponse response
)throws java.io.IOException, javax.servlet.ServletException {if (!javax.servlet.DispatcherType.ERROR
.equals(request
.getDispatcherType())) {final java.lang.String _jspx_method
= request
.getMethod();if ("OPTIONS".equals(_jspx_method
)) {response
.setHeader("Allow","GET, HEAD, POST, OPTIONS");return;}if (!"GET".equals(_jspx_method
) && !"POST".equals(_jspx_method
) && !"HEAD".equals(_jspx_method
)) {response
.setHeader("Allow","GET, HEAD, POST, OPTIONS");response
.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED
, "JSP 只允許 GET、POST 或 HEAD。Jasper 還允許 OPTIONS");return;}}final javax.servlet.jsp.PageContext pageContext
;javax.servlet.http.HttpSession session
= null;final javax.servlet.ServletContext application
;final javax.servlet.ServletConfig config
;javax.servlet.jsp.JspWriter out
= null;final java.lang.Object page
= this;javax.servlet.jsp.JspWriter _jspx_out
= null;javax.servlet.jsp.PageContext _jspx_page_context
= null;try {response
.setContentType("text/html; charset=UTF-8");pageContext
= _jspxFactory
.getPageContext(this, request
, response
,null, true, 8192, true);_jspx_page_context
= pageContext
;application
= pageContext
.getServletContext();config
= pageContext
.getServletConfig();session
= pageContext
.getSession();out
= pageContext
.getOut();_jspx_out
= out
;out
.write("\r\n");out
.write("<!DOCTYPE html>\r\n");out
.write("<html>\r\n");out
.write("<head>\r\n");out
.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");out
.write("<title>Insert title here</title>\r\n");out
.write("</head>\r\n");out
.write("<body>\r\n");out
.write("\t<h1>Hello,bailu!</h1>\r\n");out
.write("</body>\r\n");out
.write("</html>");} catch (java.lang.Throwable t
) {if (!(t
instanceof javax.servlet.jsp.SkipPageException)){out
= _jspx_out
;if (out
!= null && out
.getBufferSize() != 0)try {if (response
.isCommitted()) {out
.flush();} else {out
.clearBuffer();}} catch (java.io.IOException e
) {}if (_jspx_page_context
!= null) _jspx_page_context
.handlePageException(t
);else throw new ServletException(t
);}} finally {_jspxFactory
.releasePageContext(_jspx_page_context
);}}
}
不想看全部代碼的來(lái)看我這里的關(guān)鍵部分,如下圖所示:
說(shuō)明:我們可以看到,Java 通過(guò)out.write();方法將 JSP 標(biāo)簽輸出,并對(duì)其他元素做了處理。
這也就是當(dāng)初為什么出現(xiàn) JSP 的原因,使用 JSP 比 Java 節(jié)省了大量的代碼。同時(shí)論證了 JSP 文件編譯后首先生成的是 Servlet。也就可以說(shuō),JSP 本質(zhì)就是 Servlet,最終也是 Java 代碼。
五、訪問(wèn) JSP 文件的流程
到此,我們就得知,JSP 文件初次保存加載編譯會(huì)先生成 Servlet,并進(jìn)行之后的編譯處理。所以,除去瀏覽器緩存的原因,初次訪問(wèn) JSP 頁(yè)面你會(huì)感到速度很慢,之后再訪問(wèn)就比較快了。
是否是第一次訪問(wèn) JSP 文件的流程如下圖所示:
但是請(qǐng)注意:如果你的 JSP 文件進(jìn)行了修改,再次點(diǎn)擊保存發(fā)布會(huì)重新編譯,又會(huì)重新走編譯的流程。
總結(jié)
本文給大家介紹了 JSP 頁(yè)面發(fā)布之后編譯的流程,從 JSP 文件到 Sevlet(Java文件)再到 .class 文件最后到二進(jìn)制機(jī)器碼,剖析了為何 JSP 的本質(zhì)即 Servlet,便于大家之后對(duì) MVC 模式更進(jìn)一步了解,加深對(duì)于 JSP 在架構(gòu)中所處層次的掌握。還有建議大家養(yǎng)成一個(gè)好習(xí)慣:看源碼!源碼是一切!
我是白鹿,一個(gè)不懈奮斗的程序猿。望本文能對(duì)你有所裨益,歡迎大家的一鍵三連!若有其他問(wèn)題、建議或者補(bǔ)充可以留言在文章下方,感謝大家的支持!
總結(jié)
以上是生活随笔為你收集整理的JSP 编译原理:JSP 是 Servlet?如何用 Eclipse 查看 JSP 编译生成的 Servlet 源文件?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。