你不知道的PageContext
你不知道的PageContext
最近在文艺复兴,学习JSP和Servlet,此文为笔者学习记录。
本文分为以下几个部分:
前言
在我们使用的项目中,存储数据最常用的非 Session 和 Cookie 莫属了,但实际上还有 PageContext、ServletContext。
- PageContext :作用于每个 page 中
- ServletContext :作用于服务器中,保存数据在服务器中,例如用户 A 在当前页面设置了值,用户 B 可以在其他页面获取该值,可以看成 共享 Session。
一些题外话:
jsp 最后会被转义成 class 文件进行输出,html代码会被 out.print()出去,java 代码会原封不动的编译。
环境搭建
笔者使用的是 idea 2021.2 版本(版本不一样编译后class文件位置可能不一样),JDK 1.8。
使用 idea 创建一个maven javaweb项目

目录结构如下

修改index.jsp,写入java代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<%
String a = "";
%>
</body>
</html>
添加tomcat,启动项目,访问首页,查看被 tomcat 编译过得 java、class文件(只有访问过该jsp文件,才会有编译过得class文件,访问首页就是访问index)
我(当前版本:idea 2021.02版本)的被编译的文件路径:C:\用户\当前用户\AppData\Local\JetBrains\idea\tomcat\项目\work\
C:\Users\yb\AppData\Local\JetBrains\IntelliJIdea2021.2\tomcat\06562583-39ad-4361-959e-eedb21db4b9a\work\Catalina\localhost\page_context_war\org\apache\jsp

老版本的可能在 C:\用户\当前用户\.IntelliJIdea版本\system\tomcat\项目\work\
C:\Users\yb\.IntelliJIdea2018.3\system\tomcat\Unnamed_test\work\Catalina\localhost\test_war\org\apache\jsp

打开index_jsp.java文件,找到 _jspService 中会存在String a = "";正是上方写的java代码。
正文
首先pom文件引入jar包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
回归正题,我们一般Session存的,会用Session取值,ServletContext存值,会用ServletContext取,就像这样
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<%
pageContext.setAttribute("page", "pageContext");
request.setAttribute("req", "request");
session.setAttribute("session", "session");
application.setAttribute("app", "servletContext");
%>
<%=pageContext.getAttribute("page")%>
<br>
<%=request.getAttribute("req")%>
<br>
<%=session.getAttribute("session")%>
<br>
<%=application.getAttribute("app")%>
<br>
</body>
</html>

但是,我们可以通过PageContext获取所有值,使用pageContext.findAttribute
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<%
pageContext.setAttribute("page", "pageContext");
request.setAttribute("req", "request");
session.setAttribute("session", "session");
application.setAttribute("app", "servletContext");
%>
<%=pageContext.findAttribute("page")%>
<br>
<%=pageContext.findAttribute("req")%>
<br>
<%=pageContext.findAttribute("session")%>
<br>
<%=pageContext.findAttribute("app")%>
<br>
</body>
</html>

我们查看对应的jsp编译的class文件
response.setContentType("text/html;charset=UTF-8");
PageContext pageContext = _jspxFactory.getPageContext(this, request, response, (String)null, true, 8192, true);
_jspx_page_context = pageContext;
ServletContext application = pageContext.getServletContext();
pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
//...
pageContext.setAttribute("page", "pageContext");
request.setAttribute("req", "request");
session.setAttribute("session", "session");
application.setAttribute("app", "servletContext");
out.write(10);
out.print(pageContext.findAttribute("page"));
out.write("\n");
out.write("<br>\n");
out.print(pageContext.findAttribute("req"));
out.write("\n");
out.write("<br>\n");
out.print(pageContext.findAttribute("session"));
out.write("\n");
out.write("<br>\n");
out.print(pageContext.findAttribute("app"));
out.write("\n");
out.write("<br>\n");
我们看到 session、servletContext 是从pageContext中获得,这就比较容易解释pageContext为什么能得到session的东西了,我们再看看pageContext.findAttribute究竟是什么。
ctrl 按进去,并不能看到实现类,只能看到接口,查阅tomcat官方api,可以找到其实现类
org.apache.jasper.runtime.PageContextImpl
我们解压tomcat目录下lib文件夹中的 jasper.jar 文件,找到 PageContextImpl.class 文件

我们找到findAttribute方法
public Object findAttribute(String name) {
//如果name不存在,抛出异常
if (name == null) {
throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name"));
} else {
//首先自己 pageContext 查看自己有没有名字为 name 的东西,有则返回
Object o = this.attributes.get(name);
if (o != null) {
return o;
} else {
//没有再查看request的,然后session的,最后ServletContext
o = this.request.getAttribute(name);
if (o != null) {
return o;
} else {
if (this.session != null) {
try {
o = this.session.getAttribute(name);
} catch (IllegalStateException var4) {
}
if (o != null) {
return o;
}
}
return this.context.getAttribute(name);
}
}
}
}
我们发现 pageContext 是一步一步往上寻找的,首先从 pagecontext 中获取,没有再从request中获取,再session,最后servletContext获取,是一步一步往上寻找,我们可以测试一下。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<%
session.setAttribute("test", "测试session");
request.setAttribute("test", "测试request");
%>
<%=pageContext.findAttribute("test")%>
<br>
<%=pageContext.findAttribute("test")%>
</body>
</html>

我们看到符合我们的预期,那我们怎么从这里面获取session的值呢?(不通过getsession方法)
我们查看Structure发现有个 getAttribute(String name, int scope)
public Object getAttribute(String name, int scope) {
if (name == null) {
throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name"));
} else {
//不同scope对应不同的方法
switch(scope) {
case 1:
return this.attributes.get(name);
case 2:
return this.request.getAttribute(name);
case 3:
if (this.session == null) {
throw new IllegalStateException(Localizer.getMessage("jsp.error.page.noSession"));
}
return this.session.getAttribute(name);
case 4:
return this.context.getAttribute(name);
default:
throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope"));
}
}
}
通过第二个参数来修改来源,那我们修改index.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<%
session.setAttribute("test", "测试session");
request.setAttribute("test", "测试request");
%>
<%=pageContext.getAttribute("test",2)%>
<br>
<%=pageContext.getAttribute("test",3)%>
</body>
</html>

成功获取到了值。
里面还有一些方法,读者感兴趣可以自行阅读。
回顾之前jsp编译成class文件的代码
PageContext pageContext = _jspxFactory.getPageContext(this, request, response, (String)null, true, 8192, true);
我们看到pageContext是 jspFactory 获取到的,jspFactoryImpl 也是和PageContext实现类在同一个目录下,一步步查看最后调用了 pageContext.initialize 方法,部分参数是传递进来的,所以 pageContext 的request、session等不是传进来的,就是携带进来的,这就解释了为什么用的request等都是一个东西了。
public void initialize(Servlet servlet, ServletRequest request, ServletResponse response, String errorPageURL, boolean needsSession, int bufferSize, boolean autoFlush) throws IOException {
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(Localizer.getMessage("jsp.error.page.sessionRequired"));
} else {
this.depth = -1;
if (bufferSize == -1) {
bufferSize = 8192;
}
if (this.baseOut == null) {
this.baseOut = new JspWriterImpl(response, bufferSize, autoFlush);
} else {
this.baseOut.init(response, bufferSize, autoFlush);
}
this.out = this.baseOut;
this.setAttribute("javax.servlet.jsp.jspOut", this.out);
this.setAttribute("javax.servlet.jsp.jspRequest", request);
this.setAttribute("javax.servlet.jsp.jspResponse", response);
if (this.session != null) {
this.setAttribute("javax.servlet.jsp.jspSession", this.session);
}
this.setAttribute("javax.servlet.jsp.jspPage", servlet);
this.setAttribute("javax.servlet.jsp.jspConfig", this.config);
this.setAttribute("javax.servlet.jsp.jspPageContext", this);
this.setAttribute("javax.servlet.jsp.jspApplication", this.context);
}
}
其中有个servlet参数,明明在 jsp 编译后的class文件并没有找到servlet,为什么传个this能传servlet,其实 extends HttpJspBase (org.apache.jasper.runtime.HttpJspBase),我们查看HttpJspBase就可以找到他是继承 HttpServlet的,这就不难解释为何传 this 是servlet了,因为这个jsp本身就是servlet鸭。

总结
PageContext 为页面上下文对象,jsp编译成java再编译成class文件中,获取request等就是从 PageContext 中获取的,可以把PageContext看成一个容器。
PageContext 中含有 session、request 等,可以利用 getAttribute(String name, int scope)、setAttribute(String name, Object o, int scope) 等方法,根据 scope 不同获取 request、session等。
你不知道的PageContext的更多相关文章
- MySQL 系列(三)你不知道的 视图、触发器、存储过程、函数、事务、索引、语句
第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...
- MySQL 系列(二) 你不知道的数据库操作
第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 本章内容: 查看\创建\使用\删除 数据库 用户管理及授权实战 局域网 ...
- 《你不知道的JavaScript》整理(二)——this
最近在读一本进阶的JavaScript的书<你不知道的JavaScript(上卷)>,这次研究了一下“this”. 当一个函数被调用时,会创建一个活动记录(执行上下文). 这个记录会包含函 ...
- 《你不知道的JavaScript》整理(一)——作用域、提升与闭包
最近在读一本进阶的JavaScript的书<你不知道的JavaScript(上卷)>,里面分析了很多基础性的概念. 可以更全面深入的理解JavaScript深层面的知识点. 一.函数作用域 ...
- 你不知道的Javascript(上卷)读书笔记之一 ---- 作用域
你不知道的Javascript(上卷)这本书在我看来是一本还不错的书籍,这本书用比较简洁的语言来描述Js的那些"坑",在这里写一些博客记录一下笔记以便消化吸收. 1 编译原理 在此 ...
- JSP中的 HttpSession、pageContext对象
pageContext 隐含对象对应javax.servlet.jsp.PageContext,都自动的被加入至pageContext中, 您可以由它来取得与JSP相关的对应之Servlet对象,像是 ...
- page、pageContext、servletContext的区别
ServletContext是容器上下文,指当前的一个web应用的上下文 JSP网页本身,page对象是当前页面转换后的Servlet类的实例.从转换后的Servlet类的代码中,可以看到这种关系:O ...
- PageContext
pageContext对象 pageContext对象是JSP中很重要的一个内置对象,不过在一般的JSP程序中,很少用到它,所以知道request对象.response对象的人比较多,知道pageCo ...
- [No00002E]关于大数据,你不知道的6个迷思
还是那个观点:计算机,编程语言,互联网,大数据等等都只是工具! 导语:看过美剧<纸牌屋>没?知道这部"白宫甄嬛传"为什么会火吗?靠的是大!数!据! 过去两年,在 Net ...
- ${pageContext.request.contextPath}无效
发现在Tomcat7.0.58,在jsp页面使用${pageContext.request.contextPath}获取不到项目名称,网上找了很多答案试了都无效: 把Tomcat版本换成Tomcat7 ...
随机推荐
- Bash下切换conda环境
背景:很多时候实验命令都是基于Linux系统的,但是很多人的电脑是window系统的. 使用git自带的Bash可以运行linux命令,不过有时候在bash中想使用conda环境的时候比较麻烦,具体做 ...
- Hessian反序列化分析
RPC协议 RPC全称为Remote Procedure Call Protocol(远程调用协议),RPC和之前学的RMI十分类似,都是远程调用服务,它们不同之处就是RPC是通过标准的二进制格式来定 ...
- 挑战吧,HarmonyOS应用开发工程师
一年一度属于工程师的专属节日1024已过,但程序员多重活动持续进行中~ 参与活动即有机会获得HUAWEI Freebuds 5i 耳机等精美礼品! 点击"阅读原文"查看更多活动 ...
- Causal Inference理论学习篇-Tree Based-Causal Tree
Tree-Based Algorithms Tree-based这类方法,和之前meta-learning 类的方法最明显的区别是: 这类方法把causal effect 的计算显示的加入了到了树模型 ...
- CentOS 8 安装更新国内清华大学源手记【亲测成功】
一直和各种OS打交道,仍觉得自己是小白,故深知小白们的困惑和蛋碎,特此将安装更新源的细节和步骤做了详细整理,供大家参考.红字是命令和提示,深灰色代码框中是源配置,本文采用了清华大学CentOS 8的源 ...
- spring boot yaml 配置[三]
前言 我们知道java 因为历史的原因,一直有一个配置地狱的痛点.那么如何解决掉它呢? spring boot 是一柄利器,但是呢,还是要配置. 看来配置的避免不了的了. 那么如何可以减轻这种痛苦呢? ...
- 鸿蒙HarmonyOS实战-ArkUI组件(Image)
一.Image 在HarmonyOS中,Image组件是用于显示图像文件的UI组件.它可以显示本地图像文件或远程URL地址的图像文件.Image组件的实现方式比较简单,只需提供图像文件路径或URL地址 ...
- 百度AIPNLP 文本相似度 文本审核
效果不如有监督的bert文本相似度好 from aip import AipNlp APP_ID = "22216281" APT_KEY = "foEeYauuvnqW ...
- 深入分析 Flutter 渲染性能
简介: Flutter 有很多优点,特别是对于开发者来说,跨平台多端支持,丰富的 UI 组件库和交互效果,声明式 UI,React 的更新方式,Hot-reload 提高开发效率等等.虽然它在渲染性能 ...
- 业界首个机密计算容器运行时—Inclavare Containers正式进入CNCF!
简介: Inclavare Containers 通过云原生计算基金会(CNCF)TOC 投票正式成为 CNCF 官方沙箱项目. 作者|彦荣 2021 年 9月 15 日,Inclavare C ...