EL的函数与标签

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>对应!

EL的函数与标签

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>
上一篇:凭借这份《数据结构与算法》核心文档,含BATJM大厂


下一篇:Sample Means(耶鲁大学教材)