通过查看 JSP 页面实现类如何使用表示所述范围的 PageContext 对象,您可以更好地了解页面范围的工作原理。
在 javax.servlet.jsp 包的 javadoc 中有一个如何完成此操作的示例。有趣的部分是:
public void _jspService(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
JspFactory factory = JspFactory.getDefaultFactory();
PageContext pageContext = factory.getPageContext( ... )
...
}
如您所见,当您的 servlet 将其请求和响应对象转发给 JSP 时,将调用其页面实现类的 _jspService 方法,并将它们作为参数。紧接着,该方法获得一个PageContext 对象并将其存储为该方法的局部变量。
局部变量是线程内存(不共享),因此,任何其他线程都无法为来自另一个用户(甚至不是来自同一用户)的请求提供服务来访问该变量。一旦_jspService 返回,变量就不再存在。如果另一个线程同时调用_jspService(),工厂会给它一个不同的PageContext 实例。
这就引出了这样一个问题:从工厂获得的PageContext 对象始终是一个新实例还是一个被重用的实例。这取决于实现,如this question 的答案中所述。在后一种情况下,容器应确保它不会同时将同一实例传递给两个不同的线程(否则会出现并发问题)。
请注意,在返回之前,_jspService() 通过调用 releasePageContext() 确保 PageContext 对象的任何状态都被“清除”(参见 javadoc 示例中的 finally 块)。这反过来又在PageContext 上调用release(),其效果是“释放所有内部引用,并准备 PageContext 以供潜在的重用”。因此,当使用池化实例时,每个线程总是会得到一个“干净”的实例。