在tomcat項目work目錄檢視jsp編譯後的servlet源代碼可以發現:
final javax.servlet.jsp.PageContext pageContext;
可以知曉pageContext是一個javax.servlet.jsp.PageContext類對象
我們知道pageContext可以獲得其他容器的引用:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TM5ETM1EjM1ETOycDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
源代碼:
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四個範圍容器中依次從小到大查找,傳回其值。