1 什么EL函数库
EL函数库是由第三方对EL的扩展,我们现在学习的EL函数库是由JSTL添加的。下面我们会学习JSTL标签库。
EL函数库就是定义一些有返回值的静态方法。然后通过EL语言来调用它们!当然,不只是JSTL可以定义EL函数库,我们也可以自定义EL函数库。
EL函数库中包含了很多对字符串的操作方法,以及对集合对象的操作。例如:${fn:length(“abc”)}会输出3,即字符串的长度。
2 导入函数库
因为是第三方的东西,所以需要导入。导入需要使用taglib指令!
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
3 EL函数库介绍
l String toUpperCase(String input):
l String toLowerCase(String input):
l int indexOf(String input, String substring):
l boolean contains(String input, String substring):
l boolean containsIgnoreCase(String input, String substring):
l boolean startsWith(String input, String substring):
l boolean endsWith(String input, String substring):
l String substring(String input, int beginIndex, int endIndex):
l String substringAfter(String input, String substring):
l substringBefore(String input, String substring):
l String escapeXml(String input):
l String trim(String input):
l String replace(String input, String substringBefore, String substringAfter):
l String[] split(String input, String delimiters):
l int length(Object obj):
l String join(String array[], String separator):
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> … String[] strs = {"a", "b","c"}; List list = new ArrayList(); list.add("a"); pageContext.setAttribute("arr", strs); pageContext.setAttribute("list", list); %> ${fn:length(arr) }<br/><!--3--> ${fn:length(list) }<br/><!--1--> ${fn:toLowerCase("Hello") }<br/> <!-- hello --> ${fn:toUpperCase("Hello") }<br/> <!-- HELLO --> ${fn:contains("abc", "a")}<br/><!-- true --> ${fn:containsIgnoreCase("abc", "Ab")}<br/><!-- true --> ${fn:contains(arr, "a")}<br/><!-- true --> ${fn:containsIgnoreCase(list, "A")}<br/><!-- true --> ${fn:endsWith("Hello.java", ".java")}<br/><!-- true --> ${fn:startsWith("Hello.java", "Hell")}<br/><!-- true --> ${fn:indexOf("Hello-World", "-")}<br/><!-- 5 --> ${fn:join(arr, ";")}<br/><!-- a;b;c --> ${fn:replace("Hello-World", "-", "+")}<br/><!-- Hello+World --> ${fn:join(fn:split("a;b;c;", ";"), "-")}<br/><!-- a-b-c --> ${fn:substring("0123456789", 6, 9)}<br/><!-- 678 --> ${fn:substring("0123456789", 5, -1)}<br/><!-- 56789 --> ${fn:substringAfter("Hello-World", "-")}<br/><!-- World --> ${fn:substringBefore("Hello-World", "-")}<br/><!-- Hello --> ${fn:trim(" a b c ")}<br/><!-- a b c --> ${fn:escapeXml("<html></html>")}<br/> <!-- <html></html> --> |
4 自定义EL函数库
l 写一个类,写一个有返回值的静态方法;
l 编写tld文件,可以参数fn.tld文件来写,把tld文件放到classes下(还可以自定义命名空间);
l 在页面中添加taglib指令,导入自定义标签库。
ItcastFuncations.java
package cn.itcast.el.funcations; public class ItcastFuncations { public static String test() { return "传智播客自定义EL函数库测试"; } } |
itcast.tld(放到classes下)
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <description>ITCAST 1.0 functions library</description> <display-name>ITCAST functions</display-name> <tlib-version>1.0</tlib-version> <short-name>itcast</short-name> <uri>http://www.itcast.cn/jsp/functions</uri> <function> <name>test</name> <function-class>cn.itcast.el.funcations.ItcastFuncations</function-class> <function-signature>String test()</function-signature> </function> </taglib> |
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="itcast" uri="/WEB-INF/classes/itcast.tld" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <body> <h1>${itcast:test() }</h1> </body> </html> |
如果把itcast.tld文件放到classes\META-INF\itcast.tld,那么就可以把
<%@ taglib prefix="itcast" uri="/WEB-INF/classes/itcast.tld" %>
修改为
<%@ taglib prefix="itcast" uri="http://www.itcast.cn/jsp/functions" %>
对应itcast.tld文件中的<uri>元素内容。
一般这种方式都是打包成Jar时,在Jar中的META-INF目录中存放TLD文件。
自定义标签
Tag
1 什么是自定义标签
尽管JSP本身,以及第三方提供了很多标签库,但因为业务需要,我们有时还是需要自定义标签。因为JSP页面中不可以存在Java代码,所以我们需要自定义标签!
2 标签的真身
其实我们现在应该可以了解了,真的是万物皆对象。JSP可以是一个对象,当然标签也可以是一个对象。其实在页面中使用的标签就是对一个对象的方法调用!
标签:
l 标签处理类:都有自己的标签处理类!所有标签处理类都必须去实现Tag或SimpleTag接口;
l TLD(Tag Library Description):一个TLD文件中可以部署多个标签!JSP会通过TLD文件找到标签! (webapps\examples\WEB-INF\jsp2,这里有模板)
3 Hello Tag!
写一个类:MyTag,实现Tag接口。Tag接口中一共6个方法!我们只需要去实现两个方法即可,其他的方法当它不存在!
l setPageContext(PageContext):为本类添加PageContext属性,在本方法中把参数赋给本类属性。没错,就是用来保存PageContext中,因为doStartTag()方法中要用PageContext;
l int doStartTag():使用pageContext获取out对象,然后使用out向页面打印:“Hello Tag!”。本方法还有一个返回值,返回0就行了(先不用管返回值是干什么的)!
l TLD:TLD文件中的一个<tag>对应一个标签的部署信息!现在中需要照猫画虎就OK了!没错,TLD是一个XML文件!(可以到webapps\examples\WEB-INF\jsp2中查找TLD文件)
MyTag.java
package cn.itcast.tags; import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.Tag; public class MyTag implements Tag { private PageContext pageContext ; public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; } public int doStartTag() throws JspException { try { this.pageContext.getOut().write("Hello Tag!"); } catch (IOException e) { throw new JspException(e); } return 0 ; } public void setParent(Tag t) {} public Tag getParent() {return null;} public int doEndTag() throws JspException {return 0;} public void release() { } } |
mytld.tld
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd" version="2.0"> <description>xixi</description> <tlib-version>1.0</tlib-version> <jsp-version>2.0</jsp-version> <short-name>hello tag</short-name> <uri></uri> <small-icon></small-icon> <tag> <name>helloTag</name> <tag-class>cn.itcast.tags.MyTag</tag-class> <body-content>empty </body-content> </tag> </taglib> |
把mytld.tld文件放到/WEB-INF/classes/mytld.tld路径。
index.jsp
<%@taglib prefix="it" uri="/WEB-INF/classes/mytld.tld" %> …… <body> <h1><it:helloTag/> </h1> </body> |
4 标签的生命周期
在开始聊标签的生命周期之前,我们先来看看TLD的加载过程。
首次加载TLD文件
当index.jsp第一次被执行时,Tomcat会把index.jsp编译成index_jsp.java。在这个过程中,Tomcat会根据“<%@ taglib uir=”/WEB-INF/classes/mytld.tld”…%>”去加载mytld.tld文件。这个加载TLD文件的过程只执行一次,以后就不会再执行了。
实例化标签处理器对象
通过TLD找到标签的处理器类,然后实例化处理器对象,并把处理器对象放到“标签池”中。也就是说,每个标签的处理器类只会有一个实例!因为下一次访问这个标签就直接从池中获取处理器对象,而不需要再去创建了。
标签生命周期方法
l 调用setPageContext()方法,把当前页面的PageContext传递给标签处理器类;
l 调用setParent()方法,给标签器传递父标签。如果没有父标签就传递null;
l 在执行到开始标签时,调用doStartTag()方法,doStartTag()方法返回值:
- 0(Tag.SKIP_BODY):表示忽略标签体内容;
- 1(Tag.EVAL_BODY_INCLUDE):表示显示标签体内容;
l 在执行到标签标签时,调用doEndTag()方法,doEndTag()方法返回值:
- 5(Tag.SKIP_PAGE):表示忽略标签后面的页面东西;
- 6(Tag.EVAL_PAGE)表示显示标签后面的页面东西。
l release()方法会在重启Tomcat时被执行,即Tomcat从标签器池中移除标签时会调用这个方法。
必须实现Tag接口
添加PageContext属性
保存参数pageContext,在doStartTag()方法中方便使用。
使用pageContext获取输出流,然后使用流对象向浏览器打印Hello Tag!字符串。注意:如果没有在setPageContext()方法中保存参数,那么在doStartTag()方法中就会抛出空指针异常。
返回0表示不执行标签体。现在不用理会它!
这一部分是对当前标签库的声明!都是一些无需关心的东西。
l 当前标签库的描述;
l 当前标签库的版本;
l 支持JSP的版本;
l 标签库的名称(用于在工具上显示标签);
l uri:这个东西是有用的,后面再讲;
l 小图标也是为了在工具上显示用的;
标签的名称
标签处理类
标签体是空类型
it与taglib指令中的prefix对应;helloTag与tld文件中的<name>对应!
SimpleTag
1 SimpleTag是什么
标签处理类必须实现JspTag接口,而JspTag接口有两个子接口:Tag和SimpleTag。更加确切的说:标签处理类必须实现Tag或SimpleTag接口。
JSP2.0之后,SUN提供了SimpleTag接口,通过SimpleTag接口来实现标签处理类要比Tag这种传统方式方便很多,所以现在我们可以大声与Tag说再见了。
SimpleTag接口内容如下:
l void doTag():标签执行方法;
l JspTag getParent():获取父标签;
l void setParent(JspTag parent):设置父标签
l void setJspContext(JspContext context):设置PageContext
l void setJspBody(JspFragment jspBody):设置标签体对象;
2 继承SimpleTagSupport
public class HelloTag extends SimpleTagSupport { public void doTag() throws JspException, IOException { this.getJspContext().getOut().write("<p>Hello SimpleTag!</p>")[向页面输出!注意,不能向页面输出<%%>东西!] ; } } |
3 <body-content>
<body-content>元素的可选值有:
l empty:不能有标签体内容。
l JSP:标签体内容可以是任何东西:EL、JSTL、<%=%>、<%%>,以及html;但不建议使用Java代码段,SimpleTag已经不再支持使用<body-content>JSP</body-content>;
l scriptless:标签体内容不能是Java代码段,但可以是EL、JSTL等;
l tagdependent:标签体内容不做运算,由标签处理类自行处理,无论标签体内容是EL、JSP、JSTL,都不会做运算。
4 不执行标签下面的页面内容
我们知道,在使用Tag接口来编写标签时,可以跳过标签下面的JSP页面内容。在SimpleTag中也是可以的,这需要在doTag()方法中抛出SkipPageException。
SkipPageException是JspException的子类,当doTag()方法抛出该异常时,JSP真身不会打印异常信息,而是跳转页面内容!
5 带有属性的标签
l 在处理类中添加属性,以及getter/setter方法;
l 在TLD中部属相关属性。
forEach
功能:模仿JSTL中的<c:forEach>标签。
forEach标签属性:
l items:Object类型,可以是Collection、Map,或者数组(用到了数组反射);
l begin:Integer类型,表示从哪个下标位置开始遍历;
l end:Integer类型,表示遍历到哪个下标位置结束;
l var:String类型,使用var指定的值来保存当前项到PageContext中;
l step:Integer类型,表示步长;
l varStatus:String类型,使用varStatus的值来保存当前循环的循环状态。
jsp页面 设置一个集合
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.itzl.com/myjstl/mytagTest" prefix="mytag"%>
<
<html>
<head></head>
<body>
<%
/* List list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
request.setAttribute("list", list); */
/* Map map=new HashMap();
map.put("a1", "qwe");
map.put("a2", "asd");
map.put("a3", "zxc");
request.setAttribute("map", map);
*/
int arr[]={1,2,3};
request.setAttribute("arr", arr);
%>
<mytag:ForEach items="${arr}" var="v">
${v}
</mytag:ForEach> </body>
</html>
实现
package cn.zl.web.el; import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map; import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport; import cn.zl.web.jsp.VarStatuts; public class ForEachSimpleTag extends SimpleTagSupport { private Integer begin;
private Integer end;
private Integer step;
private Object items;
private String var;
private String varStatus;
private Collection collection = new ArrayList();
public void setBegin(Integer begin) {
this.begin = begin;
}
public void setEnd(Integer end) {
this.end = end;
}
public void setStep(Integer step) {
this.step = step;
}
public void setItems(Object items) {
this.items = items;
}
public void setVar(String var) {
this.var = var;
}
public void setVarStatus(String varStatus) {
this.varStatus = varStatus;
}
private Object[] toArray (Object items) throws JspException {
if(items == null ) {
throw new NullPointerException("items不存在!");
}
if(items instanceof Collection) {
return ((Collection)items).toArray() ;
} else if(items instanceof Map) {
return ((Map)items).entrySet().toArray() ;
} else if(items.getClass().isArray()) {
//return (Object[])items ;
int length = Array.getLength(items);//得到数组长度
for (int i = 0; i < length; i++) {
//取数组中的每个元素
Object obj = Array.get(items, i);// 取第i个元素
collection.add(obj);//将数组中的元素加入到集合中 }
return collection.toArray();
}
throw new JspException("items不是数组,不是集合!") ;
}
public void doTag() throws JspException, IOException {
Object[] objs = this.toArray(items) ;
if(begin == null) {
begin = 0;
}
if(end == null) {
end = objs.length - 1;
}
if(step == null) {
step = 1;
}
PageContext pageContext = (PageContext) this.getJspContext();
JspFragment body = this.getJspBody();
for(int i = begin; i <= end; i+=step ) {
Object item = objs[i];
if(varStatus != null ) {
VarStatuts vs = new VarStatuts();
vs.setFirst(i == begin);
vs.setLast(i == end);
vs.setIndex(i);
vs.setCount(i+1);
vs.setCurrent(objs[i]) ;
pageContext.setAttribute(varStatus, vs) ;
}
if(var != null) {
pageContext.setAttribute(var, item);
}
body.invoke(null);
}
if(var != null) {
pageContext.removeAttribute(var);
}
if(varStatus != null) {
pageContext.removeAttribute(varStatus);
}
}
}
tld文件
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>A tag library exercising SimpleTag handlers.</description>
<tlib-version>1.0</tlib-version>
<short-name>mytag</short-name>
<!--命名空间 (别名)-->
<uri>http://www.itzl.com/myjstl/mytagTest</uri>
<!-- el 自定义函数 --> <tag><!-- 配置文件 -->
<name>ForEach</name>
<tag-class>cn.zl.web.el.ForEachSimpleTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>items</name><!-- 属性名称 -->
<required>true</required><!-- 改属性是否必须,true代表必须 -->
<rtexprvalue>true</rtexprvalue><!-- 是否支持表达式 -->
</attribute>
<attribute>
<name>var</name><!-- 属性名称 -->
<required>false</required><!-- 改属性是否必须,true代表必须 -->
<rtexprvalue>false</rtexprvalue><!-- 是否支持表达式 -->
</attribute>
</tag> </taglib>