兩種include方式及filter中的dispatcher解析

五月的倉頡發表於2015-11-02

兩種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>就好了。

相關文章