兩種include方式
我自己寫了一個original.jsp,另外有一個includedPage.jsp,我想在original.jsp中把includedPage.jsp引進來有兩種方式:
1、<%@ include file="includedPage.jsp" %>,這是一種include指令
2、<jsp:include page="includedPage.jsp" />,這是一種include動作
先講原理再講區別,所有的jsp頁面在後臺,會先被轉換為一個Servlet,就比如這個includedPage.jsp吧:
public final class includedPage_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent { private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory(); private static java.util.List _jspx_dependants; private javax.el.ExpressionFactory _el_expressionfactory; private org.apache.AnnotationProcessor _jsp_annotationprocessor; ... }
而這個HttpJspBase:
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage { public final void init(ServletConfig config) throws ServletException { super.init(config); jspInit(); _jspInit(); } ... }
它是HttpServlet的子類,因此任何一個頁面Tomcat容器都會將它轉成一個Servlet,然後編譯成.class檔案,頁面上實際執行的是.class檔案,這些jsp檔案對應的.class檔案都放在Tomcat的work目錄下。OK,講完了這個再講兩種include的區別:
1、jsp指令是在original.jsp被轉換成Servlet前,將includedPage程式碼插入其中;jsp動作是在original.jsp被請求時,將次級頁面includedPage.jsp包含進來。所以jsp指令和jsp動作的根本性差別在於它們被呼叫的時間的不同,前者在頁面轉換期間被啟用,後者在請求期間被啟用。使用jsp指令的時候,嵌入的頁面includedPage.jsp要刪除MyEclipse給開發者自動生成的path、basePath的定義也是這個原因,因為在頁面轉換期間被啟用,如果不刪除,那麼兩個jsp頁面中都有path、basePath的定義,就屬性重複定義了
2、由於第一點的差別導致,include動作使得主頁面和嵌入的頁面共同生成一個Servlet,而include指令則主頁面和每個嵌入的頁面各自生成自己的Serlvet
實際應用中,一般都會使用include動作即<jsp:include page="includedPage.jsp" />的方式來嵌入頁面,因為include動作雖然在執行效率上稍稍慢於jsp指令,但是在維護性上卻遠勝。因為我們使用jsp動作的話,被嵌入的頁面如果發生了變化,那麼所有包含被嵌入頁面的Servlet都要重新編譯並更新,這是一個很大的代價。
filter的四種dispatcher
Java Web的開發都知道如何在web.xml裡面配置過濾器,過濾器中有一個屬性<dispatcher></dispatcher>卻很少有人清楚地知道什麼意思,我感覺網上也沒有寫得特別好的文章解釋清楚這個屬性。所以現在就來探究一下這個屬性的作用,首先寫一個Filter:
public class DispatcherFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Enter DispatcherFilter.doFilter()"); chain.doFilter(request, response); } public void destroy() { } }
再來在web.xml裡面定義一個filter:
<filter> <filter-name>dispatcher</filter-name> <filter-class>com.xrq.filter.DispatcherFilter</filter-class> </filter> <filter-mapping> <filter-name>dispatcher</filter-name> <url-pattern>/*</url-pattern> <dispatcher>XXX</dispatcher> </filter-mapping>
注意<dispatcher></dispatcher>必須寫在filter-mapping的最後。dispatcher的前提條件當然是要先滿足url-pattern,然後dispatcher有四種可能的屬性:
1、REQUEST
只要發起的操作是一次HTTP請求,比如請求某個URL發起了一個GET請求、表單提交方式為POST時提交表單則發起了一個POST請求、表單提交方式為GET時提交表單則發起了一次GET請求、一次重定向則前後相當於發起了兩次請求,這些情況下有幾次請求就會走幾次指定過濾器
2、FOWARD
只有噹噹前頁面是通過請求轉發轉發過來的場景,才會走指定的過濾器
3、INCLUDE
只要是通過<jsp:include page="xxx.jsp" />,嵌入進來的頁面,每嵌入的一個頁面,都會走一次指定的過濾器
4、ERROR
這個可能開發者不是很熟悉,意思是當觸發了一次error的時候,就會走一次指定的過濾器。什麼叫做觸發error,舉個例子,我在web.xml裡面配置了<error-page></error-page>:
<error-page> <error-code>400</error-code> <location>/filter/error.jsp</location> </error-page> <error-page> <error-code>404</error-code> <location>/filter/error.jsp</location> </error-page> <error-page> <error-code>500</error-code> <location>/filter/error.jsp</location> </error-page>
意思是HTTP請求響應的狀態碼只要是400、404、500三種狀態碼之一(比如訪問了一個不存在的頁面,就是404),容器就會將請求轉發到http://ip:port/工程名/filter/error.jsp下,這就觸發了一次error,走進了我自己寫的DispatchFilter。注意一點的是,雖然把請求轉發到http://ip:port/工程名/filter/error.jsp是一次forward的過程,但是我試了一下,配置成<dispatcher>FORWARD</dispatcher>並不會走DispatchFilter這個過濾器。
這四種dispatcher方式可以單獨使用,也可以組合使用,配置多個<dispatcher></dispatcher>就好了。