1、JSP头部信息
1.1 3种指令
page指令的目的:
为容器提供JSP转换为Servlet时所需要的一些信息。我们需要关心的属性有:import、session、contentType、isELIgnored。
taglib指令
<%@taglib tagdir="WEB-INF/tags/cool" prefix="cool" %>
定义JSP可以使用的标记库。
include指令
<%@ include file="wickedHeader.html" %>
定义在转换时增加到当前页面的文本和代码。从而允许你建立可重用的块(如标准页面标题或导航栏)。该标记可以将该重用块添加到各个需要的页面中,而不必在每个JSP中重复写这些代码。
1.2 page指令的常用属性
import
导入所需的包。生成Servlet会自动导入以下包:java.lang、javax.servlet、javax.servlet.http和javax.servlet.jsp
contentType
定义JSP响应的MIME。
isELIgnored
定义转换这个页面时是否忽略EL表达式。
isErrorPage
定义当前页面是否是另一个JSP的错误页面。默认值为“false”。如果为true,页面就能访问隐式的exception对象。
errPage
定义一个资源的URL,如果有未捕获到的Throwable,就会发送到这个资源。如果这个属性制定了一个JSP,该JSP的page指令的isErrorPage值必为true。
pageEncoding
定义JSP的字符编码。
2、Scriptlet
<% %>之间的代码在编译成Servlet的时候全部放在Service函数中。即:
但是如果我们想在生成Servlet时生成自己的方法怎么办?即我们想在_jspService方法之外生成一个自己定义的方法。或者内部类亦或是一般全局变量(由于Servlet不是线程安全的,所以尽量不要创建全局变量)。
没错。我们只需将代码放在<%! %>之间。
3、JSP隐式对象
JspWriter与HttpServletResponse得到的PrintWriter之间的区别:
JspWriter不能取代PrintWriter(因为JspWriter只存在与Jsp页面)但是大多数的打印方法与PrintWriter的相同,只不过增加了缓冲功能。
4、JSP的生命周期
1、容器“读取”web.xml,但是对.jsp文件不做任何处理(直到得到第一个请求)
2、客户端请求这个.jsp,①容器将.jsp转换为一个Servlet类的.java源代码。------在这个阶段检查是否有jsp语法错误。(如果有jsp语法错误,则转换失败)
②容器把这个servlet的.java源代码编译为一个.class文件。--------在这个阶段检查java语法错误。
③容器加载新生的servlet类、实例化servlet、调用jspinit()方法,.class文件成为了一个真正的Servlet。
这个转换过程只发生一次。以后的调用只是把JSP当成Servlet来访问。所以第一次访问JSP页面是会比较慢。但是许多开发商提供提供了JSP的预编译。JSP规范推荐的一种JSP预编译协议:向JSP做出请求时可以追加一个查询串“?jsp_precompile”,容器会(如果支持)立即完成转换和编译,而不是等到异地请求真正到来的时候才进行转换和编译。
例如:
我们有一个Login.jsp,该jsp没有配置为立即转换和编译。只有在第一次请求该页面的时候才会被转换和编译:
5、初始化JSP
要使JSP立即被转换和编译只需在web.xml中配置一下即可:
由4中的例子可知,在web.xml中配置过的jsp在容器启动时就会立即转换并编译为Servlet。
如果我们想让JSP初始化时做一些工作,就像Servlet的init方法的作用一样,该怎么办呢?-------->覆盖jspInit()
该方法是由Servlet的Init方法调用,所以在jspInit方法中我们可以直接调用getServletConfig()和getServletContext();来得到ServletConfig和ServletContext。获取的参数即是在web.xml中配置的参数。如上所示。覆盖jspInit方法实例如2所示。
6、JSP属性
还记得吗?Servlet中有3种作用域的属性,而JSP中有4种作用域的属性,在Servlet的基础上加了一个页面作用域。
|
可访问性 |
作用域 |
适用于 |
Servlet中 |
JSP中(使用隐式对象) |
Context(上下文)
不是线程安全的 |
web应用的所有部分:Servlet、JSP、ServletContextListener、ServletContextAttributeListener |
如果服务器或应用关闭,上下文则撤销 |
希望为整个应用共享资源。如:数据库连接,email地址等等 |
getServletContext(). |
application. |
HttpSession(会话)
不是线程安全的 |
访问这个特定会话的所有Servlet和JSP。 |
会话的生命周期,可以通过编程撤销,也可以通过超时撤销 |
与客户会话有关的资源和数据。 |
request. |
request. |
Request(请求)
线程安全 |
应用中能直接访问请求对象(httpRequest)的所有部分。 |
就是Servlet的service方法。 |
|
request.getSession() |
session |
page(页面) |
|
|
|
没有 |
pageContext. |
正如3中提高的,
可以从pageContext中得到其他隐式对象。也可以从pageContext中得到所有作用域的属性(只需添加一个表示作用域的int参数)
6.1 使用pageContext获得和设置属性
JSP 页面中的操作和模板数据是使用 JspWriter 对象编写的,该对象由隐式变量 out 引用,而此变量是使用 PageContext 对象中的方法自动初始化的。
此抽象类模仿 java.io.BufferedWriter 和 java.io.PrintWriter 类中的一些功能,但又不同于这些类,因为它从 print 方法抛出 java.io.IOException,而 PrintWriter 不会这样做。
缓冲
初始 JspWriter 对象与 ServletResponse 的 PrintWriter 对象关联,关联方式取决于页面是否被缓冲。如果页面未被缓冲,则写入此 JspWriter 对象的输出将直接写入
PrintWriter,如有必要,将通过对响应对象调用 getWriter() 方法创建 PrintWriter。但是,如果页面被缓冲,则在刷新缓冲区之前不会创建
PrintWriter 对象,并且 setContentType() 之类的操作是合法的。因为这种灵活性显著简化了编程,所以默认情况下将对 JSP 页面进行缓冲。
缓冲会带来超出缓冲区时该如何处理的问题。可以采用两种方法:
- 超出缓冲区不是致命错误;在超出缓冲区时,只需刷新输出即可。
- 超出缓冲区是致命错误;在超出缓冲区时,引发一个异常。
两种方法都有效,并且在 JSP 技术中都得到支持。页面的行为由 autoFlush 属性控制,该属性的默认值为 true。通常,需要确保已将正确且完整的数据发送到其客户端的 JSP 页面可能想将
autoFlush 设置为 false,比如客户端是应用程序自身的情况。另一方面,发送有意义的数据(即使只构造了一部分时)的 JSP 页面可能想将 autoFlush 设置为 true;比如发送要通过浏览器立即显示的数据时。每个应用程序都需要考虑自己的特定需求。
一个可以考虑的选择是不设置缓冲区大小界限;但是,这又带来了计算不受控制的弊病,将消耗数量无法估计的资源。
JSP 实现类的 "out" 隐式变量就属于这种类型。如果页面指令选择 autoflush="true",那么在导致溢出情况时,或者在当前操作是在没有进行刷新的情况下执行的时候,此类上的所有 I/O 操作都将自动刷新缓冲区的内容。如果 autoflush="false",那么在执行当前操作将导致缓冲区溢出的情况下,此类上的所有 I/O 操作都将抛出 IOException。
JspWriter
JspContext
pageContext继承自JspContext。
getAttribute、setAttribute使用不带作用域参数的版本与带 PageContext.PAGE_SCOPE 的版本是等价的。即:getAttribute、setAttribute默认的作用域参数是PageContext.PAGE_SCOPE 即页面作用域 。
findAttribute 获取的属性是那个作用域呢?
findAttribute 查找路径为:
①在页面上下文中查找。如果查到,相当于pageContext.getAttribute();
②在请求作用域中查找
③在会话作用域中查找
④在应用作用域中查找
按照这样的路径,直到查找到为止,便不再继续往下查找了。顺序为范围的由小到大