Head First Servlets and JSP(二)

第七章 作为JSP:使用JSP

在JSP中可以使用scriptlet(即放在<%…%>标记中的java代码)放入常规的java代码;
使用page指令导入包;
表达式元素自动打印放在标记之间的内容;

<%@ page import="foo.*,java.util.*"%>
<html>
<body>
The page count is:
//不要在表达式的最后加上分号
<%=Counter.getCount()%>
</body>
</html>

三种JSP元素:

  • Scriptlet: <% %>
  • 指令: <%@ %>
  • 表达式: <%=%>(表达式会成为out.print()的参数)

所有scriptlet和表达式代码都放在服务方法中,scriptlet中声明的变量总是局部变量——在scriptlet中声明变量意味着每次运行服务方法时这个变量都会重新初始化;
JSP元素:<%!和%>标记之间的所有内容都会增加到类中——变量和方法都可以声明;
容器把JSP转换为servlet时,服务方法最前面有一堆隐式对象声明和赋值。
JSP中两种不同类型的注释:
<!–HTML注释–>:容器把它直接传递给客户,在客户端浏览器会把它解释为注释;
<%–JSP注释–%>:原文注释;
容器根据你的JSP生成一个类,这个类实现了HttpJspPage接口,关于容器生成的servlet的API:

  • jspInit():这个方法由init()方法调用可以覆盖;
  • jspDestroy():这个方法由servlet的destroy()方法调用,也可覆盖;
  • _jspService():这个方法由servlet的service()方法调用,不能覆盖;
    如果 一个Web应用包含JSP,部署这个应用时,在JSP生命周期中,整个转换和编译步骤只发生一次
    初始化JSP:
    配置servlet初始化参数,必须在<servlet>标记中增加一个<jsp-file>元素;
    为一个JSP定义servlet时,还必须为JSP页面定义一个servlet映射;
    覆盖jspInit()
<%!//使用声明覆盖jspInit()方法
	public void jspInit(){
		ServletConfig sConfig=getServletConfig();//在一个servelt中可以调用继承的方法
		String eamilAddr=sConfig.getInitParamenter("email");
		ServletContext ctx=getServletContext();//得到Servlet的一个引用,并设置一个应用作用域属性
		ctx.setAttribute("mail",eamilAddr);

JSP使用4个隐式对象得到和设置对应JSP中4个属性作用域的属性;
属性作用域
可以使用PageContext引用来得到任意作用域的属性,包括从页面作用域得到绑定到PageContext的属性;
三个指令:

指令作用
<%@ page import=“foo.*” session=“false”%>定义页面特定的属性
<%@ taglib tagdir=“WEB-INF/tags/cool” prefix=“cool”%>定义JSP可以使用的标记库
<%@ include file=“wickedHeader.html” %>定义在转换时增加到当前页面的文本和代码

EL:用途是提供一种更简单的方法来调用java代码,但是代码本身放别的地方;
使用<scripting-invalid>标记让JSP禁用脚本元素;

第八章 没有脚本的页面:无脚本的JSP

使用表达式语言(EL)和标准动作构建JSP页面
使用几个标准动作——消除JSP中的脚本代码(包括声明、scriplet和表达式);

//使用标准动作(不使用脚本)
<html><body>
<jsp:useBean id="person" class="foo.Person" scope="request"/>
Person created by servlet:<jsp:getProperty name="person" property="name"/>
</body></html>

<jsp:useBean>用来声明和初始化在<jsp:getProperty>中使用的具体bean对象;
用<jsp:getProperty>得到bean属性的性质值;
如果<jsp:useBean>找不到对应名的属性对象,会新建一个:
useBean
利用<jsp:useBean>体,可以有条件地运行代码,只有找不到bean属性,而且创建了一个新bean时才会运行体中的代码;(bean没有带参数的构造函数)
建立多态的引用,向标记增加type属性,例如:

<jsp:useBean id="person" type="foo.person" class="foo.Employee" scope="page">

如果在<jsp:useBean>或<jsp:getProperty>标记中没有指定作用域,容器会使用默认作用域"page";
type不同于class

利用param属性,可以把bean的性质值设置为一个请求参数的值。只需指定请求参数;
如果请求参数名与bean性质名匹配,就不需要在<jsp:setProperty>标记中为该性质指定值;
Bean标记会自动转换基本类型的性质;
当心
利用<jsp:getProperty>,只能访问bean属性的性质,不能访问嵌套性质——使用EL(JSP表达式语言);
EL表达式:

${person.name}//放在大括号里,且前面有一个美元前缀符

表达式中的第一个命名变量可以是一个隐式对象,也可以是一个属性;
EL语言规范
使用点号(.)操作符访问性质和映射值;
如果表达式变量后有一个点号,点号左边的变量必须是一个Map或一个bean;
点号右边必须是一个Map键或一个bean性质;
或者使用中括号:
中括号
数组和List中的String索引会强制转换为int;
EL会显示原始文本,包括HTML
EL有一些隐式对象。x想从请求得到HTTP请求方法,使用requestScope会得到请求性质,而不是request性质,要得到request性质,需要通过pageContext;

Method is:${pageContext.request.method}

EL的隐式对象
可以编写简单的EL表达式,调用自己写的普通Java类的静态方法;

  • 编写一个有公共静态方法的Java类;
  • 编写一个标记库描述文件(TLD)——提供了定义函数的Java类与调用函数的JSP之间的一个映射;
  • 在JSP中放一个taglib指令,定义命名空间;
  • 使用EL调用函数:按照${prefix:name()}的形式从表达式调用函数;

例如:

<%@ taglib prefix="mine" uri="WEB-INF/foo.tld"%>

对于可重用的模板部件,JSP有一个处理机制——包含(include);
inlcude指令告诉容器:复制所包含文件的内同并粘贴到这个文件中;
<jsp:include>标准动作内部原理不同于include指令——include在转换时发生,<jsp:include>标准动作在运行时发生;
不要把开始和结束HTML和BODY标记放在可重用部件中;
使用<jsp:param>定制包含的内容:
完成包含的JSP:

<html><body>
<jsp:include page="Header.jsp">//没有结束反斜线
	jsp:param name="subTitle" value="Whatever"/>
</jsp:include>
</body></html>

使用新参数的被包含页眉:

<img src="images/Web-Services.jpg"><br>
<em><strong>${param.subTitle}</strong></em><br>

从一个jsp转发到另一个jsp或者servlet:<jsp:forward>标准动作;
提供条件转发的JSP(Hello.jsp)

<html><body>
Welcome to our page!
<% if (request.getParameter("userName")==null){%>
	<jsp:forward page="HandleIt.jsp"?>
<% } %>
Hello ${param.userName}
</body></html>

请求转发到的目标JSP(HandleIt.jsp)

<html><body>
you need to log in again.
<form action="Hello.jsp" method="get">
Name: <input name="userName" type="text">
<input name="Submit" type="submit">
</form>
</body></html>

发生转发时,请求转发到的目标资源首先会清空响应缓冲区——不要先刷新输出再转发;

第九章 强大的定制标记:使用JSTL

使用JSTL之前,需要将文件(“jstl.jar"和"standard.jar”)放在Web应用的WEB-INF/lib 目录中;
使用<c:out>标记转换HTML特殊字符为实体格式;
用c:out标记用户串防止cross-site scripting攻击
<c:out>提供一个default属性

<b> Hello <c:out value='${user}' default='guest' />.</b>

<c:forEach>不用脚本实现循环
迭代处理数组和集合
var属性存放集合中的各个元素,items对应具体要循环处理的集合(数组、collection、Map);可选属性varStatus得到一个循环计数器;
"var"变量的作用域仅限于标记内部;
使用<c:if>完成条件包含,例如(成员在页面看到更多的东西)

//JSP代码
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html><body>
<strong>Member comments</strong><br>
<hr>${commentList}<hr>//假设有一个servlet会根据用户的登录信息设置useType属性
<c:if test="${userType eq 'member'}">//标记和EL中单引号和双引号都能用
	<jsp:include page="inputComments.jsp"/>//此处省略input.Comments.jsp的构造
</c:if>

if/else的构造——<c:choose>、<c:when>和<c:otherwise>实现;
<c:set>的两个版本,var版本设置属性变量,target版本设置bean性质或Map值(一些要点和技巧)
使用<c:remove>删除一个属性;
除了include指令、<jsp:include>标准动作,使用<c:import>将资源的内容增加到JSP中;
<c:import>JSTL标记
JSP中URL的重写:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html><body>
This is a hyperlink with URL rewriting enabled.
<a href="<c:url value='/inputComments.jsp' />">Click here</a>
//在"value"相对URL的最后增加jsessionid,如果禁用了cookie.
</body></html>

URL编码把不安全/保留的字符替换为其他字符,然后在服务器端完成解码;<c:url>不会自动地对URL编码;在<c:url>的体中使用<c:param>,完成URL的重写和编码;

设计定制页面处理错误,然后使用page指令配置错误页面;

//指定的错误页面("erroePage.jsp")
<%@page isErrorPage="true" %>
<html><body>
<strong>Bummer.</strong>
You cause a ${pageContext.exception} on the server.<br>//可以得到详细错误信息
//容器为错误页面提供了一个额外的exception对象
<img src="images/bummerGuy.jpg">
</body></html>

//抛出异常的“坏”页面("badPage.jsp")
<%@ page errorPage="erroePage.jsp" %>
<html><body>
About to be bad...
<% int x=10/0;%>//使页面抛出异常
</body></html>

可以在DD中为整个Web应用声明错误页面,甚至可以为不同的异常类型或HTTP错误码类型(404,500等)配置不同的错误页面;
使用<c:catch>标记完成一种try/catch,把有风险的标记或表达式包起来;
想在<c:catch>标记结束后访问异常,可以使用可选的var属性,把异常对象放在页面作用域,并按照声明的var值来命名;
使用非JSTL的标记库:URI是标记库描述文件(TLD)的唯一标识符,容器需要这个信息将JSP中使用的标记名映射到实际调用的java代码;
当标记的<body-content>元素值不是empty时,这个标记才能有体;
可以把TLD看作定制标记的API;
taglib<uri>只是一个名,而不是一个位置;
容器会自动建立TLD和<uri>名之间的映射;如果JSP使用多个标记库,确保taglib uri名是唯一的,不要使用保留的前缀;

第十章 定制标记开发

利用标记文件,可以使用一个定制标记调用可重用的内容;
在JSP中放一个taglib指令,并调用这个标记:

<%@ taglib prefix="myTags" tagdir="/WEB-INF/tags" %>
<html><body>
<myTags:Header/>
Welcome to our site.
</body></html>

对于标记文件,发送的不是请求参数,而是标记属性;
标记作用域
标记文件使用attribute指令;这个指令只能由标记文件使用;
attribute指令
对于标记属性很长的内容可以放在标记的体中,使用tag指令声明body-content类型;标记文件标记的体重不能使用脚本代码(默认scriptless),除非使用"empty"或"tagdependent",可以声明body-content;
如果标记文件在一个JAR中部署,就必须有一个TLD,如果直接放在Web应用中,就不需要TLD;
建立一个简单的标记处理器:
建立标记处理器
如果标记需要一个属性,就要在TLD中声明,并在标记处理器中为每个属性提供一个bean式的设置方法;
JspFragment是表示JSP代码的一个对象;getJspContext()实际上是标记体向其他对象提供信息的一个途径;
SkipPageException:停止处理页面,会显示异常出现之前的所有内容;
Tag处理器API有5个接口和3个支持类;
简单标记只有一个doTag(),但是传统标记有一个doStartTag()和一个doEndTag(),有一个doAfterBody()方法,在计算体之后并在doEndTag()运行前调用;
传统标记生命周期取决于返回值;
如果没有覆盖返回一个整数的TagSupport生命周期方法,要当心TagSupport方法实现返回的默认值;
标记处理器类必须为TLD中声明的每个标记属性实现一个设置方法,使用DynamicAttribute接口:
DynamicAttribute接口
为使用DynamicAttributes,标记处理器类必须实现JSP API的DynamicAttributes接口,碧必须实现setDynamicAttribute()方法,使用hashmap存储属性名/值对;
通过扩展BodyTagSupport,能从Body-Tag接口得到另外两个生命周期方法——setBodyContent()和doInitBody();
标记可以调用其父标记;
标记处理器类不是一个servlet或JSP,但有PageContext的一个引用;
标记文件的查找位置(复习):
标记文件的查找

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值