本文内容主要介绍EL表达式、JSP标准标签库(JSTL)、JSP自定义标签库。其中拿EL表达式和JSP技术做对比。而JSP标准标签库和JSP的自定义标签内容都是较为粗略地带过。
EL标签
EL表达式我的理解是能够方便地从request、session等各个域中获取变量值。在这之前,我们通过JSP技术来获取这些变量值,使用JSP技术和使用EL表达式的示例代码分别如下所示:
<body>
用户名:<%=request.getAttribute("username")%>
密码:<%= request.getAttribute("password")%>
</body>
使用了EL表达式的代码如下所示:
<body>
用户名:${username}
密码:${password}
</body>
由此可见,使用EL表达式来获取变量比使用JSP技术要简洁得多。在上例中,使用JSP技术获取变量时,需要使用隐式对象(如这里的request)调用getAttribute()方法来实现,而EL表达式不需要调用隐式对象。
说到隐式对象,JSP有9个隐式对象,而EL有11个隐式对象,但是这不代表EL就有更多的功能。事实上,EL隐式对象中,param、paramValues、header、headerValues、cookie都是针对request而言的,也就是说这五个隐式对象都是为了获取请求信息的方便。所以单独从功能的实现不考虑便捷程度的话,EL中的requestScope已经可以实现这四个隐式对象的功能。11-5=6,这么一算,剩下六个隐式对象,反而比JSP的9个隐式对象要少三个。
其中JSP中的config对应EL中的initParam。
少了哪一些呢,少了out、response、exception。我们可以看到,少了有两个用于输出和响应的。而在这方面EL表达式并没有对应的隐式对象。所以我的理解是EL表达式中隐式对象的目的在于简化对request对象的信息获取过程。而输出和报错等功能,我们仍旧可以沿用JSP技术提供的内容,并不会显得复杂。
EL表达式和JSP技术的隐式对象中,pageContext是二者所共有的。在EL表达式中,也可以通过pageContext.request来访问到本属于JSP技术的隐式对象。
它的好处无需调用getAttribute()的方法,这时候可以写成${pageContext.request.contentType}。
JSP技术中是通过调用getAttribute()的方法来实现的,写法繁杂。并且如果采用这种方法,我们就必须先选择我们的隐式对象,需要先明确知道我们的变量存放在哪个隐式对象中。而在EL表达式中,可以不指定查找域,直接引用属性名就可以,然后会在page、request、session、application这四个作用域内按顺序依次进行查找。
而现在可以说我们就只需要关注变量本身了,只需要知道变量名,就能够获取变量值。
JSP标准标签库
JSTL =J
ava Server Pages S
tandard T
ag L
ibrary
JSTL虽然被称为标准标签库,但是这个标签库是由五个不同功能的标签库共同组成的。
标签库 | 前缀 |
---|---|
Core | c |
I18N | fmt |
SQL | sql |
XML | x |
Functions | fn |
从名字上也不难猜出来,这些标签库具有不同的功能。其中Core是核心标签库,包含web应用中通用操作的标签。SQL是一个数据库标签库,包含用于访问数据库和对数据库中的数据库进行操作的标签(但是比较少用,因为一般不会直接在jsp页面操作数据库)。而XML则是用于操作XML文档的标签库。
这里我们先来搞清楚EL表达式
、JSP技术
、JSP标签
概念和结构上的差别。
首先上面说的EL是表达式,不用放在标签中。JSP技术要求将代码写在<% %>这个“标签”里面,但这是为了将Java代码和Html页面的其他内容区分开所使用的方法,不能叫JSP标签,只能叫JSP技术。而这里的JSP标签则是真真正正使用标签对的方法。
本来,我们理所应当认为JSP标签对应该是使用JSP技术来实现的。但却发现,在JSP标签中使用的是EL技术来获取变量,而不是JSP技术。例如:
<c:out value="${param.username}">unknown</c:out>
所以可以说, JSP标签
使用了EL技术
来获取变量
,EL技术替代了
使用JSP技术
获取变量的方法。本来直接使用JSP获取变量就可以了,这里使用EL技术是为了方便。当然如果有其他方面的需求,还是要利用JSP中的其他技术。例如当变量不存在session等域中,这时候还是要用JSP技术才能获取到该变量。例如创建一个对象或者创建一个Map对象。如果不将其保存到session等域中,那么JSTL标签是没有办法获取的。**
<%
String [] fruits= {"apple","orange","grape","banana"};
%>
<c:foreach var="name" items="<%= fruits %>">
${name}<br>
</c:foreach>
在上面的代码中,无论是创建数组,还是引用都是用jsp技术。为什么引用对象不用EL表达式${fruits}呢。这是因为JSTL标签虽然获取变量方便,但是它能够获取的只是request、session、context、application等域的变量。(当然现在有了<c:set>标签,也能在jsp页面新建一些变量,这部分也能够获取)但是如果本来就是在<% %>创建的数组fruits,而这个数组有没有保存在某个域中,那么自然就只能够通过<% %>方式来获取了,也就是说只能使用回JSP技术了。
如果说EL技术满足了我们读取信息的需求,并且让我们在读取信息的时候可以更专注于变量本身,而不需要去通过域对象获取的话,那么Core标签库在一定程度上就满足了我们输出、更改等方面的需求。
修改javabean中的属性的方法见下例:
<c:set value="itcast" target="${user}" property="username"/>
先通过找到user对象,然后再对其属性进行更改。
除此以外,Core标签库还有<c:out><c:set><c:remove><c:catch>等标签,我们也就发现原来JSP多出来而EL中少的几个隐式对象(out、response、exception),都可以在Core标签库中找到更便捷的解决方法。而且除了这些功能外,还能进行一些简单的逻辑判断(<c:if><c:choose>等标签)。
而JSTL中的function标签库则是为了简化JSP页面对字符串的操作
。例如fn:split
用于对字符串进行分割,而fn:join
用于将字符串数组合并成一个字符串。并且这个function标签库虽然说是标签库,但其实也没有放在标签里面,更类似于EL表达式,存在于花括号中。
说实在话,上面的标签看起来功能很多,但是主要也只是完成了属性的获取和字符串的操作这两个功能。还有许许多多无能为力的方面。
而这些功能可以通过自定义标签来实现。我们可以说自定义标签库就是一段段小代码。有什么好处呢。首先将代码对应成标签,然后调用标签来进行执行,本身就可以减少很多的代码重复。
另一方面,可以结合JSP技术和EL技术,换句话说,可以很方便地获取到应用内的各个变量,也降低了代码量。之前说过,不希望JSP页面出现太多的代码,以往的解决方法是将代码部分都转移到其他的Servlet中,然后将结果返回到JSP页面,JSP页面只执行显示功能,没有太多定制功能。而现在则是利用标签来减少代码量,JSP页面本身就能够定制结果的显示,甚至能执行一定的功能。
JSP自定义标签库
JSP自定义标签库功能强大,但是可以简单理解为将Java代码以“标签库”的方式进行引用。这部分内容也有很多容易混淆的地方,而且刚开始接触,所以这里只是简单的做一些学习的记录(甚至都称不上总结),后面用到的时候再回头看看吧。
开发自定义标签需要编写作为标签处理器的Java类,还要编写一个标签库描述符(Tag Library Descriptor)文件,简称TLD文件(TLD文件和标签处理器之间的关系就如同web.xml文件与Servlet之间的关系)。然后就可以通过下述代码在JSP文件中引入:
<%@ taglib uri = "" prefix = "" %>
其中uri的值和TLD文件中<uri>元素的值要保持一致。
自定义标签开发又分为传统标签开发和简单标签开发。首先,无论是传统标签还是简单标签,都需要实现Tag接口。它定义了4个int类型的静态常量和6个抽象方法。
setPageContext()
-->setParent()
-->getParent()
-->doStartTag()
-->doEndTag()
-->release()
其中doStartTag()用于判断是否执行标签体内容,而doEndTag用于判断是否继续执行标签后面的JSP内容。
传统标签开发中IterationTag接口是针对标签体的内容重复处理所设计的接口。在Tag接口上新增了一个doAfterBody()
方法。TagSupprot
类是该接口的一个实现类。
public class Iterate extends TagSupport{
...
private int num;
public int doAfterBody() throws JspException{
num --;
if (num > 0){
return EVAL_BODY_AGAIN;
}else{
return SKIP_BODY;
}
}
}
而继承自IterationTag接口的BodyTag
接口除了能进行重复显示,还能够对标签体的内容做一些处理,然后再向浏览器输出。
如果实现了BodyTag接口(BodyTagSupport
是该接口的一个实现类),在标签处理器调用doStartTag()方法时,JSP容器会调用setBodyContent()方法,通过该方法将BodyContent
对象传递给标签处理器类使用。
而BodyContent就是用于实现对标签内容处理的功能的。
传统标签一般都要实现doStartTag()和doEndTag()方法。当然,如果doStartTag()或者doEndTag()方法不需要进行重载,那么就可以不用写出来。例如在BodyTagSupport类中,doStartTag()方法默认会返回EVAL_BODY_BUFFERD常量,JSP容器就会在执行标签体之前创建BodyContent对象。所以如无必要,可以只实现doEndTag()方法。
SimpleTag
是所有简单标签的父接口,它定义了5个方法(SimpleTagSupport
是该接口的一个实现类)。
而简单标签则是将传统标签接口中定义的doStartTag()、doEndTag()和doAfterBody()等方法都集中到doTag()方法来进行实现。
上面的传统标签实现了是否多次显示标签体和对标签体进行处理的功能。传统标签中使用BodyTagSupport
来实现获取标签体的功能,现在则是通过直接调用getJspBody()
来实现。调用该方法就可以获取到一个JspFragment类
的实例对象。在标签体的多次显示功能上,通过invoke()
而不是返回变量的方式来决定显示标签体。
之前提到的BodyContent
对象具有数据缓冲区,并且可以通过getString()
等方法来获取缓冲区的内容。而这里的JspFragment
没有缓存,也没有getString()方法。如果需要对内容进行修改的话,需要在调用invoke()
方法的时候传入一个可以取出结果数据的输出流对象(例如StringWriter、CharArrayWriter)。
网友评论