jap的INCLUDE标签与INCLUDE指令FORWARD指令研究

来源:互联网 发布:伦敦陷落 知乎 编辑:程序博客网 时间:2024/04/30 03:56

我实在Tomcat的服务器查看的,其他服务器可能有些不同。

首先介绍下Tomcat中的JSP构成

在Tomcat中一个JSP应该继承org.apache.jasper.runtime.HttpJspBase并实现org.apache.jasper.runtime.JspSourceDependent这个接口

看下Tomcat是怎么介绍HttpJspBase的

This is the super class of all JSP-generated servlets

这个类继承于javax.servlet.http.HttpServlet

这充分说明了JSP就是一个Servlet

接下来查看下org.apache.jasper.runtime.JspSourceDependent

Interface for tracking the source files dependencies, for the purpose of compiling out of date pages. This is used for 1) files that are included by page directives 2) files that are included by include-prelude and include-coda in jsp:config 3) files that are tag files and referenced 4) TLDs referenced

这个接口主要是用来处理Include标签的

下面接着查看inclue标签

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;  static {    _jspx_dependants = new java.util.HashMap<java.lang.String,java.lang.Long>(1);    _jspx_dependants.put("/test2/NewFile.jsp", Long.valueOf(1324475713523L));  }

这是一个JSP页面生成的Servlet其中这段代码就是include标签

JSP的include是通过一段静态块与哈希表实现的,这样保证程序的每个对象使用同一个include 节省资源又保证相同。

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {    return _jspx_dependants;  }

这个函数是JspSourceDependent接口声明的。这个函数是用来获取文件中的静态引入标签。

下面继续查看下include动作指令

org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "lader.jsp" + (("lader.jsp").indexOf('?')>0? '&': '?') + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("shangdi", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("10", request.getCharacterEncoding()) + "&" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("xiadi", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("20", request.getCharacterEncoding()) + "&" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("gao", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("10", request.getCharacterEncoding()), out, false);


这个URL是要传入的信息就是include指令中的param参数,可以看出他是通过URL改写添加的参数,并且里面传送了request与response,这样可以在include嵌入的子页中对request与response进行操作。
下面继续看下forward

      if (true) {        _jspx_page_context.forward("lader.jsp" + (("lader.jsp").indexOf('?')>0? '&': '?') + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("shangdi", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode(String.valueOf(shangdi_s ), request.getCharacterEncoding()) + "&" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("xiadi", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode(String.valueOf(xiadi_s ), request.getCharacterEncoding()) + "&" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode("gao", request.getCharacterEncoding())+ "=" + org.apache.jasper.runtime.JspRuntimeLibrary.URLEncode(String.valueOf(gao_s ), request.getCharacterEncoding()));        return;      }


这是生成的JAVA文件中forward文件的代码从这里可以看出forward并没有传送request与response则表明这里的forward不能对request中的数据进行操作。而内部参数是通过URL重写实现的。

下面深入查看下include与forward的实现

    public static void include(ServletRequest request,                               ServletResponse response,                               String relativePath,                               JspWriter out,                               boolean flush)        throws IOException, ServletException {        if (flush && !(out instanceof BodyContent))            out.flush();        // FIXME - It is tempting to use request.getRequestDispatcher() to        // resolve a relative path directly, but Catalina currently does not        // take into account whether the caller is inside a RequestDispatcher        // include or not.  Whether Catalina *should* take that into account        // is a spec issue currently under review.  In the mean time,        // replicate Jasper's previous behavior        String resourcePath = getContextRelativePath(request, relativePath);        RequestDispatcher rd = request.getRequestDispatcher(resourcePath);        rd.include(request,                   new ServletResponseWrapperInclude(response, out));    }


 

这是JspRuntimeLibrary类中的include函数 可以看出他的实现原理是通过调用 RequestDispatcher 中的include实现的,这和一个正常的Servlet的include相同。

 

   public void forward(final String relativeUrlPath) throws ServletException,            IOException {        if (SecurityUtil.isPackageProtectionEnabled()) {            try {                AccessController.doPrivileged(                        new PrivilegedExceptionAction<Void>() {                    @Override                    public Void run() throws Exception {                        doForward(relativeUrlPath);                        return null;                    }                });            } catch (PrivilegedActionException e) {                Exception ex = e.getException();                if (ex instanceof IOException) {                    throw (IOException) ex;                } else {                    throw (ServletException) ex;                }            }        } else {            doForward(relativeUrlPath);        }    }
    private void doForward(String relativeUrlPath) throws ServletException,            IOException {        // JSP.4.5 If the buffer was flushed, throw IllegalStateException        try {            out.clear();        } catch (IOException ex) {            IllegalStateException ise = new IllegalStateException(Localizer                    .getMessage("jsp.error.attempt_to_clear_flushed_buffer"));            ise.initCause(ex);            throw ise;        }        // Make sure that the response object is not the wrapper for include        while (response instanceof ServletResponseWrapperInclude) {            response = ((ServletResponseWrapperInclude) response).getResponse();        }        final String path = getAbsolutePathRelativeToContext(relativeUrlPath);        String includeUri = (String) request.getAttribute(                RequestDispatcher.INCLUDE_SERVLET_PATH);        if (includeUri != null)            request.removeAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);        try {            context.getRequestDispatcher(path).forward(request, response);        } finally {            if (includeUri != null)                request.setAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH,                        includeUri);        }    }

 

这是PageContextImpl中的forward函数这个类是PageContext的实现类。这个类就是JSP中真正的PageContext

可以看出forward也是通过RequestDispatcher的forward实现 只不过他对response进行了一些处理


 

原创粉丝点击