forEach标签
forEach标签最基本的功能:遍历集合、数组
- 首先,我先写一个可以遍历List集合的标签,可能我们会这样设计:
public class Demo2 extends SimpleTagSupport { //遍历的是List集合,于是标签的属性就为List private List items; //遍历出来的对象就用Object存着,因为我们不知道List集合保存的是什么元素 private Object var; @Override public void doTag() throws JspException, IOException { //获取到迭代器 Iterator iterator = items.iterator(); //遍历集合 while (iterator.hasNext()) { //获取到集合的元素 var = iterator.next(); //.....var属性代表的就是集合的元素,现在问题来了,好像在标签体内无法获取到这个对象.... //做到这里完成不下去了.... } } public void setItems(List items) { this.items = items; } public void setVar(Object var) { this.var = var; } }
上面的思路是正常的,但是做不下去!我们换一个思路呗。上面的问题主要是在标签体获取不到被遍历出来的对象!
我们这样做:把var定义成String类型的,如果遍历得到对象了,就设置PageContext的属性,var为关键字,对象为值。在标签体用EL表达式搜索以var为关键字的对象!每遍历出一个对象,就执行一次标签体!
- 于是就有了以下的代码!
public class Demo1 extends SimpleTagSupport { //遍历的是List集合,定义List集合成员变量 private List items; //以var为关键字存储到PageContext private String var; @Override public void doTag() throws JspException, IOException { //获取到集合的迭代器 Iterator iterator = items.iterator(); //获取到代表标签体内容的对象 JspFragment jspFragment = this.getJspBody(); //遍历集合 while (iterator.hasNext()) { Object o = iterator.next(); //把遍历出来的对象存储到page范围中,关键字为标签的属性var(在标签体中使用EL表达式${var},就能够获取到集合的对象了!) this.getJspContext().setAttribute(var, o); //每设置了一个属性,我就执行标签体 jspFragment.invoke(null); } } public void setItems(List items) { this.items = items; } public void setVar(String var) { this.var = var; } }
- tld文件如下:
<tag> <name>forEach</name> <tag-class>tag.Demo1</tag-class> <body-content>scriptless</body-content> <attribute> <name>var</name> <rtexprvalue>true</rtexprvalue> <!--字符串即可,不需要EL表达式的支持--> <required>false</required> </attribute> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag>
- 测试的jsp代码如下:
<% List list = new ArrayList(); list.add("zhongfucneng"); list.add("1"); list.add("2"); list.add("3"); request.setAttribute("list",list); %> <zhongfucheng:forEach items="${list}" var="str"> ${str} </zhongfucheng:forEach>
- 效果:
上面写的仅仅能够遍历List集合,做一个通用的forEach标签麻烦的是在:不知道传进来的是什么类型的数组、什么类型集合!,需要逐一去判断
- 我们的实现思路就是将所有的集合或数组都转成是Collection类型的!
- 我们来尝试着写一下:
//如果items是Collection类型的,就强转为Colletion if (items instanceof Collection) { collection = (Collection) items; } //如果itmes是Map类型的,那么就强转为Map,再获取到<Map.Entry<K, V>,这个是Set集合的! if (items instanceof Map) { Map map = (Map) items; collection = (Collection) map.entrySet(); } //对象数组 if (items instanceof Object[]) { Object[] objects = (Object[]) items; collection = Arrays.asList(objects); } //int[],Byte[],char[]等八大基本数据类型.....
还有int[],byte[],char[]等八大基本数据类型,这八大基本数据类型就不能用Arrays.asList()把引用传进去了。因为JDK5以后会把引用自动装箱成Interger[]、Byte[]等等,而不是获取到数组的元素数据。
- 测试代码如下:
public static void main(String[] args) { int[] ints = new int[]{1, 2, 3}; Object[] objects = new Object[]{"1", "2", "3"}; if (objects instanceof Object[]) { Collection collection = Arrays.asList(objects); System.out.println(collection); } if (ints instanceof int[]) { Collection collection1 = Arrays.asList(ints); System.out.println(collection1); } }
- 效果:
- 对于八大基本类型数据我们就可以这样干:
if (items instanceof int[]) { int[] ints = (int[]) items; collection = new ArrayList(); for (int anInt : ints) { collection.add(anInt); } } //......这里还要写7个
- JSTL的forEach标签类似就是这样干的:
由于JDK5的新特性,我们又有另外的解决方案,Class对象能够判断是否为数组类,reflect反射包下Array类:
- 其实,无论Map集合、还是任何类型的数组、都可以使用Colletion进行遍历!。
- 如果是八大基本数据类型的数组,我们就使用反射来进行构建出Collection集合。
- 标签处理器的代码:
public class Demo1 extends SimpleTagSupport { //遍历的是未知的集合或数组,定义成Object private Object items; //每次被遍历的对象存储关键字 private String var; //Colletion private Collection collection; //在WEB容器设置标签的属性的时候,判断是什么类型的数组和集合 public void setItems(Object items) { this.items = items; //如果items是Collection类型的,就强转为Colletion if (items instanceof Collection) { collection = (Collection) items; } //如果itmes是Map类型的,那么就强转为Map,再获取到<Map.Entry<K, V>,这个是Set集合的! if (items instanceof Map) { Map map = (Map) items; collection = (Collection) map.entrySet(); } //可以这样解决,Class对象判断是否是一个数组类 if (items.getClass().isArray()) { //创建Collection集合添加数组的元素! collection = new ArrayList(); //再利用reflect包下的Array类获取到该数组类的长度 int len = Array.getLength(items); //遍历并添加到集合中 for (int i = 0; i < len; i++) { collection.add(Array.get(items, i)); } } } public void setVar(String var) { this.var = var; } @Override public void doTag() throws JspException, IOException { //获取到代表标签体内容的对象 JspFragment jspFragment = this.getJspBody(); Iterator iterator = collection.iterator(); //遍历集合 while (iterator.hasNext()) { Object o = iterator.next(); //把遍历出来的对象存储到page范围中(在标签体中使用EL表达式${var},就能够获取到集合的对象了!) this.getJspContext().setAttribute(var, o); jspFragment.invoke(null); } } }
- tld文件和上面是一样的,下面是测试代码:
<% /*list集合*/ List list = new ArrayList(); list.add("zhongfucneng"); list.add("1"); list.add("2"); list.add("3"); request.setAttribute("list",list); /*基本数据类型数组*/ int[] ints = new int[]{1, 2, 3, 4, 5}; request.setAttribute("ints", ints); /*对象数组*/ Object[] objects = new Object[]{2, 3, 4, 5, 6}; request.setAttribute("objects", objects); /*map集合*/ Map map = new HashMap(); map.put("aa", "aa"); map.put("bb", "bb"); map.put("cc", "cc"); request.setAttribute("map",map); %> List集合: <zhongfucheng:forEach items="${list}" var="str"> ${str} </zhongfucheng:forEach> <hr> <hr> 基本数据类型数组: <zhongfucheng:forEach items="${ints}" var="i"> ${i} </zhongfucheng:forEach> <hr> <hr> 对象数组: <zhongfucheng:forEach items="${objects}" var="o"> ${o} </zhongfucheng:forEach> <hr> <hr> map集合: <zhongfucheng:forEach items="${map}" var="me"> ${me.key} = ${me.value} </zhongfucheng:forEach>
- 效果:
HTML转义标签
要开发这个标签就很简单了,只要获取到标签体的内容,再通过经过方法转义下标签体内容,输出给浏览器即可!
- 标签处理器代码:
public class Demo1 extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { //获取到标签体的内容再修改 StringWriter stringWriter = new StringWriter(); JspFragment jspFragment = this.getJspBody(); jspFragment.invoke(stringWriter); String content = stringWriter.toString(); //经过filter()转义,该方法在Tomcat可以找到 content = filter(content); //再把转义后的内容输出给浏览器 this.getJspContext().getOut().write(content); } private String filter(String message) { if (message == null) return (null); char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuffer result = new StringBuffer(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]) { case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return (result.toString()); } }
- 测试代码:
<zhongfucheng:filter><a href="2.jsp">你好啊</a> </zhongfucheng:filter> <br> <a href="2.jsp">你好啊
- 效果:
if else标签
在JSTL中并没有if else的标签,JSTL给予我们的是choose,when,otherwise标签,现在我们模仿choose,when,otherwise开发标签
思路:when标签有个test属性,但otherwise怎么判断标签体是执行还是不执行呢?这时就需要choose标签的支持了!choose标签默认定义一个Boolean值为false,。当when标签体被执行了,就把Boolean值变成true,只要Boolean值为false就执行otherwise标签体的内容。
看程序就容易理解上面那句话了:!
- choose标签处理器
public class Choose extends SimpleTagSupport { private boolean flag; @Override public void doTag() throws JspException, IOException { this.getJspBody().invoke(null); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
- When标签处理器
public class When extends SimpleTagSupport { private boolean test ; @Override public void doTag() throws JspException, IOException { Choose choose = (Choose) this.getParent(); //如果test为true和flag为false,那么执行该标签体 if (test == true && choose.isFlag() == false) { this.getJspBody().invoke(null); //修改父标签的flag choose.setFlag(true); } } public void setTest(boolean test) { this.test = test; } }
- OtherWise标签处理器
public class OtherWise extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { Choose choose = (Choose) this.getParent(); //如果父标签的flag为false,就执行标签体(如果when标签没执行,flag值就不会被修改!when标签没执行,就应该执行otherwise标签!) if (choose.isFlag() == false) { getJspBody().invoke(null); //改父标签的flag为false choose.setFlag(true); } } }
- 测试代码:
<zhongfucheng:choose> <zhongfucheng:when test="${user!=null}"> user为空 </zhongfucheng:when> <zhongfucheng:otherwise> user不为空 </zhongfucheng:otherwise> </zhongfucheng:choose>
- 效果:
DynamicAttribute接口
此接口的主要功能是用于完成动态属性的设置!前面我们讲解属性标签的时候,属性都是写多少个,用多少个的。现在如果我希望属性可以动态的增加,只需要在标签处理器类中实现DynamicAttribute接口即可!
现在我要开发一个动态加法的标签:
- 标签处理器:
public class Demo1 extends SimpleTagSupport implements DynamicAttributes { //既然有动态属性和动态的值,那么我们就用一个Map集合存储(1-1对应的关系),做的加法运算,值为Double类型的。 Map<String, Double> map = new HashMap<>(); @Override public void doTag() throws JspException, IOException { //定义一个sum变量用于计算总值 double sum = 0.0; //获取到Map集合的数据 Iterator iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Double> entry = (Map.Entry<String, Double>) iterator.next(); sum += entry.getValue(); } //向浏览器输出总和是多少 this.getJspContext().getOut().write(String.valueOf(sum)); } //对于这个要实现的方法,我们只要关注第2个参数和第3个参数即可 //第二个参数表示的是动态属性的名称,第三个参数表示的是动态属性的值 @Override public void setDynamicAttribute(String s, String localName, Object value) throws JspException { //将动态属性的名字和值加到Map集合中 map.put(localName, Double.valueOf(Float.valueOf(value.toString()))); } }
- tld文件,注意要把dynamic-attribute设置为true
<tag> <name>dynamicAttribute</name> <tag-class> tag.Demo1</tag-class> <body-content> empty</body-content> <!--这个必须要设置为true--> <dynamic-attributes>true</dynamic-attributes> </tag>
- 测试代码:
<zhongfucheng:dynamicAttribute num="1.1" num2="2.2" num3="1"/>
- 效果,double在运算的时候会丢失精度的,现在只是测验下动态属性,这里就不详细说了!
开发自定义函数
至于怎么开发自定义函数,在EL表达式的博客中有!