JavaWeb开发与代码的编写(六)
Jsp的九个内置对象
jsp的运行原理
每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理。JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet) ,然后按照servlet的调用方式进行调用。 由于JSP第一次访问时会翻译成servlet,所以第一次访问通常会比较慢,但第二次访问,JSP引擎如果发现JSP没有变化,就不再翻译,而是直接调用,所以程序的执行效率不会受到影响。 JSP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与web开发相关的对象供_jspServlet使用。JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用。
认识九个内置对象
request,response,session,application,config这些对象在前面都已经作了详细的介绍,这里重点介绍一下剩下的pageContext对象,out对象,page对象。
内置对象使用说明
page对象
page对象表示当前一个JSP页面,可以理解为一个对象本身,即:把一个JSP当作一个对象来看待。page对象在开发中几乎不用,了解一下即可
out对象
out对象用于向客户端发送文本数据。 out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter方法返回的PrintWriter对象非常相似。 JSP页面中的out对象的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存。 只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用ServletResponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:
- 设置page指令的buffer属性关闭了out对象的缓存功能
- out对象的缓冲区已满
- 整个JSP页面结束
out对象的工作原理图
pageContext对象
pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其它8大隐式对象的引用,它自身还是一个域对象(容器),可以用来保存数据。并且,这个对象还封装了web开发中经常涉及到的一些常用操作,例如引入和跳转其它资源、检索其它域对象中的属性等。
通过pageContext获得其他对象
- getException方法返回exception隐式对象
- getPage方法返回page隐式对象
- getRequest方法返回request隐式对象
- getResponse方法返回response隐式对象
- getServletConfig方法返回config隐式对象
- getServletContext方法返回application隐式对象
- getSession方法返回session隐式对象
- getOut方法返回out隐式对象
pageContext封装其它8大内置对象的意义
如果在编程过程中,把pageContext对象传递给一个普通java对象,那么这个java对象将可以获取8大隐式对象,此时这个java对象就可以和浏览器交互了,此时这个java对象就成为了一个动态web资源了,这就是pageContext封装其它8大内置对象的意义,把pageContext传递给谁,谁就能成为一个动态web资源,那么什么情况下需要把pageContext传递给另外一个java类呢,什么情况下需要使用这种技术呢,在比较正规的开发中,jsp页面是不允许出现java代码的,如果jsp页面出现了java代码,那么就应该想办法把java代码移除掉,我们可以开发一个自定义标签来移除jsp页面上的java代码,首先围绕自定义标签写一个java类,jsp引擎在执行自定义标签的时候就会调用围绕自定义标签写的那个java类,在调用java类的时候就会把pageContext对象传递给这个java类,由于pageContext对象封装了对其它8大隐式对象的引用,因此在这个java类中就可以使用jsp页面中的8大隐式对象(request,response,config,application,exception,Session,page,out)了,pageContext对象在jsp自定义标签开发中特别重要。
pageContext作为域对象
pageContext对象可以作为容器来使用,因此可以将一些数据存储在pageContext对象中。
pageContext对象的常用方法
public void setAttribute(java.lang.String name,java.lang.Object value)
public java.lang.Object getAttribute(java.lang.String name)
public void removeAttribute(java.lang.String name)
public java.lang.Object findAttribute(java.lang.String name)
重点介绍一下findAttribute方法,这个方法是用来查找各个域中的属性的,查看这个方法的API可以看到关于这个方法的描述: Searches for the named attribute in page, request, session (if valid), and application scope(s) in order and returns the value associated or null.
当要查找某个属性时,findAttribute方法按照查找顺序"page→request→session→application"在这四个对象中去查找,只要找到了就返回属性值,如果四个对象都没有找到要查找的属性,则返回一个null。
范例:使用pageContext的findAttribute方法查找属性值
pageContext的findAttribute方法查找属性值
pageContext.findAttribute方法查找到的属性值:
pageContext对象的name1属性:
request对象的name2属性:
session对象的name3属性:
application对象的name4属性:
查找不存在的name5属性:
使用EL表达式进行输出:
pageContext对象的name1属性:${name1}
request对象的name2属性:${name2}
session对象的name3属性:${name3}
application对象的name4属性:${name4}
不存在的name5属性:${name5}
运行结果:
EL表达式语句在执行时,会调用pageContext.findAttribute方法,用标识符为关键字,分别从page、request、 session、application四个域中查找相应的对象,找到则返回相应对象,找不到则返回”” (注意,不是null,而是空字符串)。
pageContext对象中封装了访问其它域的方法
public java.lang.Object getAttribute(java.lang.String name,int scope)
public void setAttribute(java.lang.String name, java.lang.Object value,int scope)
public void removeAttribute(java.lang.String name,int scope)
代表各个域的常量
PageContext.APPLICATION_SCOPE
PageContext.SESSION_SCOPE
PageContext.REQUEST_SCOPE
PageContext.PAGE_SCOPE
范例:pageContext访问其它域
pageContext访问其它域
取出存放在session对象中的属性值:
第一种做法:使用pageContext.getAttribute("attributeName",PageContext.SESSION_SCOPE);去取出session对象中值
姓名:
第二种做法:使用session.getAttribute("attributeName");去取出session对象中值
姓名:
PageContext引入和跳转到其他资源
PageContext类中定义了一个forward方法(用来跳转页面)和两个include方法(用来引入页面)来分别简化和替代RequestDispatcher.forward方法和include方法。 方法接收的资源如果以“/”开头, “/”代表当前web应用。
范例:使用pageContext的forward方法跳转到其他页面
使用pageContext的forward方法跳转页面
运行结果如下:
pageContext.forward("/pageContextDemo05.jsp");
这种写法是用来简化和替代pageContext.getRequest().getRequestDispatcher("/pageContextDemo05.jsp").forward(request, response);这种写法的。在实际开发中,使用pageContext.forward(relativeUrlPath)方法跳转页面用得不多,主要是因为要在Jsp页面中嵌套java代码,所以这种做法简单了解一下即可,在开发中,要想从一个Jsp页面采用服务器端跳转的方式跳转到另一个Jsp页面,那么一般会使用标签,标签用于把请求转发给另外一个资源。
范例:使用pageContext的include方法引入资源
使用pageContext的include方法引入资源
使用pageContext的include方法引入资源
运行结果:
在实际开发中,使用pageContext的include方法引入页面这种做法也很少用,一般都使用jsp:include标签引入资源,因此这种做法了解一下即可。
Jsp的属性范围
所谓的属性范围就是一个属性设置之后,可以经过多少个其他页面后仍然可以访问的保存范围。
JSP中提供了四种属性范围,四种属性范围分别指以下四种:
- 当前页:一个属性只能在一个页面中取得,跳转到其他页面无法取得
- 一次服务器请求:一个页面中设置的属性,只要经过了服务器跳转,则跳转之后的页面可以继续取得。
- 一次会话:一个用户设置的内容,只要是与此用户相关的页面都可以访问(一个会话表示一个人,这个人设置的东西只要这个人不走,就依然有效)
- 上下文中:在整个服务器上设置的属性,所有人都可以访问
属性的操作方法
既然JSP中提供了四种属性范围,则四种属性范围中都将包含以下的属性操作方法。
No.
方法
描述
1
public void setAttribute(String name,Object value)
设置属性
2
public object getAttribute(String name)
取得属性
3
public void removeAttribute(String name)
删除属性
属性的操作无外乎就是增加、取得和删除这个几个操作。
单词Attribute的意思是“属性”,setAttribute(String name,Object value)从单词的组合来看就可以知道是这个方法的是设置属性,设置属性的名字和属性的值,名字(name)为String类型,值(value)为Object类型,由于值为Object类型,这表示可以设置任意类型的数据作为值,因为所有的类都是从Object类型继承而来。因此设置属性值的时候可以是任意类型的数据。getAttribute(String name)方法是根据属性的名字取得属性,removeAttribute(String name)方法是根据属性的名字删除属性。
JSP四种属性范围的具体介绍
page属性范围(pageContext)
page属性范围相对好理解一些:在一个页面设置的属性,跳转到其他页面就无法访问了。但是在使用page属性范围的时候必须注意的是,虽然习惯上将页面范围的属性称为page范围,但是实际上操作的时候是使用pageContext内置对象完成的。
pageContext属性范围操作流程图
pageContext从字面上的定义,可以发现,是表示一个页面(page)的上下文(Context),可以表示一个页面中的所有内容。
从操作流程图来看,在第一个页面设置的属性经过服务器端跳转到第二个页面以后,在第二个页面是无法取得在第一个页面中设置的属性的,就好比现在坐着的桌子上有一支笔,但一旦离开了这张桌子,坐到别的桌子上时,笔就没有了。
下面通过代码来观察此范围的属性
范例:pageContextDemo01.jsp
在页面中设置两个属性
姓名:
日期:
程序运行结果如下:
这说明了在本页设置的pageContext范围属性在本页确实可以取得,下面使用跳转语句,观察跳转之后是否还可以取得属性。
范例:pageScopeDemo02.jsp
范例:pageScopeDemo03.jsp
姓名:
日期:
在以上程序中的pageScopeDemo02.jsp只是设置了两个属性,跳转到pageScopeDemo03.jsp之后再在pageScopeDemo03.jsp中取在pageScopeDemo02.jsp设置的page属性。此时,运行结果如下:
使用了服务器端跳转,但是发现内容并不能取得,证明page范围的属性只能在本页中取得,跳转到其他页面之中不能取得。如果现在希望跳转到其他页面之中,依然可以取得,则可以扩大属性范围,使用request属性范围即可。
request属性范围
request属性范围表示在一次服务器跳转中有效,只要是服务器跳转,则设置的request属性可以一直传递下去。
范例:requestScopeDemo01.jsp
范例:requestScopeDemo02.jsp
姓名:
日期:
运行结果如下:
从运行结果来看,程序跳转了,但是与page范围相比,内容可以向下继续传递,即在第一个页面设置的属性跳转到第二个页面后在第二个页面中依然可以取得第一个页面设置的属性。
如果现在有第三个页面了,则也可以继续向后传递
范例:修改requestScopeDemo02.jsp
范例:requestScopeDemo03.jsp
姓名:
日期:
以上的结果依然可以访问,但是如果,此时使用了超链接的方式传递的话,则属性是无法向下继续传递的。
范例:修改requestScopeDemo03.jsp
姓名:
日期:
跳转到requestScopeDemo04.jsp
此时使用了超链接跳转,一旦跳转之后,地址栏改变,所以此种跳转也可以称为客户端跳转。
requestScopeDemo04.jsp
姓名:
日期:
运行结果:
requestScopeDemo04.jsp页面显示的结果是null。这说明了在requestScopeDemo01.jsp这个页面设置的属性经过超链接这种客户端跳转到别的页面时别的页面是无法取得requestScopeDemo01.jsp中设置的属性的。
如果还想进一步扩大属性范围,则可以使用session范围属性
session属性范围
session设置的属性不管如何跳转,都可以取得的。当然,session只针对一个用户
在第一个页面上设置的属性,跳转(服务器跳转/客户端跳转)到其他页面之后,其他的页面依然可以取得第一个页面上设置的属性。
下面通过代码来观察session属性范围
范例:sessionScopeDemo01.jsp
这里使用的是服务器端跳转
sessionScopeDemo02.jsp
姓名:
日期:
sessionScopeDemo03
这里使用的是超链接这种客户端跳转
运行程序sessionScopeDemo01.jsp结果如下所示:
sessionScopeDemo03.jsp
姓名:
日期:
点击超链接sessionScopeDemo03,跳转到了sessionScopeDemo03.jsp这个页面,此时程序的运行结果如下:
这说明了即使是采用客户端跳转,在别的页面依然可以取得第一个页面中设置的session属性。但是,如果,此时新开了一个浏览器,则sessionScopeDemo03.jsp肯定无法取得sessionScopeDemo01.jsp中设置的session对象的属性,因为session只是保留了一个人的信息。
如果一个属性想让所有的用户都可以访问,则可以使用最后一种属性范围:application范围。
application属性范围
因为application属性范围是在服务器上设置的一个属性,所以一旦设置之后任何用户都可以浏览到此属性。
下面通过代码来观察application属性范围
范例:applicationScopeDemo01.jsp
applicationScopeDemo02
范例:applicationScopeDemo02.jsp
姓名:
日期:
观察页面的运行效果:
开启多个浏览器窗口,运行applicationScopeDemo02.jsp时,都可以显示出上图所示的结果,因为属性范围设置在了服务器中,所以只要是连接到此服务器的任意用户都可以取得此属性,当然,如果服务器关闭的话,则此属性肯定消失。
如把Tomcat服务器先关闭后再重新启动,打开浏览器窗口运行applicationScopeDemo02.jsp时,得到的结果如下图所示:
注意:如果在服务器上设置了过多的application属性,则会影响到服务器的性能。
关于pageContext属性范围的进一步补充
之前所讲解的四种属性范围,实际上都是通过pageContext属性范围设置上的。打开pageContext所在的说明文档。
PageContext类继承了JspContext类,在JspContext类中定义了setAttribute方法,如下:
public abstract void setAttribute(String name,Object value,int scope)
此方法中存在一个scope的整型变量,此变量就表示一个属性的保存范围。
PageContext类继承了JspContext类,所以在PageContext类中实现了抽象的setAttribute方法:
public abstract void setAttribute(String name,Object value,int scope)
这个setAttribute()方法如果不写后面的int类型的scope参数,则此参数默认为PAGE_SCOPE,则此时setAttribute()方法设置的就是page属性范围,如果传递过来的int类型参数scope为REQUEST_SCOPE,则此时setAttribute()方法设置的就是request属性范围,同理,传递的scope参数为SESSION_SCOPE和APPLICATION_SCOPE时,则表示setAttribute()方法设置的就是session属性范围和application属性范围。
下面通过代码来观察此四种属性范围常量的作用,以:request为例
范例:pageScopeDemo04.jsp
pageScopeDemo05.jsp
使用request对象获取属性:
姓名:
日期:
使用pageContext对象获取属性:
姓名:
日期:
运行结果:
从运行结果可以看到:在pageScopeDemo04.jsp使用的是pageContext对象调用setAttribute()方法设置的属性范围是request的属性范围,因此在调用此方法时,把一个int类型的scope范围常量REQUEST_SCOPE传递了进来,这个REQUEST_SCOPE属性范围常量的作用就是告诉pageContext对象现在要设置的属性范围是request的属性范围,所以pageScopeDemo05.jsp这个页面中可以直接使用request.getAttribute();方法获取在pageScopeDemo04.jsp设置的属性。
jsp四种属性范围的使用场合
1、request:如果客户向服务器发请求,产生的数据,用户看完就没用了,像这样的数据就存在request域,像新闻数据,属于用户看完就没用的。 2、session:如果客户向服务器发请求,产生的数据,用户用完了等一会儿还有用,像这样的数据就存在session域中,像购物数据,用户需要看到自己购物信息,并且等一会儿,还要用这个购物数据结帐。 3、application(servletContext):如果客户向服务器发请求,产生的数据,用户用完了,还要给其它用户用,像这样的数据就存在application(servletContext)域中,像聊天数据。
服务器内部跳转与客户端跳转的区别
内部跳转,可以带参数。客户端浏览器的地址没有变
用户访问当前jsp跳转到target.jsp的页面 带参数
target.jsp 接收参数
服务器内部跳转后的页面
userName:
password:
客户端跳转
request的信息带不过去,只能带过去session和application的信息
response.sendRedirect("")
重定向
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setAttribute("requestKey", "request值");
HttpSession session=request.getSession(); // 获取session
session.setAttribute("sessionKey", "session值");
ServletContext application=this.getServletContext(); // 获取application
application.setAttribute("applicationKey", "application值");
response.sendRedirect("target.jsp"); // 客户端跳转/重定向
}
target.jsp
目标地址
request值:
session值:
application值:
服务器跳转
转发可以获取request的值
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setAttribute("requestKey", "request值");
HttpSession session=request.getSession(); // 获取session
session.setAttribute("sessionKey", "session值");
ServletContext application=this.getServletContext(); // 获取application
application.setAttribute("applicationKey", "application值");
RequestDispatcher rd=request.getRequestDispatcher("target.jsp");
rd.forward(request, response); // 服务器调转/转发
}