在tomcat项目work目录查看jsp编译后的servlet源代码可以发现:

final javax.servlet.jsp.PageContext pageContext;

可以知晓pageContext是一个javax.servlet.jsp.PageContext类对象

我们知道pageContext可以获得其他容器的引用:


源代码:

package javax.servlet.jsp;

public abstract class PageContext extends JspContext
{
  public static final int PAGE_SCOPE = 1;
  public static final int REQUEST_SCOPE = 2;
  public static final int SESSION_SCOPE = 3;
  public static final int APPLICATION_SCOPE = 4;
  public static final String PAGE = "javax.servlet.jsp.jspPage";
  public static final String PAGECONTEXT = "javax.servlet.jsp.jspPageContext";
  public static final String REQUEST = "javax.servlet.jsp.jspRequest";
  public static final String RESPONSE = "javax.servlet.jsp.jspResponse";
  public static final String CONFIG = "javax.servlet.jsp.jspConfig";
  public static final String SESSION = "javax.servlet.jsp.jspSession";
  public static final String OUT = "javax.servlet.jsp.jspOut";
  public static final String APPLICATION = "javax.servlet.jsp.jspApplication";
  public static final String EXCEPTION = "javax.servlet.jsp.jspException";

  public abstract void initialize(Servlet paramServlet, ServletRequest paramServletRequest, ServletResponse paramServletResponse, String paramString, boolean paramBoolean1, int paramInt, boolean paramBoolean2)
    throws IOException, IllegalStateException, IllegalArgumentException;

  public abstract void release();

  public abstract HttpSession getSession();

  public abstract Object getPage();

  public abstract ServletRequest getRequest();

  public abstract ServletResponse getResponse();

  public abstract Exception getException();

  public abstract ServletConfig getServletConfig();

  public abstract ServletContext getServletContext();

  public abstract void forward(String paramString)
    throws ServletException, IOException;

  public abstract void include(String paramString)
    throws ServletException, IOException;

  public abstract void include(String paramString, boolean paramBoolean)
    throws ServletException, IOException;

  public abstract void handlePageException(Exception paramException)
    throws ServletException, IOException;

  public abstract void handlePageException(Throwable paramThrowable)
    throws ServletException, IOException;

  public BodyContent pushBody()
  {
    return null;
  }

  public ErrorData getErrorData()
  {
    int status = 0;

    Integer status_code = (Integer)getRequest().getAttribute("javax.servlet.error.status_code");

    if (status_code != null) {
      status = status_code.intValue();
    }

    return new ErrorData((Throwable)getRequest().getAttribute("javax.servlet.error.exception"), status, (String)getRequest().getAttribute("javax.servlet.error.request_uri"), (String)getRequest().getAttribute("javax.servlet.error.servlet_name"));
  }
}


是一个继承了JspContext抽象类的抽象类

package javax.servlet.jsp;

public abstract class JspContext
{
  public abstract void setAttribute(String paramString, Object paramObject);

  public abstract void setAttribute(String paramString, Object paramObject, int paramInt);

  public abstract Object getAttribute(String paramString);

  public abstract Object getAttribute(String paramString, int paramInt);

  public abstract Object findAttribute(String paramString);

  public abstract void removeAttribute(String paramString);

  public abstract void removeAttribute(String paramString, int paramInt);

  public abstract int getAttributesScope(String paramString);

  public abstract Enumeration<String> getAttributeNamesInScope(int paramInt);

  public abstract JspWriter getOut();

  /** @deprecated */
  public abstract ExpressionEvaluator getExpressionEvaluator();

  public abstract ELContext getELContext();

  /** @deprecated */
  public abstract VariableResolver getVariableResolver();

  public JspWriter pushBody(Writer writer)
  {
    return null;
  }

  public JspWriter popBody()
  {
    return null;
  }
}
因此那么具有父类的抽象方法,可以对各个作用于属性存取:

注意存取的第二个变量决定作用域的范围

   public static final int PAGE_SCOPE = 1;
  public static final int REQUEST_SCOPE = 2;
  public static final int SESSION_SCOPE = 3;
  public static final int APPLICATION_SCOPE = 4;

我们了解了pageContent是一个抽象类,那么是怎么具体实现的?

private static final javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory.getDefaultFactory();

pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);

发现是由javax.servlet.jsp.JspFactory来构造

getPageContext(Servlet paramServlet, ServletRequest paramServletRequest, ServletResponse paramServletResponse, String paramString, boolean paramBoolean1, int paramInt, boolean paramBoolean2);

package javax.servlet.jsp;

public abstract class JspFactory
{
  private static volatile JspFactory deflt = null;

  public static void setDefaultFactory(JspFactory deflt)
  {
    deflt = deflt;
  }

  public static JspFactory getDefaultFactory()
  {
    return deflt;
  }

  public abstract PageContext getPageContext(Servlet paramServlet, ServletRequest paramServletRequest, ServletResponse paramServletResponse, String paramString, boolean paramBoolean1, int paramInt, boolean paramBoolean2);

  public abstract void releasePageContext(PageContext paramPageContext);

  public abstract JspEngineInfo getEngineInfo();

  public abstract JspApplicationContext getJspApplicationContext(ServletContext paramServletContext);
}
这是一个抽象类,自然需要实现类来实现:

package org.apache.jasper.runtime;


public class JspFactoryImpl extends JspFactory
{
  private final Log log;
  private static final String SPEC_VERSION = "2.1";
  private static final boolean USE_POOL = Boolean.valueOf(System.getProperty("org.apache.jasper.runtime.JspFactoryImpl.USE_POOL", "true")).booleanValue();

  private static final int POOL_SIZE = Integer.valueOf(System.getProperty("org.apache.jasper.runtime.JspFactoryImpl.POOL_SIZE", "8")).intValue();
  private ThreadLocal<PageContextPool> localPool;

  public JspFactoryImpl()
  {
    this.log = LogFactory.getLog(JspFactoryImpl.class);

    this.localPool = new ThreadLocal();
  }

  public PageContext getPageContext(Servlet servlet, ServletRequest request, ServletResponse response, String errorPageURL, boolean needsSession, int bufferSize, boolean autoflush)
  {
    if (Constants.IS_SECURITY_ENABLED) {
      PrivilegedGetPageContext dp = new PrivilegedGetPageContext(this, servlet, request, response, errorPageURL, needsSession, bufferSize, autoflush);

      return ((PageContext)AccessController.doPrivileged(dp));
    }
    return internalGetPageContext(servlet, request, response, errorPageURL, needsSession, bufferSize, autoflush);
  }

子类的函数实现父类,pageContext由此制造出:

public PageContext getPageContext(Servlet servlet, ServletRequest request, ServletResponse response, String errorPageURL, boolean needsSession, int bufferSize, boolean autoflush)
  {
    if (Constants.IS_SECURITY_ENABLED) {
      PrivilegedGetPageContext dp = new PrivilegedGetPageContext(this, servlet, request, response, errorPageURL, needsSession, bufferSize, autoflush);

      return ((PageContext)AccessController.doPrivileged(dp));
    }
    return internalGetPageContext(servlet, request, response, errorPageURL, needsSession, bufferSize, autoflush);
  }
pageContext处理字节字符流共存问题:

而pageContent的pushbody()源码:

因为javax.servlet.jsp.PageContext是一个抽象类,工厂方法必定构造其子类

package org.apache.jasper.runtime;

public class PageContextImpl extends PageContext
{
  private static final JspFactory jspf = JspFactory.getDefaultFactory();
  private BodyContentImpl[] outs;
  private int depth;
  private Servlet servlet;
  private ServletConfig config;
  private ServletContext context;
  private JspApplicationContextImpl applicationContext;
  private String errorPageURL;
  private transient HashMap<String, Object> attributes;
  private transient ServletRequest request;
  private transient ServletResponse response;
  private transient HttpSession session;
  private transient ELContextImpl elContext;
  private boolean isIncluded;
  private transient JspWriter out;
  private transient JspWriterImpl baseOut;

  PageContextImpl()
  {
    this.outs = new BodyContentImpl[0];
    this.attributes = new HashMap(16);
    this.depth = -1;
  }

  public void initialize(Servlet servlet, ServletRequest request, ServletResponse response, String errorPageURL, boolean needsSession, int bufferSize, boolean autoFlush)
    throws IOException
  {
    _initialize(servlet, request, response, errorPageURL, needsSession, bufferSize, autoFlush);
  }

  private void _initialize(Servlet servlet, ServletRequest request, ServletResponse response, String errorPageURL, boolean needsSession, int bufferSize, boolean autoFlush)
  {
    this.servlet = servlet;
    this.config = servlet.getServletConfig();
    this.context = this.config.getServletContext();
    this.errorPageURL = errorPageURL;
    this.request = request;
    this.response = response;

    this.applicationContext = JspApplicationContextImpl.getInstance(this.context);

    if ((request instanceof HttpServletRequest) && (needsSession))
      this.session = ((HttpServletRequest)request).getSession();
    if ((needsSession) && (this.session == null)) {
      throw new IllegalStateException("Page needs a session and none is available");
    }

    this.depth = -1;
    if (this.baseOut == null)
      this.baseOut = new JspWriterImpl(response, bufferSize, autoFlush);
    else {
      this.baseOut.init(response, bufferSize, autoFlush);
    }
    this.out = this.baseOut;

    setAttribute("javax.servlet.jsp.jspOut", this.out);
    setAttribute("javax.servlet.jsp.jspRequest", request);
    setAttribute("javax.servlet.jsp.jspResponse", response);

    if (this.session != null) {
      setAttribute("javax.servlet.jsp.jspSession", this.session);
    }
    setAttribute("javax.servlet.jsp.jspPage", servlet);
    setAttribute("javax.servlet.jsp.jspConfig", this.config);
    setAttribute("javax.servlet.jsp.jspPageContext", this);
    setAttribute("javax.servlet.jsp.jspApplication", this.context);

    this.isIncluded = (request.getAttribute("javax.servlet.include.servlet_path") != null);
  }

<pre name="code" class="java"> 

 
可以清楚理解其原理: 

@Override
    public JspWriter pushBody(Writer writer) {
        depth++;
        if (depth >= outs.length) {
            BodyContentImpl[] newOuts = new BodyContentImpl[depth + 1];
            for (int i = 0; i < outs.length; i++) {
                newOuts[i] = outs[i];
            }
            newOuts[depth] = new BodyContentImpl(out);
            outs = newOuts;
        }

        outs[depth].setWriter(writer);
        out = outs[depth];

        // Update the value of the "out" attribute in the page scope
        // attribute namespace of this PageContext
        setAttribute(OUT, out);

        return outs[depth];
    }
pageContext的finAttribute(paramName);

@Override
    public Object findAttribute(final String name) {
        if (SecurityUtil.isPackageProtectionEnabled()) {
            return AccessController.doPrivileged(
                    new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    if (name == null) {
                        throw new NullPointerException(Localizer
                                .getMessage("jsp.error.attribute.null_name"));
                    }

                    return doFindAttribute(name);
                }
            });
        } else {
            if (name == null) {
                throw new NullPointerException(Localizer
                        .getMessage("jsp.error.attribute.null_name"));
            }

            return doFindAttribute(name);
        }
    }

   <pre name="code" class="java">

 
 
<pre name="code" class="java"><span style="color:#000000;"> private Object doFindAttribute(String name) {

        Object o = attributes.get(name);
        if (o != null)
            return o;

        o = request.getAttribute(name);
        if (o != null)
            return o;

        if (session != null) {
            try {
                o = session.getAttribute(name);
            } catch(IllegalStateException ise) {
                // Session has been invalidated.
                // Ignore and fall through to application scope.
            }
            if (o != null)
                return o;
        }

        return context.getAttribute(name);
    }</span>
 

了解到由pageContext---->request--->session--->ServeltContext四个范围容器中依次从小到大查找,返回其值。


Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐