Java Web基础入门第二十三讲 JSP技术——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大对象的引用。

JSP九大隐式对象

JSP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与Web开发相关的对象供_jspServlet使用。这9个隐式对象分别是哪些,以及作用是笔试经常考察的知识点。
在这里插入图片描述
request,response,session,application,config等这些对象在前面都已经作了详细的介绍,这里重点介绍一下剩下的pageContext对象,out对象,page对象。

JSP隐式对象的使用说明

page隐式对象

page隐式对象表示当前一个JSP页面,可以理解为一个对象本身,即把一个JSP当作一个对象来看待。page对象在开发中几乎不用,了解一下即可。其作用是在JSP里面得到JSP对应的Servlet。

out隐式对象

out隐式对象用于向客户端发送文本数据。out对象是通过调用pageContext对象的getOut方法返回的,其作用和用法与ServletResponse.getWriter方法返回的PrintWriter对象非常相似。
JSP页面中的out隐式对象的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存。只有向out对象中写入了内容,且满足如下任何一个条件时,out对象才去调用ServletResponse.getWriter方法,并通过该方法返回的PrintWriter对象将out对象的缓冲区中的内容真正写入到Servlet引擎提供的缓冲区中:

  1. 设置page指令的buffer属性关闭了out对象的缓存功能;
  2. out对象的缓冲区已满;
  3. 整个JSP页面结束。

out隐式对象的工作原理图

在这里插入图片描述
请看下面的一个例子,使用浏览器访问如下JSP页面,输出结果会是什么?为什么?

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>out隐式对象</title>
</head>
<body>
	<%
		out.write("hahahahaha<br/>");
		response.getWriter().write("wowowowo<br/>");
	%>
</body>
</html>

在Chrome浏览器上的运行结果如下:
在这里插入图片描述
原因如下图:
在这里插入图片描述
那么使用浏览器访问如下JSP页面,输出结果又会是什么呢?为什么?

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>out隐式对象</title>
</head>
<body>
	aaaaaaaaaaaaa<br/>
	<%
		//out.write("hahahahaha<br/>");
		response.getWriter().write("wowowowo<br/>");
	%>
</body>
</html>

因为在JSP中编写这样的代码hahahahaha<br/>,在翻译过来的Servlet中就是out.write("hahahahaha<br/>");这样的代码,所以运行结果与原因均同上例。

out隐式对象的注意事项

记住不要同时使用out和response.getWriter()输出数据,原因上面已经说的很清楚了,最好是只使用out对象来输出数据。

pageContext隐式对象

pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,这个对象不仅封装了对其它8大隐式对象的引用,它自身还是一个域对象,可以用来保存数据。并且,这个对象还封装了Web开发中经常涉及到的一些常用操作,例如引入和跳转其它资源、检索其它域对象中的属性等。

通过pageContext获得其他对象

在这里插入图片描述
不知你有没想过pageContext封装其它8大内置对象的意义,更直白点说,如果在编程过程中,把pageContext对象传递给一个普通Java对象,那么这个Java对象将具有什么功能呢? 如果在编程过程中,把pageContext对象传递给一个普通Java对象,那么这个Java对象将可以获取8大隐式对象,此时这个Java对象就可以和浏览器交互了,此时这个Java对象就成为了一个动态Web资源了,这就是pageContext封装其它8大内置对象的意义。把pageContext传递给谁,谁就能成为一个动态Web资源。
那么在什么情况下需要把pageContext传递给另外一个Java类呢,什么情况下需要使用这种技术呢?在比较正规的开发中,JSP页面是不允许出现Java代码的(JSP页面里面是不能出现一行Java代码的,在软件开发里面,你要想你这个JSP是一个合格、良好的JSP的话,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对象中。
在这里插入图片描述
重点介绍一下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。所以,findAttribute方法的实现原理为:假设要查找的属性为data,首先从page域里面找,有没有data,如果page域里面没有找到data,再从request域里面找,有没有data,如果request域里面没有找到data,再从session域里面找,有没有data,如果session域里面没有找到data,再从application域里面找,有没有data,如果这4个域都没找到,那么返回null。
例一,使用pageContext的findAttribute方法查找属性值。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>pageContext的findAttribute方法查找属性值</title>
</head>
<body>
	<%
        pageContext.setAttribute("name1", "陈浩南");
        request.setAttribute("name2", "赵山河");
        session.setAttribute("name3", "大天二");
        application.setAttribute("name4", "苏阿细");
    %>

    <%
        /*
         * 使用pageContext的findAttribute方法查找属性,
         * 由于取得的值为Object类型,
         * 因此必须使用String强制向下转型,转换成String类型
         */
         // 查找name1属性,按照顺序"page→request→session→application"在这四个对象中去查找
         String refName1 = (String)pageContext.findAttribute("name1");
         String refName2 = (String)pageContext.findAttribute("name2");
         String refName3 = (String)pageContext.findAttribute("name3");
         String refName4 = (String)pageContext.findAttribute("name4");
         // 查找一个不存在的属性
         String refName5 = (String)pageContext.findAttribute("name5");
    %>
    <h1>pageContext.findAttribute方法查找到的属性值:</h1>
    <h3>pageContext对象的name1属性:<%=refName1 %></h3>
    <h3>request对象的name2属性:<%=refName2 %></h3>
    <h3>session对象的name3属性:<%=refName3 %></h3>
    <h3>application对象的name4属性:<%=refName4 %></h3>
    <h3>查找不存在的name5属性:<%=refName5 %></h3>
    <hr/>
    <h1>使用EL表达式进行输出:</h1>
    <h3>pageContext对象的name1属性:${name1 }</h3>
    <h3>request对象的name2属性:${name2 }</h3>
    <h3>session对象的name3属性:${name3 }</h3>
    <h3>application对象的name4属性:${name4 }</h3>
    <h3>查找不存在的name5属性:${name5 }</h3>
</body>
</html>

运行结果如下:
在这里插入图片描述
el表达式在JSP里面的作用就是取出数据。el表达式语句在执行时,会调用pageContext.findAttribute方法,用标识符为关键字,分别从page、request、 session、application四个域中查找相应的对象,找到则返回相应对象,找不到则返回””(注意,不是null,而是空字符串)。
例二,pageContext访问其它域。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>pageContext访问其它域</title>
</head>
<body>
	<%
        /*
         * 此时相当于往session对象中存放了一个name属性,
         * 等价于 session.setAttribute("name","铜锣湾扛把子");
         */
        pageContext.setAttribute("name", "陈浩南", pageContext.SESSION_SCOPE);
    %>

    <%
        // 取得session对象的属性,使用pageContext对象获取
        String refName1 = (String)pageContext.getAttribute("name", pageContext.SESSION_SCOPE);
        String refName2 = (String)session.getAttribute("name"); 
    %>
    <h1>取出存放在session对象中的属性值:</h1>
    <p>第一种做法:使用pageContext.getAttribute("attributeName",PageContext.SESSION_SCOPE);去取出session对象中值</p>
    <h3>姓名:<%=refName1 %></h3>
    <p>第二种做法:使用session.getAttribute("attributeName");去取出session对象中值</p>
    <h3>姓名:<%=refName2 %></h3>
</body>
</html>

运行结果如下:
在这里插入图片描述

pageContext域对象的生命周期

pageContext这个域对象的生命周期——即往pageContext这个域对象存的东西,哪里可以取出来?pageContext这个域对象的生命周期在整个页面范围之类,也就是说服务器在执行这个JSP的时候,上来就帮你创建pageContext,服务器把这个JSP执行完了之后,那这个pageContext就死了,意味着往pageContext这个域对象存的东西,只能在这个页面范围内取出来。pageContext这个域我们称之为page域,它是JavaWeb开发中最小的域

pageContext引入和跳转到其他资源

PageContext类中定义了一个forward方法(用来跳转页面)和两个include方法(用来引入页面)来分别简化和替代RequestDispatcher.forward方法和include方法。方法接收的资源如果以/开头, /代表当前Web应用。
例一,使用pageContext的forward方法跳转到其他页面。

  • 11.jsp页面的内容如下:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>使用pageContext的forward方法跳转页面</title>
    </head>
    <body>
    	<%
    		/*
    		 * 使用pageContext的forward方法跳转到1.jsp页面,"/"代表了当前的Web应用
    		 * 使用pageContext.forward(relativeUrlPath)替代了RequestDispatcher.forward(relativeUrlPath)
    		 */
    		pageContext.forward("/1.jsp");
    		
    		// 使用RequestDispatcher的forward方法实现的跳转方式
            // pageContext.getRequest().getRequestDispatcher("/1.jsp").forward(request, response);
    	%>
    </body>
    </html>
    
  • 1.jsp页面的内容如下:

    <%@page import="java.util.Date"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>JSP入门案例——输出当前时间</title>
    </head>
    <body>
    	<font color="red">
    		当前时间值:
    		<%
    			Date date = new Date();
    			String time = date.toLocaleString();
    		%>
    		
    		<%=time %> <!-- 脚本表达式,它的作用就是用于向浏览器输出数据 -->
    	</font>
    </body>
    </html>
    

浏览器访问11.jsp页面的运行结果如下:
在这里插入图片描述
在实际开发中,使用pageContext.forward(relativeUrlPath)方法跳转页面用得不多,主要是因为要在JSP页面中嵌套Java代码,所以这种做法简单了解一下即可,在开发中,要想从一个JSP页面采用服务器端跳转的方式跳转到另一个JSP页面,那么一般会使用<jsp:forward>标签,<jsp:forward>标签用于把请求转发给另外一个资源。
例二,使用pageContext的include方法引入资源。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>使用pageContext的include方法引入资源</title>
</head>
<body>
	<%
        pageContext.include("/public/head.jsp");
    %>
    	使用pageContext的include方法引入资源<br/>
    <%
        pageContext.include("/public/foot.jsp");
    %>
</body>
</html>

运行结果如下:
在这里插入图片描述
在实际开发中,使用pageContext的include方法引入页面这种做法也很少用,一般都使用<jsp:include>标签引入资源,因此这种做法了解一下即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李阿昀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值