
我们不玩术语堆砌,而是把每个内置对象掰成“白话+场景+小例子”:比如把request比作“用户给服务器的‘快递单’”,装着表单数据、URL参数;response像“服务器回寄的‘包裹’”,负责送回网页或数据;out就是“服务器的‘打印机’”,直接把内容打在页面上……上篇先聚焦最常用的5个内置对象(request、response、out、session、application),从“是什么”“怎么用”到“容易踩的坑”,一步步讲透——比如request的“中文乱码”怎么解决?session的“过期时间”怎么设置?application存全局数据要注意什么?全部用新手能听懂的话讲清楚。
不管你是想补JSP基础,还是要解决“怎么接收用户输入”“怎么存全局数据”这类实际问题,这篇解析都能帮你把模糊的概念变成顺手的工具。 咱们一起把“隐形的对象”变成“好用的武器”!
你有没有过这种情况?刚学JSP时,明明没写Request request = new Request()
这种代码,却能直接在页面里用request.getParameter("username")
取用户输入的表单数据?或者写out.println("欢迎来到我的网站")
,就能把文字输出到网页上?我当年学JSP的第一天,盯着电脑屏幕愣了10分钟——这“凭空出现”的变量到底是哪来的?后来问了带我的老程序员才明白:这叫“内置对象”,是JSP给新手藏的“小福利”——不用自己创建,直接就能用,专解决基础开发里最常用的需求。今天这篇文章,我就把这些“隐形变量”扒得明明白白,尤其是上篇要讲的5个新手必用、不用就会卡壳的内置对象,保证你看完就能上手写例子。
先搞懂“内置对象”的底层逻辑:不是魔法,是JSP帮你“提前做了功课”
要理解内置对象,得先搞清楚JSP的本质——你写的.jsp
文件,最终会被Tomcat、Jetty这种Servlet容器编译成.java
文件(也就是Servlet)。比如你写了个index.jsp
,编译后会变成index_jsp.java
,打开看会发现:里面自动生成了HttpServletRequest request
、HttpServletResponse response
这些变量,甚至连PrintWriter out
都帮你创建好了。
我去年带过一个实习生小周,刚开始写JSP时,坚持要自己new
一个request
对象,结果运行时直接报错。我把编译后的.java
文件打开给她看,她才拍脑袋:“哦!原来JSP早就帮我做好了,我不用再重复造轮子?”对,这就是内置对象的核心——Servlet容器提前为你创建的、用于处理Web请求响应的常用对象,目的是减少新手的代码量,让你专注于业务逻辑。
Oracle官方文档(https://docs.oracle.com/javaee/5/tutorial/doc/bnahe.htmlnofollow)里明确说过:JSP内置对象是“隐式对象”(Implicit Objects),它们的作用域和生命周期由容器管理,开发者不需要显式声明或初始化。 你写JSP时,这些对象就像“预先摆好在桌上的工具”,拿起来就能用——比如要取用户输入,直接用request
;要给用户返回内容,直接用response
或out
。
我再举个真实例子:去年帮朋友做他的美食博客,他想做一个“提交菜谱”的功能——用户填完菜名、做法,点提交就能把数据存到数据库里。我当时写JSP页面时,直接用request.getParameter("dishName")
拿到菜名,用request.getParameter("method")
拿到做法,压根没写任何创建request
的代码。朋友看完问:“你这代码是不是漏了什么?怎么没定义request?”我笑着说:“JSP早就帮咱们定义好了,这就是内置对象的方便之处。”
上篇先搞定5个“高频到离谱”的内置对象:request、response、out、session、application
我统计过自己写过的JSP代码——80%的基础功能都只用这5个对象:取用户输入用request
,返回数据用response
,输出内容用out
,存登录状态用session
,存全局数据用application
。接下来我用“大白话+真实例子”,把每个对象拆成你能立刻理解的样子。
你可以把request
想象成“用户寄给服务器的快递单”——上面写着用户的“寄件信息”(比如浏览器类型、IP地址)、“包裹内容”(比如表单数据、URL参数)。不管用户是提交表单,还是点击带参数的链接,这些数据都会被装进request
里,送到服务器。
我之前做过一个电商项目的商品详情页,需要根据URL里的productId
显示对应的商品信息。比如URL是product.jsp?productId=123
,我用request.getQueryString()
拿到productId=123
,再用request.getParameter("productId")
取出123
,然后查数据库拿到商品数据——整个过程不到10行代码,特别顺手。
这里要提醒你一个常见坑:request
的“生命周期”只到本次请求结束。比如用户提交表单后,服务器处理完这个请求,request
里的数据就会被销毁——所以如果你想把数据从一个页面传到另一个页面,不能用request
存(除非用request.setAttribute()
配合转发,但那是另一个知识点)。我之前踩过这个坑:想把用户输入的用户名从login.jsp
传到home.jsp
,直接用request
存,结果home.jsp
里拿不到数据,后来才明白——request
只能管“一次请求”,跨页面得用session
。
如果说request
是“快递单”,那response
就是“服务器寄回给用户的包裹”——里面可以是网页HTML、JSON数据,也可以是“重定向”这样的指令。比如你登录成功后,服务器用response.sendRedirect("home.jsp")
让浏览器跳转到首页,这就是response
的“指令功能”。
我之前帮一个朋友做的登录页面,遇到过一个经典错误:重定向之后还往response
里写内容,结果页面报了IllegalStateException
。后来查资料才知道:response.sendRedirect()
会发送一个302状态码给浏览器,告诉它“去新地址”——这时候服务器已经结束了本次响应,再用out
输出内容,就会冲突。所以记住:重定向之后,不能再输出任何内容。
还有个实用技巧:用response.setContentType("text/html;charset=UTF-8")
解决中文乱码问题。我之前写的博客页面,输出中文时变成了“???”,就是因为没设置响应的字符编码——加上这句话后,中文立刻显示正常了。你要是遇到中文乱码,先检查response
的 contentType 设置,90%的情况都能解决。
out
对象就像“服务器的打印机”——你让它打什么,它就往网页上输出什么。比如out.println("
欢迎你!
"),就能在页面上显示一个大标题;或者用,把变量的值输出到页面。
我之前写过一个博客的“最新文章列表”,用out
循环输出文章标题:
<%
List articles = getLatestArticles(); // 假设从数据库拿最新文章
for (Article a articles) {
out.println("
" + a.getTitle() + " ");
}
%>
比直接写死HTML灵活多了——如果文章列表变了,不用改JSP代码,直接改数据库就行。
这里要区分out.println()
和的区别:
out.println()
是“主动输出”,可以放在Java代码块里;是“表达式输出”,会把表达式的值直接输出到页面,比如
等价于
out.print(username)
。我通常的习惯是:循环或复杂逻辑用out.println()
,简单变量用,这样代码更清晰。
session
对象就像“用户的购物车”——只要用户没关闭浏览器,或者没超过“会话超时时间”,里面的数据就一直存在。比如用户登录成功后,把用户ID存到session
里:session.setAttribute("userId", 1001)
,之后不管用户跳转到哪个页面,都能用session.getAttribute("userId")
拿到用户ID,判断是不是登录状态。
我之前做的论坛项目,用session
实现了“记住登录状态”:用户登录时,把user
对象存到session
里;每次访问需要登录的页面(比如发帖页),先检查session
里有没有user
对象——没有的话,跳转到登录页。这个功能特别常用,几乎所有需要用户登录的网站都在用。
关于session
的“生命周期”,你要记住两点:一是默认超时时间是30分钟(Tomcat的默认设置),可以在web.xml
里改:60
(改成60分钟);二是用户关闭浏览器后,session会失效——因为session是靠浏览器的Cookie来标识的,关闭浏览器后Cookie没了,服务器就认不出这个session了。
application
对象是“网站的全局公告板”——不管哪个用户访问,都能看到上面的内容。比如存网站的“总访问量”“在线用户数”,或者全局配置(比如网站名称、版权信息)。我之前做的一个博客项目,用application
存“总访问量”:
<%
Integer visitCount = (Integer) application.getAttribute("visitCount");
if (visitCount == null) {
visitCount = 1;
} else {
visitCount++;
}
application.setAttribute("visitCount", visitCount);
%>
本站总访问量:
这样不管哪个用户访问,总访问量都会递增——因为application
的生命周期是整个应用的运行时间,从服务器启动到服务器关闭,数据一直存在。
要注意的是:application
是所有用户共享的,所以修改里面的数据时要加锁(比如用synchronized
),避免并发问题。我之前做的论坛项目,用application
存在线用户数,一开始没加锁,结果并发访问时,在线用户数统计错了——后来加了synchronized(application)
,才解决了问题。
用一张表帮你记牢这5个对象的核心信息
为了让你快速对比这5个对象,我做了张“大白话表格”——直接看这张表,就能记住每个对象的“作用、比喻、生命周期”:
对象名 | 通俗比喻 | 核心作用 | 生命周期 |
---|---|---|---|
request | 用户的快递单 | 存用户输入的请求数据 | 本次请求结束后销毁 |
response | 服务器的包裹 | 向用户返回内容或指令 | 本次响应结束后销毁 |
out | 服务器的打印机 | 输出内容到网页 | 本次响应结束后销毁 |
session | 用户的购物车 | 存用户会话的临时数据 | 会话超时或关闭浏览器后销毁 |
application | 全局公告板 | 存所有用户共享的数据 | 服务器启动到关闭期间存在 |
你现在可以试着写个小例子:比如做一个“提交用户名”的页面——用form
表单提交用户名,然后在JSP里用request.getParameter("username")
拿到用户名,用out.println("欢迎你," + username)
输出到页面。这个例子不到20行代码,却能覆盖request
和out
两个核心对象,特别适合新手练手。
我当年学这些对象时,也是从这样的小例子开始的——写一行代码,运行看效果,再改一行,再看效果。慢慢就从“懵圈”变成“熟练”了。你要是今天试了这个例子,欢迎留言告诉我有没有成功——下次我再讲剩下的4个内置对象,咱们把JSP的“隐形变量”彻底搞透!
本文常见问题(FAQ)
为什么JSP里不用自己定义,就能直接用request、response这些对象?
因为这些是JSP的“内置对象”,也叫隐式对象——其实是JSP文件编译成Servlet的时候,容器自动帮你生成了这些对象的声明和初始化代码。比如你写的index.jsp编译后会变成index_jsp.java,里面早就有HttpServletRequest request、HttpServletResponse response这些变量了,根本不用你自己写new Request()这种代码。
而且这些对象的生命周期和作用域都是容器管的,你直接拿过来用就行,就像桌上预先摆好的工具,不用自己买或者组装。
用request取中文参数的时候乱码,该怎么解决?
首先得看请求方式——如果是POST请求(比如表单提交),你可以在取参数之前加一句request.setCharacterEncoding(“UTF-8”),这样request就能正确解析中文参数了;如果是GET请求(比如URL里带?name=张三这种参数),可能需要改Tomcat的server.xml配置,在Connector标签里加URIEncoding=”UTF-8″,或者用new String(request.getParameter(“name”).getBytes(“ISO-8859-1”), “UTF-8”)手动转码。
另外别忘了response那边也要设置response.setContentType(“text/html;charset=UTF-8”),这样返回给浏览器的内容也不会乱码,两头都得照顾到。
session的过期时间能不能改?默认是多久啊?
session默认过期时间一般是30分钟(比如Tomcat的默认设置),想改的话很简单——在项目的web.xml里加一段配置就行,比如要改成60分钟,就写60,单位是分钟。
不过要注意,session过期是指用户“超过设置时间没操作”才会失效,不是从登录开始算固定时间;另外用户关闭浏览器的话,session也会失效,因为session是靠浏览器的Cookie标识的,关了浏览器Cookie没了,服务器就认不出这个session了。
用application存全局数据的时候,为什么要加锁啊?
因为application是所有用户共享的,比如你存网站总访问量,要是同时有10个用户访问,都去改application里的visitCount,就会出问题——比如两个用户同时读到visitCount=100,都加1变成101,结果实际应该是102,统计就错了。
所以改application里的数据时,得用synchronized加个锁,比如synchronized(application) { 把改数据的代码包在里面 },这样同一时间只有一个用户能改,就不会乱了。
out和response都能输出内容,它们有什么不一样的地方?
out是JSP专门的输出流,就像“服务器的打印机”,直接把内容打在网页上;而response是更底层的Servlet响应对象,你可以用response.getWriter()拿到输出流来写内容。
还有个小细节要注意:out有自己的缓存,有时候out输出的内容会比response的内容晚显示——比如你先写out.println(“欢迎”),再写response.getWriter().println(“你好”),可能页面上先显示“你好”再显示“欢迎”,所以一般 统一用out或者统一用response,别混着用,避免顺序乱了。