第六章使用JSP
目录
1、从JSP到Servlet
1.1JSP生命周期
JSP与Servlet是一体的两面。基本上Servlet能实现的功能,使用JSP也能做到,因为JSP最后还是会被容器转译为Servlet源代码、自动编译为.class文件、载入.class文件,然后生成Servlet对象。
如果是在Tomcat或Glassfish中,由于转译后的Servlet是继承自HttpJspBase类,所以打开该类,就可以发现为什么:
1.2Servlet至JSP的简单转换
每个JSP中的元素,都可以对照至Servlet中的某个元素或代码,如指示元素、隐式元素、Scriptlet元素、操作数元素等,都与Servlet有实际的对应,所以要了解JSP,必先了解Servlet。
1.3指示元素
JSP指示(Directive)元素的主要目的,在于指示容器将JSP转译为Servlet源代码时,一些必须遵守的信息。指示元素的语法如下:
<%@ 指示类型 [属性="值"]* %>
在JSP中有三种常用的指示类型:page、include与taglib。page指示类型告知容器如何转译目前的JSP网页。include指示类型告知容器将别的JSP页面包括进来进行转译。taglib指示类型告知容器如何转译这个页面中的标签库(Tag Library)。
1.page指示类型
page指示类型的import属性告知容器转译JSP时,必须在源代码中包括的import陈述。可以在同一个import属性中,使用逗号分隔数个import的内容。
page指示类型的contentType属性告知容器转译JSP时,必须使用HttpServletRequest的setContentType(),调用方法时传入的参数就是contentType的属性值。
pageEncoding属性则告知这个JSP网页中的文字编码,以及内容类型附加的charset设置。如果网页中包括非ASCII编码范围中的字符(如中文),就要指定正确的编码格式,才不会出现乱码。
page指示类型还有一些可以设置的属性如下:
2.include指示类型
include指示类型,用来告知容器包括另一个网页的内容进行转译。指令元素include来包括其他页面内容时,会在转译时期就决定转译后的Servlet内容,是一种静态的包括方式。最后只会生成一个Servlet。
1.4声明、Scriptlet与表达式元素
JSP网页会转译为Servlet类,转译后的Servlet类应该包括哪些成员、方法声明或哪些语句,在编写JSP时,可以使用声明元素、Scriptlet元素及表达式元素来指定。
1.声明元素
<%! 类成员声明或方法声明 %>
在<%!与%>之间声明的代码,都将转译为Servlet中的类成员或方法,之所以成为声明元素,是指它用来声明类成员或方法。所以使用<%!与%>声明变量时,必须小心数据共享与线程安全的问题。如果有一些初始化操作,想要在JSP载入时执行,则可以重新定义jspInit()方法,或是在jspDestroy()中定义结尾动作。定义jspInit()与jspDestroy()的方法,就是在<%!与%>之间进行,这样转译后的Servlet源代码,就会有相对应的方法片段出现。
2.Scriptlet元素
<% Java语句 %>
在声明元素中可以编写Java语句,就如同在Java的方法中编写语句一样。事实上,<%与%>之间所包括的内容,将被转译为Servlet源代码_jspService()方法中的内容。
直接在JSP中编写的HTML,都会变成out对象所输出的内容。Scriptlet出现的顺序,也就是在转译为Servlet后,语句出现在_jspService()中的顺序。
3.表达式元素
<%= Java表达式 %>
可以在表达式元素中编写Java表达式,表达式的运算结果将直接输出为网页的一部分。注意,表达式元素中不用加上分号(;)。
1.5注释元素
JSP网页中可以在<%于%>之间直接使用Java语法编写程序,所以可在其中使用Java的注释方式来编写注释文件。另一个时HTML网页使用的注释方式<!--与-->。JSP有一个专用的注释,即<%--与--%>。容器在转译JSP至Servlet时,会忽略<%--与--%>之间包括的文字。生成的Servlet中不会包括注释文字,也不会输出至浏览器。
1.6隐式对象
1.out隐式对象
out隐式对象在转译后,会对应于javax.servlet.jsp.JspWriter类的实例,JspWriter则直接继承java.io.Writer类。JspWriter主要模拟了BufferWriter与PrintWriter的功能。JspWriter在内部也是使用PrinterWriter来进行输出,但JspWriter具有缓冲功能。当使用JspWriter的print()或println()进行响应输出时,如果JSP页面没有缓冲,则直接创建PrintWriter来输出响应,如果JSP页面有缓冲,则只有在清除(flush)缓冲区时,才会真正创建PrintWriter对象进行输出。
对页面进行缓冲处理,表示在缓冲区满的时候,可能有两种处理方式:
- 累积缓冲区的容量后再一次输出响应,所以缓冲区满了就直接清除。
- 你也许是想控制输出的量在缓冲区容量之内,所以缓冲区满了表示有错误,此时要抛出异常。
2.page指示对象
在编写JSP页面时,可以通过page指示元素的buffer属性来设置缓冲区的大小,默认值是8kb。缓冲区满了之后该采取哪种行为,则是由autoFlush属性决定值,默认值是true,表示满了就直接清除。如果设置为false,则要自行调用JspWriter的flush()方法来清除缓冲区,如果缓冲区满了却还没调用flush()将数据送出至客户端,调用println()时将会抛出IOException异常。
3.pageContext隐式对象
pageContext隐式对象转译后对应于javax.servlet.jsp.PageContext类型的对象,这个对象将所有JSP页面的信息封装起来,转译后的Servlet可通过pageContext来取得所有的JSP页面信息。所有的隐式对象都可以通过pageContext来取得。除了封装所有的JSP页面信息之外,还可以使用pageContext来设置页面范围属性。同样是使用setAttribute()、getAttribute()与removeAttribute()来进行设置。默认是可设置或取得页面范围属性,页面范围属性表示作用范围仅限同一页面中。
以pageContext提供单一的API来管理属性作用范围,可以使用以下方法来设置:
getAttribute(String name, int scope)
setAttribute(String name, Object value, int scope)
removeAttribute(String name, int scope)
其中的scope可以使用以下常数来进行指定:pageContext.PAGE_SCOPE、pageContext.REQUEST_SCOPE、pageContext.SESSION_SCOPE、pageContext.APPLICATION_SCOPE。分别表示页面、请求、会话与应用程序范围。
1.7错误处理
JSP终究会转译为Servlet,所以错误可能发生在三个时候。
- JSP转换为Servlet源代码时。
- Servlet源代码进行编译时。
- Servlet载入容器进行服务但发送运行时错误。
2、标准标签
2.1<jsp:include>、<jsp:forward>标签
1.<jsp:include>标签
<jsp:include page="add.jsp">
<jsp:param name="a" value="1" />
<jsp:param name="b" value="2" />
</jsp:include>
2.<jsp:forward>
<jsp:forward page="add.jsp">
<jsp:param name="a" value="1" />
<jsp:param name="b" value="2" />
</jsp:forward>
<jsp:include>或<jsp:forward>标签,在转译为Servlet源代码之后,底层也是取得RequestDispather对象,并执行对应的forward()或include()方法。
2.2<jsp:useBean>、<jsp;setProperty>与<jsp:getProperty>
<jsp:useBean>标签是用来搭配JavaBean元件的标准标签,这里指的JavaBean并非桌面系统或EJB中的JavaBean元件,而是只要满足以下条件的纯粹Java对象。
- 必须实现java.io.Serializable接口
- 没有公开(Public)的类变量
- 具有无参数的构造器
- 具有公开的设值方法(Setter)与取值方法(Getter)
<jsp:useBean>标签用来取得或创建JavaBean。id属性用于指定JavaBean实例的参考名称,之后在使用<jsp:setProperty>或<jsp:getProperty>标签时,就可以根据这个名称来取得所创建的JavaBean名称。class属性用以指定实例化哪一类。scope指定可先查找哪个属性范围是否有JavaBean的属性存在。
<jsp:setProperty>标签用于设置JavaBean的属性值。name属性用于指定要使用哪个名称取得JavaBean实例。在property属性设置为“*”时,表示自动寻找符合JavaBean中设值方法名称的请求参数值。如果请求参数名称为XXX,就将请求参数值使用setXXX()方法设置给JavaBean实例。
<jsp:getProperty>用来取得JavaBean的属性值。name属性用于指定要使用哪个名称取得JavaBean实例。property属性则指定要取得哪一个属性值。如果指定为XXX,则使用getXXX()方法取得JavaBean属性值并显示在网页上。
2.3深入<jsp:useBean>、<jsp:setProperty>与<jsp:getProperty>
JSP网页最终将转换为Servlet,所谓的JavaBean,实际上也是Servlet中的一个对象实例。当使用JavaBean时,实际上就是在声明一个JavaBean的对象,id属性即是用以指定参考名称与属性名称,而class属性则是类型名称。
使用<jsp:useBean>标签时,会在属性范围(默认是page范围)中寻找有无id名称所指定的属性。如果找到就直接使用,如果没有找到就创建新的对象。可以在使用<jsp:useBean>标签时,使用scope属性指定储存的属性范围,可以指定的值有page(默认)、request、session与application。
在转译后的Servlet代码中,如果想指定声明JavaBean时的类型,则可以使用type属性。type属性的设置可以是一个抽象类,也可以是一个接口。
标签的目的是减少JSP中Script的使用。
2.4谈谈Model1
2.5XML格式标签
3、表达式语言(EL)
3.1EL简介
JSP中若有用Scriptlet编写的Java代码,以进行属性、请求参数、标头与Cookie等信息的取得,或一些简单的运算或判断,可以试着使用EL来取代,以减少JSP页面上Scriptlet的使用。
EL是使用${与}来包括所要进行处理的表达式,可使用点运算(.)指定要存取的属性。param是EL隐式对象之一,表示用户的请求参数。
pageContext也是EL的隐式对象之一,通过点运算符之后接上xxx名称,表示调用getXxx()方法。如果必须转换类型,EL也会自行处理。
可以使用page指示元素的isELIgnored属性(默认是false),来设置JSP网页是否使用EL。
3.2使用EL取得属性
如果EL访问的对象是个数组或List类型的对象,则可以使用[]运算符来指定索引以访问元素。
- 如果使用点(.)运算符,则左边可以是JavaBean或Map对象。
- 如果使用[]运算符,则左边可以是JavaBean、Map、数组或List对象。
3.3自定义EL函数