一篇文章讀懂Java Web的框架標簽
在Java Web應用開發時,如果不是前后端分離進行數據交互的實現,一般都會通過JSP、FreeMarker、Velocity之類的技術進行頁面的渲染。而在頁面的渲染過程中,很多時候會使用到標簽(taglib)這個技術。
比如為了控制頁面一些顯示邏輯,實現類似代碼里if/else這種效果,就會使用到core標簽里的內容,類似這樣:
要實現一個容器數據的遍歷,可以直接使用core標簽的foreach。
要實現頁面上數據的格式化,則可以直接使用format標簽,進行對應數據的格式化展現。
甚至在一些MVC框架中,也都包含一些便捷的用于頁面數據渲染的標簽,可以直接使用。例如Spring中的form標簽就可以直接進行數據的綁定。
那這些標簽背后是如何工作的呢?
之前的文章里有寫過JSP的工作原理,概括起來,就是會在執行時將JSP生成Servlet文件,然后再執行對應的service方法,進行請求的處理。其中涉及到使用標簽的部分也會生成對應的執行邏輯。
而實際上,一個tag,對應的是一個Java類,根據規范實現相應的方法。由JSP生成的Servlet在執行標簽的過程中,會直接調用標簽對應類的指定方法,根據返回值,來進行頁面上對應內容的輸出。如果是繼續則輸出內容,如果是跳過內容就會被忽略。基本就是這樣一個思路。
例如本文前面的 if 標簽,對應生成的Servlet內容是這樣的:
再看 foreach 這個標簽,生成的內容是這樣的:
我們看到,foreach 被直接轉換成了do while 循環。
其中,最核心的兩個方法是:
- doStartTag
- doEndTag
doStartTag
該方法會在JSP類內,被具體的標簽實現類調用。用于實例的標簽開始,執行時pageContext和一系列屬性被認為已經設置完成。
會返回EVAL_BODY_INCLUDE或者SKIP_BODY,從這兩個結果的變量名稱可以看出,如果如果tag期望繼續處理body,就返回前者,否則不處理就返回后者。
doEndTag用于確認該標簽執行后,頁面是否要繼續渲染。
整個taglib使用起來都比較便捷,直接在JSP中聲明 prefix 和 uri ,相當于把這部分內容依賴添加了進來,然后直接使用標簽進行屬性的設置,對應命名空間下標簽的使用等。
而這些標簽的聲明,是通過類似這樣的形式,被保存在Jar文件或者WEB-INF這些地方。
具體的標簽實現類,一般繼承TagSupport,然后重寫我們上面提到的doStartTag 和 doEndTag 方法。
所以,對于一般在JSP頁面內有大堆的 <% %>這種所謂的 scriptlet,可以直接定義一個標簽,然后把邏輯移動到重寫方法內即可。
這些Tag聲明的tld,一般會在應用部署后啟動時進行掃描,然后添加到Map里。
頁面解析執行的時候,會判斷對應聲明的tld是否存在,沒有就會停止頁面執行。
頁面解析生成Servlet類之后,執行時會調用具體標簽的屬性設置,doStartTag這些方法,此時如果一些屬性不存在,綁定不成功等這些具體的標簽邏輯會被暴露出來。
比如我們在使用 Spring 標簽時,經常使用其 form 標簽進行參數綁定。為了試驗,我們隨便寫一個Spring 的標簽使用
- <form:input path="abc" id="test"/>
此時,JSP生成的內容是這樣的:
然后頁面渲染時,會真正的進行數據綁定,判斷這些屬性的合法性等等,這里由于是隨便寫的一行代碼,無法綁定所有報錯了。
所以,對于 Spring、Struts2 等等這些框架的標簽,本質上執行也還是這些邏輯,如果頁面在渲染的時候出現問題,了解清楚是在哪一步的時候出了問題,知道具體這些標簽的工作原理。
例如下圖 Spring 的 InputTag 繼承關系,也沒有脫離Servlet 的Tag這個框。
總結下,Taglib的工作原理,是在應用部署的時候,解析tld的聲明。頁面渲染的時候,解析如果引入的tld不存在,就會報錯。tld合法之后執行 tag 的具體邏輯,根據返回值判斷是否繼續頁面的渲染。
框架的標簽也是如此。
【本文為51CTO專欄作者“侯樹成”的原創稿件,轉載請通過作者微信公眾號『Tomcat那些事兒』獲取授權】