maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)(二)
2014-09-14 00:12
381 查看
接着上一篇 /article/11572651.html 写了jstl的out标签的实现过程 本文接着看set 、 if 、catch、forEach标签的实现过程
1、set标签 -- 向page/request/session/application中添加值 或者是给javabean的属性赋值 或者是向map中添加key,value键值对
jsp页面
tld描述
相应的描述 在前一篇 已经解释过了
接着看 标签处理类 SetTag.java
会看到只有 value target property 三个属性的赋值方法 重点都在 SetSupport.java里面
会看到 拥有 var scope两个属性定义
在SetSupport.java里面只是重写了 doEndTag 方法
会发现 只要有var属性 即使有target和property属性 target和property属性也不起作用 上面也已证实
如果是指定了var属性 则 将变量的值导出
上面就是 如果指定的value是ValueExpression 则写入到ELContext上下文的变量映射对中
不然的话 则判断scope是否为page 如果是的话 并且ELContext中已有此变量 则将此变量置空
或者将此变量写入到相应的scope中去
或者value为空的话 清楚相应范围内的值
如果var没有指定 则判断 是否是给map赋值 或者是给javabean属性赋值
导出到map中 很简单
给javabean的属性赋值 也不复杂 用了 內省与反射技术 如下
通过Introspector获取到javabean的属性信息 然后遍历 找到匹配的属性名后 利用反射技术 调用 匹配的属性的set方法 进行 赋值
上面 即是set 赋值过程
2、看第二个 if 标签 这个比较简单
jsp页面
tld描述
标签处理类 IfTag.java
看父类 ConditionalTagSupport.java
还是从开始 doStartTag 开始
获取test的值 给 result 然后将结果 导出
然后再根据 test 即 result的值判断 是否执行标签的内容 为true的话 进一步解析标签内容 不然 不解析标签内容
此即是if标签
3、catch标签 也比较简单
jsp页面
tld描述
标签处理类 CatchTag.java
会发现 多了doCatch doFinally两个方法 来自于 TryCatchFinally接口
比较详细的解释的话 就是这个了
在执行catch里面的标签是 会将其放入try catch finally 代码块 当有异常发生时 调用 doCatch方法 向page页面添加名为var的错误信息’
最后标签执行完 不管有没有异常 都会调用finally代码块 调用doFinally来根据条件清除属性
此即使catch标签 的过程 比较简单
4、看最后一个 forEach标签 有点复杂 其实
jsp页面
tld描述
看标签解析类 ForEachTag.java
会发现 只有 begin end step items 的赋值方法 看父类 ForEachSupport.java
会发现此类 看起来不太完整 连不起来 继续看父类 LoopTagSupport.java
哈哈 找到头了 doStartTag 这就好办了 哦 对了 在执行doStartTag之前 执行构造函数
子类不要构建有参数的构造器 不然此父类构造器不会被调用 那么init()就没的执行了 哈哈 一般 我们也不会用 其实
看到了吧 我们在使用 c:forEach标签的时候 不指定 begin end step 它都会帮我们设定初始化的值
但是如果 你没有指定begin end step的值的话 在jsp页面里面使用${ps.end}就获取不到值啊 其实获取到空值 为什么呢 原因在这
看到了吧 根据endSpecified进行返回值 而在 上面的init中 设为了 false 只有当 你设置了end值的时候 才会调用 setEnd方法 进行修改此值
调用完构造器后 就调用doStartTag了
进来先会判断设定的begin end的大小 如果没有设定的话 end=-1 begin=0
然后会设定 index count last几个状态值的初始值
接着会调用子类即ForEachSupport的prepare方法
此方法和其他方法一起构建 迭代的对象items 因为参数类型不一致 所以需要多种类型辅助 看toIterator即可
接着看doStartTag 中的就是 discardIgnoreSubset(begin);
此方法的作用 即是将迭代器的位置 遍历到begin 所指向的位置
接着是 获取到begin位置的值 给item
接着是 discard(step - 1);
此方法的作用 就是将迭代器的指针指向下一个即将迭代对象的前一个
为了防止出现step不等于1的情况 这样处理后 迭代器拥有都是指向以begin为基础step为变化值的子集项
接下来 导出var和varStatus的值
向page范围写入var和varStatus的值
接着 校正last的状态值
此时doStartTag方法完了后 因为返回值为 EVAL_BODY_INCLUDE
通过 hasNext和atEnd判断此迭代是否该继续
执行完后 返回
此值是在IterationTag里面定义的 用于重复解析forEach标签中的内容
此即使forEach标签的执行流程
目前 只是看了这几个 等到有时间了 再看吧 记录学习的脚步
1、set标签 -- 向page/request/session/application中添加值 或者是给javabean的属性赋值 或者是向map中添加key,value键值对
jsp页面
<c:out value="=========================测试jstl的catch和set标签========================="></c:out> <br/> <c:catch var="tryInfo2"> <c:set var="name" value="under" scope="request"></c:set> <c:set value="unde" target="person" property="name"></c:set> </c:catch> <c:out value="将值放入request中 然后取出 显示 ${requestScope.name }"></c:out><br/> <c:out value="c:set中如果有var=un 则target、property不起作用 将值放入page中 然后取出 显示 ${pageScope.un }"></c:out><br/> <c:out value="因为没有person对象 也没有setName方法 所以会报错 ${tryInfo2 }" escapeXml="false"></c:out> <br/> <c:set value="undeMap" target="<%=map %>" property="name"></c:set><br/> <c:out value="向map添加值 然后取出 :"></c:out> <c:out value='<%=map.get("name") %>'></c:out><br/> <c:out value="=========================测试jstl的catch和set标签========================="></c:out>
tld描述
<tag> <description> Sets the result of an expression evaluation in a 'scope' </description> <name>set</name> <tag-class>org.apache.taglibs.standard.tag.rt.core.SetTag</tag-class> <body-content>JSP</body-content> <attribute> <description> Name of the exported scoped variable to hold the value specified in the action. The type of the scoped variable is whatever type the value expression evaluates to. </description> <name>var</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <attribute> <description> Expression to be evaluated. </description> <name>value</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <deferred-value> <type>java.lang.Object</type> </deferred-value> </attribute> <attribute> <description> Target object whose property will be set. Must evaluate to a JavaBeans object with setter property property, or to a java.util.Map object. </description> <name>target</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description> Name of the property to be set in the target object. </description> <name>property</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <description> Scope for var. </description> <name>scope</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
相应的描述 在前一篇 已经解释过了
接着看 标签处理类 SetTag.java
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.taglibs.standard.tag.rt.core; import org.apache.taglibs.standard.tag.common.core.SetSupport; /** * JSTL 1.1 compatible version of <set> that accepts expression results for attribute values. * * @author Shawn Bayern */ public class SetTag extends SetSupport { private boolean valueSpecified; private Object value; private Object target; private String property; public SetTag() { } public void setValue(Object value) { this.value = value; this.valueSpecified = true; } public void setTarget(Object target) { this.target = target; } public void setProperty(String property) { this.property = property; } @Override public void release() { value = null; target = null; property = null; valueSpecified = false; super.release(); } @Override protected boolean isValueSpecified() { return valueSpecified; } @Override protected Object evalValue() { return value; } @Override protected Object evalTarget() { return target; } @Override protected String evalProperty() { return property; } }
会看到只有 value target property 三个属性的赋值方法 重点都在 SetSupport.java里面
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.taglibs.standard.tag.common.core; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import javax.el.ELContext; import javax.el.ELException; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.el.VariableMapper; import javax.servlet.jsp.JspApplicationContext; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspFactory; import javax.servlet.jsp.JspTagException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.BodyTagSupport; import org.apache.taglibs.standard.resources.Resources; /** * <p>Support for handlers of the <set> tag.</p> * * @author Shawn Bayern */ public abstract class SetSupport extends BodyTagSupport { //********************************************************************* // Internal state private String var; // tag attribute private String scope; // tag attribute //********************************************************************* // Construction and initialization /** * Constructs a new handler. As with TagSupport, subclasses should * not provide other constructors and are expected to call the * superclass constructor. */ protected SetSupport() { super(); } // Releases any resources we may have (or inherit) @Override public void release() { var = null; scope = null; super.release(); } //********************************************************************* // Tag logic @Override public int doEndTag() throws JspException { // decide what to do with the result if (var != null) { exportToVariable(getResult()); } else { Object target = evalTarget(); if (target == null) { // can happen if target evaluates to null throw new JspTagException(Resources.getMessage("SET_INVALID_TARGET")); } String property = evalProperty(); if (target instanceof Map) { exportToMapProperty(target, property, getResult()); } else { exportToBeanProperty(target, property, getResult()); } } return EVAL_PAGE; } Object getResult() throws JspException { if (isValueSpecified()) { return evalValue(); } else if (bodyContent == null) { return ""; } else { String content = bodyContent.getString(); if (content == null) { return ""; } else { return content.trim(); } } } /** * Indicates that the value attribute was specified. * If no value attribute is supplied then the value is taken from the tag's body content. * * @return true if the value attribute was specified */ protected abstract boolean isValueSpecified(); /** * Evaluate the value attribute. * * @return the result of evaluating the value attribute * @throws JspException if there was a problem evaluating the expression */ protected abstract Object evalValue() throws JspException; /** * Evaluate the target attribute. * * @return the result of evaluating the target attribute * @throws JspException if there was a problem evaluating the expression */ protected abstract Object evalTarget() throws JspException; /** * Evaluate the property attribute. * * @return the result of evaluating the property attribute * @throws JspException if there was a problem evaluating the expression */ protected abstract String evalProperty() throws JspException; /** * Export the result into a scoped variable. * * @param result the value to export * @throws JspTagException if there was a problem exporting the result */ void exportToVariable(Object result) throws JspTagException { /* * Store the result, letting an IllegalArgumentException * propagate back if the scope is invalid (e.g., if an attempt * is made to store something in the session without any * HttpSession existing). */ int scopeValue = Util.getScope(scope); ELContext myELContext = pageContext.getELContext(); VariableMapper vm = myELContext.getVariableMapper(); if (result != null) { // if the result is a ValueExpression we just export to the mapper if (result instanceof ValueExpression) { if (scopeValue != PageContext.PAGE_SCOPE) { throw new JspTagException(Resources.getMessage("SET_BAD_DEFERRED_SCOPE", scope)); } vm.setVariable(var, (ValueExpression) result); } else { // make sure to remove it from the VariableMapper if we will be setting into page scope if (scopeValue == PageContext.PAGE_SCOPE && vm.resolveVariable(var) != null) { vm.setVariable(var, null); } pageContext.setAttribute(var, result, scopeValue); } } else { //make sure to remove it from the Var mapper if (vm.resolveVariable(var) != null) { vm.setVariable(var, null); } if (scope != null) { pageContext.removeAttribute(var, Util.getScope(scope)); } else { pageContext.removeAttribute(var); } } } /** * Export the result into a Map. * * @param target the Map to export into * @param property the key to export into * @param result the value to export */ void exportToMapProperty(Object target, String property, Object result) { @SuppressWarnings("unchecked") Map<Object, Object> map = (Map<Object, Object>) target; if (result == null) { map.remove(property); } else { map.put(property, result); } } /** * Export the result into a bean property. * * @param target the bean to export into * @param property the bean property to set * @param result the value to export * @throws JspTagException if there was a problem exporting the result */ void exportToBeanProperty(Object target, String property, Object result) throws JspTagException { PropertyDescriptor[] descriptors; try { descriptors = Introspector.getBeanInfo(target.getClass()).getPropertyDescriptors(); } catch (IntrospectionException ex) { throw new JspTagException(ex); } for (PropertyDescriptor pd : descriptors) { if (pd.getName().equals(property)) { Method m = pd.getWriteMethod(); if (m == null) { throw new JspTagException(Resources.getMessage("SET_NO_SETTER_METHOD", property)); } try { m.invoke(target, convertToExpectedType(result, m)); } catch (ELException ex) { throw new JspTagException(ex); } catch (IllegalAccessException ex) { throw new JspTagException(ex); } catch (InvocationTargetException ex) { throw new JspTagException(ex); } return; } } throw new JspTagException(Resources.getMessage("SET_INVALID_PROPERTY", property)); } /** * Convert an object to an expected type of the method parameter according to the conversion * rules of the Expression Language. * * @param value the value to convert * @param m the setter method * @return value converted to an instance of the expected type; will be null if value was null * @throws javax.el.ELException if there was a problem coercing the value */ private Object convertToExpectedType(final Object value, Method m) throws ELException { if (value == null) { return null; } Class<?> expectedType = m.getParameterTypes()[0]; return getExpressionFactory().coerceToType(value, expectedType); } protected ExpressionFactory getExpressionFactory() { JspApplicationContext appContext = JspFactory.getDefaultFactory().getJspApplicationContext(pageContext.getServletContext()); return appContext.getExpressionFactory(); } //********************************************************************* // Accessor methods /** * Name of the exported scoped variable to hold the value specified in the action. * The type of the scoped variable is whatever type the value expression evaluates to. * * @param var name of the exported scoped variable */ public void setVar(String var) { this.var = var; } /** * Scope for var. * Values are verified by TLV. * * @param scope the variable scope */ public void setScope(String scope) { this.scope = scope; } }
会看到 拥有 var scope两个属性定义
在SetSupport.java里面只是重写了 doEndTag 方法
@Override public int doEndTag() throws JspException { // decide what to do with the result if (var != null) { exportToVariable(getResult()); } else { Object target = evalTarget(); if (target == null) { // can happen if target evaluates to null throw new JspTagException(Resources.getMessage("SET_INVALID_TARGET")); } String property = evalProperty(); if (target instanceof Map) { exportToMapProperty(target, property, getResult()); } else { exportToBeanProperty(target, property, getResult()); } } return EVAL_PAGE; }
会发现 只要有var属性 即使有target和property属性 target和property属性也不起作用 上面也已证实
如果是指定了var属性 则 将变量的值导出
/** * Export the result into a scoped variable. * * @param result the value to export * @throws JspTagException if there was a problem exporting the result */ void exportToVariable(Object result) throws JspTagException { /* * Store the result, letting an IllegalArgumentException * propagate back if the scope is invalid (e.g., if an attempt * is made to store something in the session without any * HttpSession existing). */ int scopeValue = Util.getScope(scope); ELContext myELContext = pageContext.getELContext(); VariableMapper vm = myELContext.getVariableMapper(); if (result != null) { // if the result is a ValueExpression we just export to the mapper if (result instanceof ValueExpression) { if (scopeValue != PageContext.PAGE_SCOPE) { throw new JspTagException(Resources.getMessage("SET_BAD_DEFERRED_SCOPE", scope)); } vm.setVariable(var, (ValueExpression) result); } else { // make sure to remove it from the VariableMapper if we will be setting into page scope if (scopeValue == PageContext.PAGE_SCOPE && vm.resolveVariable(var) != null) { vm.setVariable(var, null); } pageContext.setAttribute(var, result, scopeValue); } } else { //make sure to remove it from the Var mapper if (vm.resolveVariable(var) != null) { vm.setVariable(var, null); } if (scope != null) { pageContext.removeAttribute(var, Util.getScope(scope)); } else { pageContext.removeAttribute(var); } } }
上面就是 如果指定的value是ValueExpression 则写入到ELContext上下文的变量映射对中
不然的话 则判断scope是否为page 如果是的话 并且ELContext中已有此变量 则将此变量置空
或者将此变量写入到相应的scope中去
或者value为空的话 清楚相应范围内的值
如果var没有指定 则判断 是否是给map赋值 或者是给javabean属性赋值
Object target = evalTarget(); if (target == null) { // can happen if target evaluates to null throw new JspTagException(Resources.getMessage("SET_INVALID_TARGET")); } String property = evalProperty(); if (target instanceof Map) { exportToMapProperty(target, property, getResult()); } else { exportToBeanProperty(target, property, getResult()); }
导出到map中 很简单
/** * Export the result into a Map. * * @param target the Map to export into * @param property the key to export into * @param result the value to export */ void exportToMapProperty(Object target, String property, Object result) { @SuppressWarnings("unchecked") Map<Object, Object> map = (Map<Object, Object>) target; if (result == null) { map.remove(property); } else { map.put(property, result); } }
给javabean的属性赋值 也不复杂 用了 內省与反射技术 如下
/** * Export the result into a bean property. * * @param target the bean to export into * @param property the bean property to set * @param result the value to export * @throws JspTagException if there was a problem exporting the result */ void exportToBeanProperty(Object target, String property, Object result) throws JspTagException { PropertyDescriptor[] descriptors; try { descriptors = Introspector.getBeanInfo(target.getClass()).getPropertyDescriptors(); } catch (IntrospectionException ex) { throw new JspTagException(ex); } for (PropertyDescriptor pd : descriptors) { if (pd.getName().equals(property)) { Method m = pd.getWriteMethod(); if (m == null) { throw new JspTagException(Resources.getMessage("SET_NO_SETTER_METHOD", property)); } try { m.invoke(target, convertToExpectedType(result, m)); } catch (ELException ex) { throw new JspTagException(ex); } catch (IllegalAccessException ex) { throw new JspTagException(ex); } catch (InvocationTargetException ex) { throw new JspTagException(ex); } return; } } throw new JspTagException(Resources.getMessage("SET_INVALID_PROPERTY", property));
通过Introspector获取到javabean的属性信息 然后遍历 找到匹配的属性名后 利用反射技术 调用 匹配的属性的set方法 进行 赋值
上面 即是set 赋值过程
2、看第二个 if 标签 这个比较简单
jsp页面
<c:if test="${5==5 }" var="numJud"></c:if> <c:out value="和上面带有scope的属性一致 结果为:${pageScope.numJud }"></c:out> <br/>
tld描述
<tag> <description> Simple conditional tag, which evalutes its body if the supplied condition is true and optionally exposes a Boolean scripting variable representing the evaluation of this condition </description> <name>if</name> <tag-class>org.apache.taglibs.standard.tag.rt.core.IfTag</tag-class> <body-content>JSP</body-content> <attribute> <description> The test condition that determines whether or not the body content should be processed. </description> <name>test</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>boolean</type> </attribute> <attribute> <description> Name of the exported scoped variable for the resulting value of the test condition. The type of the scoped variable is Boolean. </description> <name>var</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <attribute> <description> Scope for var. </description> <name>scope</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
标签处理类 IfTag.java
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.taglibs.standard.tag.rt.core; import javax.servlet.jsp.jstl.core.ConditionalTagSupport; /** * <p>Tag handler for <if> in JSTL's rtexprvalue library. Because * of the support provided by the ConditionalTagSupport class, this * tag is trivial enough not to require a separate base supporting class * common to both libraries.</p> * * @author Shawn Bayern */ public class IfTag extends ConditionalTagSupport { //********************************************************************* // Constructor and lifecycle management // initialize inherited and local state public IfTag() { super(); init(); } // Releases any resources we may have (or inherit) @Override public void release() { super.release(); init(); } //********************************************************************* // Supplied conditional logic @Override protected boolean condition() { return test; } //********************************************************************* // Private state private boolean test; // the value of the 'test' attribute //********************************************************************* // Accessors // receives the tag's 'test' attribute public void setTest(boolean test) { this.test = test; } //********************************************************************* // Private utility methods // resets internal state private void init() { test = false; } }
看父类 ConditionalTagSupport.java
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package javax.servlet.jsp.jstl.core; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspTagException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.TagSupport; /** * <p>Abstract class that facilitates implementation of conditional actions * where the boolean result is exposed as a JSP scoped variable. The * boolean result may then be used as the test condition in a <c:when> * action.</p> * <p>This base class provides support for:</p> * <ul> * <li> Conditional processing of the action's body based on the returned value * of the abstract method <tt>condition()</tt>.</li> * <li> Storing the result of <tt>condition()</tt> as a <tt>Boolean</tt> object * into a JSP scoped variable identified by attributes <tt>var</tt> and * <tt>scope</tt>. * </ul> * * @author Shawn Bayern */ public abstract class ConditionalTagSupport extends TagSupport { //********************************************************************* // Abstract methods /** * <p>Subclasses implement this method to compute the boolean result * of the conditional action. This method is invoked once per tag invocation * by <tt>doStartTag()</tt>. * * @return a boolean representing the condition that a particular subclass * uses to drive its conditional logic. */ protected abstract boolean condition() throws JspTagException; //********************************************************************* // Constructor /** * Base constructor to initialize local state. As with <tt>TagSupport</tt>, * subclasses should not implement constructors with arguments, and * no-argument constructors implemented by subclasses must call the * superclass constructor. */ public ConditionalTagSupport() { super(); init(); } //********************************************************************* // Lifecycle management and implementation of conditional behavior /** * Includes its body if <tt>condition()</tt> evaluates to true. */ @Override public int doStartTag() throws JspException { // execute our condition() method once per invocation result = condition(); // expose variables if appropriate exposeVariables(); // handle conditional behavior if (result) { return EVAL_BODY_INCLUDE; } else { return SKIP_BODY; } } /** * Releases any resources this ConditionalTagSupport may have (or inherit). */ @Override public void release() { super.release(); init(); } //********************************************************************* // Private state private boolean result; // the saved result of condition() private String var; // scoped attribute name private int scope; // scoped attribute scope //********************************************************************* // Accessors /** * Sets the 'var' attribute. * * @param var Name of the exported scoped variable storing the result of * <tt>condition()</tt>. */ public void setVar(String var) { this.var = var; } /** * Sets the 'scope' attribute. * * @param scope Scope of the 'var' attribute */ public void setScope(String scope) { if (scope.equalsIgnoreCase("page")) { this.scope = PageContext.PAGE_SCOPE; } else if (scope.equalsIgnoreCase("request")) { this.scope = PageContext.REQUEST_SCOPE; } else if (scope.equalsIgnoreCase("session")) { this.scope = PageContext.SESSION_SCOPE; } else if (scope.equalsIgnoreCase("application")) { this.scope = PageContext.APPLICATION_SCOPE; } // TODO: Add error handling? Needs direction from spec. } //********************************************************************* // Utility methods // expose attributes if we have a non-null 'var' private void exposeVariables() { if (var != null) { pageContext.setAttribute(var, new Boolean(result), scope); } } // initializes internal state private void init() { result = false; // not really necessary var = null; scope = PageContext.PAGE_SCOPE; } }
还是从开始 doStartTag 开始
/** * Includes its body if <tt>condition()</tt> evaluates to true. */ @Override public int doStartTag() throws JspException { // execute our condition() method once per invocation result = condition(); // expose variables if appropriate exposeVariables(); // handle conditional behavior if (result) { return EVAL_BODY_INCLUDE; } else { return SKIP_BODY; } }
获取test的值 给 result 然后将结果 导出
// Utility methods // expose attributes if we have a non-null 'var' private void exposeVariables() { if (var != null) { pageContext.setAttribute(var, new Boolean(result), scope); } }
然后再根据 test 即 result的值判断 是否执行标签的内容 为true的话 进一步解析标签内容 不然 不解析标签内容
此即是if标签
3、catch标签 也比较简单
jsp页面
<c:catch var="tryInfo2"> <c:set var="name" value="under" scope="request"></c:set> <c:set value="unde" target="person" property="name"></c:set> </c:catch>
<c:out value="因为没有person对象 也没有setName方法 所以会报错 ${tryInfo2 }" escapeXml="false"></c:out>
tld描述
<tag> <description> Catches any Throwable that occurs in its body and optionally exposes it. </description> <name>catch</name> <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class> <body-content>JSP</body-content> <attribute> <description> Name of the exported scoped variable for the exception thrown from a nested action. The type of the scoped variable is the type of the exception thrown. </description> <name>var</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
标签处理类 CatchTag.java
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.taglibs.standard.tag.common.core; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.tagext.TryCatchFinally; /** * <p>Tag handler for <catch> in JSTL 1.0.</p> * * <p><catch> simply catches any Throwables that occur in its body * and optionally exposes them. * * @author Shawn Bayern */ public class CatchTag extends TagSupport implements TryCatchFinally { /* * If all tags that I proposed were this simple, people might * think I was just trying to avoid work. :-) */ //********************************************************************* // Constructor and lifecycle management // initialize inherited and local state public CatchTag() { super(); init(); } // Releases any resources we may have (or inherit) @Override public void release() { super.release(); init(); } private void init() { var = null; } //********************************************************************* // Private state private String var; // tag attribute private boolean caught; // internal status //********************************************************************* // Tag logic @Override public int doStartTag() { caught = false; return EVAL_BODY_INCLUDE; } public void doCatch(Throwable t) { if (var != null) { pageContext.setAttribute(var, t, PageContext.PAGE_SCOPE); } caught = true; } public void doFinally() { if (var != null && !caught) { pageContext.removeAttribute(var, PageContext.PAGE_SCOPE); } } //********************************************************************* // Attribute accessors public void setVar(String var) { this.var = var; } }
会发现 多了doCatch doFinally两个方法 来自于 TryCatchFinally接口
/* * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the "License"). You may not use this file except * in compliance with the License. * * You can obtain a copy of the license at * glassfish/bootstrap/legal/CDDLv1.0.txt or * https://glassfish.dev.java.net/public/CDDLv1.0.html. * See the License for the specific language governing * permissions and limitations under the License. * * When distributing Covered Code, include this CDDL * HEADER in each file and include the License file at * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable, * add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your * own identifying information: Portions Copyright [yyyy] * [name of copyright owner] * * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * * Portions Copyright Apache Software Foundation. */ package javax.servlet.jsp.tagext; /** * The auxiliary interface of a Tag, IterationTag or BodyTag tag * handler that wants additional hooks for managing resources. * * <p>This interface provides two new methods: doCatch(Throwable) * and doFinally(). The prototypical invocation is as follows: * * <pre> * h = get a Tag(); // get a tag handler, perhaps from pool * * h.setPageContext(pc); // initialize as desired * h.setParent(null); * h.setFoo("foo"); * * // tag invocation protocol; see Tag.java * try { * doStartTag()... * .... * doEndTag()... * } catch (Throwable t) { * // react to exceptional condition * h.doCatch(t); * } finally { * // restore data invariants and release per-invocation resources * h.doFinally(); * } * * ... other invocations perhaps with some new setters * ... * h.release(); // release long-term resources * </pre> */ public interface TryCatchFinally { /** * Invoked if a Throwable occurs while evaluating the BODY * inside a tag or in any of the following methods: * Tag.doStartTag(), Tag.doEndTag(), * IterationTag.doAfterBody() and BodyTag.doInitBody(). * * <p>This method is not invoked if the Throwable occurs during * one of the setter methods. * * <p>This method may throw an exception (the same or a new one) * that will be propagated further up the nest chain. If an exception * is thrown, doFinally() will be invoked. * * <p>This method is intended to be used to respond to an exceptional * condition. * * @param t The throwable exception navigating through this tag. * @throws Throwable if the exception is to be rethrown further up * the nest chain. */ void doCatch(Throwable t) throws Throwable; /** * Invoked in all cases after doEndTag() for any class implementing * Tag, IterationTag or BodyTag. This method is invoked even if * an exception has occurred in the BODY of the tag, * or in any of the following methods: * Tag.doStartTag(), Tag.doEndTag(), * IterationTag.doAfterBody() and BodyTag.doInitBody(). * * <p>This method is not invoked if the Throwable occurs during * one of the setter methods. * * <p>This method should not throw an Exception. * * <p>This method is intended to maintain per-invocation data * integrity and resource management actions. */ void doFinally(); }
比较详细的解释的话 就是这个了
// tag invocation protocol; see Tag.java * try { * doStartTag()... * .... * doEndTag()... * } catch (Throwable t) { * // react to exceptional condition * h.doCatch(t); * } finally { * // restore data invariants and release per-invocation resources * h.doFinally(); * }
在执行catch里面的标签是 会将其放入try catch finally 代码块 当有异常发生时 调用 doCatch方法 向page页面添加名为var的错误信息’
public void doCatch(Throwable t) { if (var != null) { pageContext.setAttribute(var, t, PageContext.PAGE_SCOPE); } caught = true; }
最后标签执行完 不管有没有异常 都会调用finally代码块 调用doFinally来根据条件清除属性
public void doFinally() { if (var != null && !caught) { pageContext.removeAttribute(var, PageContext.PAGE_SCOPE); } }
此即使catch标签 的过程 比较简单
4、看最后一个 forEach标签 有点复杂 其实
jsp页面
<c:out value="=========================测试jstl的forEach标签========================="></c:out> <br/> <c:out value="字符串迭代"></c:out> <br/> <c:catch var="tryInfo"> <c:forEach items="'q1','q2','q3'" var="p"> <c:out value="${p}"></c:out> </c:forEach> </c:catch> <br/> <c:out value="${tryInfo }"></c:out> <br/> <c:out value="列表迭代"></c:out> <br/> <% List<Integer> numList=new ArrayList<Integer>(); numList.add(12); numList.add(20); numList.add(25); pageContext.setAttribute("numList", numList); Map map=new HashMap(); %> <c:catch var="tryInfo1"> <c:forEach items="${numList }" var="p" varStatus="ps" step="1" begin="0"> <c:out value="当前元素为:${p} 开始迭代位置:${ps.begin } ${ps.end } 列表迭代步长:${ps.step } 列表中索引位置:${ps.index } 迭代列表中位置:${ps.count } 当前元素是否是第一个:${ps.first } 当前元素是否是最后一个:${ps.last } "></c:out> <br/> </c:forEach> </c:catch> <br/> <c:out value="${tryInfo1 }"></c:out> <br/> <c:out value="=========================测试jstl的forEach标签========================="></c:out> <br/>
tld描述
<tag> <description> The basic iteration tag, accepting many different collection types and supporting subsetting and other functionality </description> <name>forEach</name> <tag-class>org.apache.taglibs.standard.tag.rt.core.ForEachTag</tag-class> <tei-class>org.apache.taglibs.standard.tei.ForEachTEI</tei-class> <body-content>JSP</body-content> <attribute> <description> Collection of items to iterate over. </description> <name>items</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <type>java.lang.Object</type> <deferred-value> <type>java.lang.Object</type> </deferred-value> </attribute> <attribute> <description> If items specified: Iteration begins at the item located at the specified index. First item of the collection has index 0. If items not specified: Iteration begins with index set at the value specified. </description> <name>begin</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <type>int</type> </attribute> <attribute> <description> If items specified: Iteration ends at the item located at the specified index (inclusive). If items not specified: Iteration ends when index reaches the value specified. </description> <name>end</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <type>int</type> </attribute> <attribute> <description> Iteration will only process every step items of the collection, starting with the first one. </description> <name>step</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <type>int</type> </attribute> <attribute> <description> Name of the exported scoped variable for the current item of the iteration. This scoped variable has nested visibility. Its type depends on the object of the underlying collection. </description> <name>var</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <attribute> <description> Name of the exported scoped variable for the status of the iteration. Object exported is of type javax.servlet.jsp.jstl.core.LoopTagStatus. This scoped variable has nested visibility. </description> <name>varStatus</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
看标签解析类 ForEachTag.java
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.taglibs.standard.tag.rt.core; import java.util.ArrayList; import javax.servlet.jsp.JspTagException; import javax.servlet.jsp.jstl.core.LoopTag; import javax.servlet.jsp.tagext.IterationTag; import org.apache.taglibs.standard.tag.common.core.ForEachSupport; /** * <p>A handler for <forEach> that supports rtexprvalue-based * attributes.</p> * * @author Shawn Bayern */ public class ForEachTag extends ForEachSupport implements LoopTag, IterationTag { //********************************************************************* // Accessor methods // for tag attribute public void setBegin(int begin) throws JspTagException { this.beginSpecified = true; this.begin = begin; validateBegin(); } // for tag attribute public void setEnd(int end) throws JspTagException { this.endSpecified = true; this.end = end; validateEnd(); } // for tag attribute public void setStep(int step) throws JspTagException { this.stepSpecified = true; this.step = step; validateStep(); } public void setItems(Object o) throws JspTagException { // for null items, simulate an empty list if (o == null) { rawItems = new ArrayList(); } else { rawItems = o; } } }
会发现 只有 begin end step items 的赋值方法 看父类 ForEachSupport.java
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.taglibs.standard.tag.common.core; import org.apache.taglibs.standard.resources.Resources; import javax.el.ValueExpression; import javax.servlet.jsp.JspTagException; import javax.servlet.jsp.jstl.core.IndexedValueExpression; import javax.servlet.jsp.jstl.core.IteratedExpression; import javax.servlet.jsp.jstl.core.IteratedValueExpression; import javax.servlet.jsp.jstl.core.LoopTagSupport; import java.lang.reflect.Array; import java.util.*; /** * <p>Support for tag handlers for <forEach>, the core iteration * tag in JSTL 1.0. This class extends LoopTagSupport and provides * ForEach-specific functionality. The rtexprvalue library and the * expression-evaluating library each have handlers that extend this * class.</p> * <p>Localized here is the logic for handling the veritable smorgasbord * of types supported by <forEach>, including arrays, * Collections, and others. To see how the actual iteration is controlled, * review the javax.servlet.jsp.jstl.core.LoopTagSupport class instead. * </p> * * @author Shawn Bayern * @see javax.servlet.jsp.jstl.core.LoopTagSupport */ public abstract class ForEachSupport extends LoopTagSupport { protected Iterator items; // our 'digested' items protected Object rawItems; // our 'raw' items @Override protected void prepare() throws JspTagException { // produce the right sort of ForEachIterator if (rawItems == null) { // if no items were specified, iterate from begin to end items = new ToEndIterator(end); } else if (rawItems instanceof ValueExpression) { deferredExpression = (ValueExpression) rawItems; Object o = deferredExpression.getValue(pageContext.getELContext()); Iterator iterator = toIterator(o); if (isIndexed(o)) { items = new IndexedDeferredIterator(iterator, deferredExpression); } else { items = new IteratedDeferredIterator(iterator, new IteratedExpression(deferredExpression, getDelims())); } } else { items = toIterator(rawItems); } } private Iterator toIterator(Object rawItems) throws JspTagException { if (rawItems instanceof Collection) { return ((Collection) rawItems).iterator(); } else if (rawItems.getClass().isArray()) { return new ArrayIterator(rawItems); } else if (rawItems instanceof Iterator) { return (Iterator) rawItems; } else if (rawItems instanceof Enumeration) { return new EnumerationIterator((Enumeration) rawItems); } else if (rawItems instanceof Map) { return ((Map) rawItems).entrySet().iterator(); } else if (rawItems instanceof String) { return new EnumerationIterator(new StringTokenizer((String) rawItems, ",")); } else { throw new JspTagException(Resources.getMessage("FOREACH_BAD_ITEMS")); } } private boolean isIndexed(Object o) { return o.getClass().isArray(); } @Override protected boolean hasNext() throws JspTagException { return items.hasNext(); } @Override protected Object next() throws JspTagException { return items.next(); } @Override public void release() { super.release(); items = null; rawItems = null; } /** * Iterator that simply counts up to 'end.' * Unlike the previous implementation this does not attempt to pre-allocate an array * containing all values from 0 to 'end' as that can result in excessive memory allocation * for large values of 'end.' * LoopTagSupport calls next() 'begin' times in order to discard the initial values, * In order to maintain this contract, this implementation always starts at 0. * Future optimization to skip these redundant calls might be possible. */ private static class ToEndIterator extends ReadOnlyIterator { private final int end; private int i; private ToEndIterator(int end) { this.end = end; } public boolean hasNext() { return i <= end; } public Object next() { if (i <= end) { return i++; } else { throw new NoSuchElementException(); } } } /** * Iterator over an Enumeration. */ private static class EnumerationIterator extends ReadOnlyIterator { private final Enumeration e; private EnumerationIterator(Enumeration e) { this.e = e; } public boolean hasNext() { return e.hasMoreElements(); } public Object next() { return e.nextElement(); } } /** * Iterator over an array, including arrays of primitive types. */ private static class ArrayIterator extends ReadOnlyIterator { private final Object array; private final int length; private int i = 0; private ArrayIterator(Object array) { this.array = array; length = Array.getLength(array); } public boolean hasNext() { return i < length; } public Object next() { try { return Array.get(array, i++); } catch (ArrayIndexOutOfBoundsException e) { throw new NoSuchElementException(); } } } private static class IndexedDeferredIterator extends DeferredIterator { private final ValueExpression itemsValueExpression; private IndexedDeferredIterator(Iterator iterator, ValueExpression itemsValueExpression) { super(iterator); this.itemsValueExpression = itemsValueExpression; } public Object next() { iterator.next(); return new IndexedValueExpression(itemsValueExpression, currentIndex++); } } private static class IteratedDeferredIterator extends DeferredIterator { private final IteratedExpression itemsValueIteratedExpression; private IteratedDeferredIterator(Iterator iterator, IteratedExpression itemsValueIteratedExpression) { super(iterator); this.itemsValueIteratedExpression = itemsValueIteratedExpression; } public Object next() { iterator.next(); return new IteratedValueExpression(itemsValueIteratedExpression, currentIndex++); } } private abstract static class DeferredIterator extends ReadOnlyIterator { protected final Iterator iterator; protected int currentIndex = 0; protected DeferredIterator(Iterator iterator) { this.iterator = iterator; } public boolean hasNext() { return iterator.hasNext(); } } private abstract static class ReadOnlyIterator implements Iterator { public void remove() { throw new UnsupportedOperationException(); } } }
会发现此类 看起来不太完整 连不起来 继续看父类 LoopTagSupport.java
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package javax.servlet.jsp.jstl.core; import javax.el.ELContext; import javax.el.ValueExpression; import javax.el.VariableMapper; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspTagException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.IterationTag; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.tagext.TryCatchFinally; /** * <p>Base support class to facilitate implementation of iteration tags.</p> * <p>Since most iteration tags will behave identically with respect to * actual iterative behavior, JSTL provides this * base support class to facilitate implementation. Many iteration tags * will extend this and merely implement the <tt>hasNext()</tt> and * <tt>next()</tt> methods * to provide contents for the handler to iterate over.</p> * <p>In particular, this base class provides support for:</p> * <ul> * <li> Iteration control, based on protected <tt>prepare()</tt>, <tt>next()</tt>, * and <tt>hasNext()</tt> methods * <li> Subsetting (<tt>begin</tt>, <tt>end</tt>, <tt>step></tt>functionality, * including validation * of subset parameters for sensibility) * <li> item retrieval (<tt>getCurrent()</tt>) * <li> status retrieval (<tt>LoopTagStatus</tt>) * <li> exposing attributes (set by <tt>var</tt> and <tt>varStatus</tt> attributes) * </ul> * <p>In providing support for these tasks, <tt>LoopTagSupport</tt> contains * certain control variables that act to modify the iteration. Accessors * are provided for these control variables when the variables represent * information needed or wanted at translation time (e.g., <tt>var</tt>, * <tt>varStatus</tt>). For * other variables, accessors cannot be provided here since subclasses * may differ on their implementations of how those accessors are received. * For instance, one subclass might accept a <tt>String</tt> and convert it into * an object of a specific type by using an expression evaluator; others * might accept objects directly. Still others might not want to expose * such information to outside control.</p> * * @author Shawn Bayern */ public abstract class LoopTagSupport extends TagSupport implements LoopTag, IterationTag, TryCatchFinally { //********************************************************************* // 'Protected' state /* * JavaBean-style properties and other state slaved to them. These * properties can be set directly by accessors; they will not be * modified by the LoopTagSupport implementation -- and should * not be modified by subclasses outside accessors unless those * subclasses are perfectly aware of what they're doing. * (An example where such non-accessor modification might be sensible * is in the doStartTag() method of an EL-aware subclass.) */ /** * Starting index ('begin' attribute) */ protected int begin; /** * Ending index of the iteration ('end' attribute). * A value of -1 internally indicates 'no end * specified', although accessors for the core JSTL tags do not * allow this value to be supplied directly by the user. */ protected int end; /** * Iteration step ('step' attribute) */ protected int step; /** * Boolean flag indicating whether 'begin' was specified. */ protected boolean beginSpecified; /** * Boolean flag indicating whether 'end' was specified. */ protected boolean endSpecified; /** * Boolean flag indicating whether 'step' was specified. */ protected boolean stepSpecified; /** * Attribute-exposing control */ protected String itemId, statusId; protected ValueExpression deferredExpression; //********************************************************************* // 'Private' state (implementation details) /* * State exclusively internal to the default, reference implementation. * (While this state is kept private to ensure consistency, 'status' * and 'item' happen to have one-for-one, read-only, accesor methods * as part of the LoopTag interface.) * * 'last' is kept separately for two reasons: (a) to avoid * running a computation every time it's requested, and (b) to * let LoopTagStatus.isLast() avoid throwing any exceptions, * which would complicate subtag and scripting-variable use. * * Our 'internal index' begins at 0 and increases by 'step' each * round; this is arbitrary, but it seemed a simple way of keeping * track of the information we need. To avoid computing * getLoopStatus().getCount() by dividing index / step, we keep * a separate 'count' and increment it by 1 each round (as a minor * performance improvement). */ private LoopTagStatus status; // our LoopTagStatus private Object item; // the current item private int index; // the current internal index private int count; // the iteration count private boolean last; // current round == last one? //********************************************************************* // Constructor /** * Constructs a new LoopTagSupport. As with TagSupport, subclasses * should not implement constructors with arguments, and no-arguments * constructors implemented by subclasses must call the superclass * constructor. */ public LoopTagSupport() { super(); init(); } //********************************************************************* // Abstract methods /** * <p>Returns the next object over which the tag should iterate. This * method must be provided by concrete subclasses of LoopTagSupport * to inform the base logic about what objects it should iterate over.</p> * <p>It is expected that this method will generally be backed by an * Iterator, but this will not always be the case. In particular, if * retrieving the next object raises the possibility of an exception * being thrown, this method allows that exception to propagate back * to the JSP container as a JspTagException; a standalone Iterator * would not be able to do this. (This explains why LoopTagSupport * does not simply call for an Iterator from its subtags.)</p> * * @return the java.lang.Object to use in the next round of iteration * @throws java.util.NoSuchElementException * if next() is called but no new elements are available * @throws javax.servlet.jsp.JspTagException * for other, unexpected exceptions */ protected abstract Object next() throws JspTagException; /** * <p>Returns information concerning the availability of more items * over which to iterate. This method must be provided by concrete * subclasses of LoopTagSupport to assist the iterative logic * provided by the supporting base class.</p> * <p>See <a href="#next()">next</a> for more information about the * purpose and expectations behind this tag.</p> * * @return <tt>true</tt> if there is at least one more item to iterate * over, <tt>false</tt> otherwise * @throws javax.servlet.jsp.JspTagException * * @see #next */ protected abstract boolean hasNext() throws JspTagException; /** * <p>Prepares for a single tag invocation. Specifically, allows * subclasses to prepare for calls to hasNext() and next(). * Subclasses can assume that prepare() will be called once for * each invocation of doStartTag() in the superclass.</p> * * @throws javax.servlet.jsp.JspTagException * */ protected abstract void prepare() throws JspTagException; //********************************************************************* // Lifecycle management and implementation of iterative behavior /** * Releases any resources this LoopTagSupport may have (or inherit). */ @Override public void release() { super.release(); init(); } /** * Begins iterating by processing the first item. */ @Override public int doStartTag() throws JspException { if (end != -1 && begin > end) { // JSTL 1.1. We simply do not execute the loop. return SKIP_BODY; } // we're beginning a new iteration, so reset our counts (etc.) index = 0; count = 1; last = false; // let the subclass conduct any necessary preparation prepare(); // throw away the first 'begin' items (if they exist) discardIgnoreSubset(begin); // get the item we're interested in if (hasNext()) // index is 0-based, so we don't update it for the first item { item = next(); } else { return SKIP_BODY; } /* * now discard anything we have to "step" over. * (we do this in advance to support LoopTagStatus.isLast()) */ discard(step - 1); // prepare to include our body... exposeVariables(); calibrateLast(); return EVAL_BODY_INCLUDE; } /** * Continues the iteration when appropriate -- that is, if we (a) have * more items and (b) don't run over our 'end' (given our 'step'). */ @Override public int doAfterBody() throws JspException { // re-sync the index, given our prior behind-the-scenes 'step' index += step - 1; // increment the count by 1 for each round count++; // everything's been prepared for us, so just get the next item if (hasNext() && !atEnd()) { index++; item = next(); } else { return SKIP_BODY; } /* * now discard anything we have to "step" over. * (we do this in advance to support LoopTagStatus.isLast()) */ discard(step - 1); // prepare to re-iterate... exposeVariables(); calibrateLast(); return EVAL_BODY_AGAIN; } /** * Removes any attributes that this LoopTagSupport set. * <p> These attributes are intended to support scripting variables with * NESTED scope, so we don't want to pollute attribute space by leaving * them lying around. */ public void doFinally() { /* * Make sure to un-expose variables, restoring them to their * prior values, if applicable. */ unExposeVariables(); } /** * Rethrows the given Throwable. */ public void doCatch(Throwable t) throws Throwable { throw t; } //********************************************************************* // Accessor methods /* * Overview: The getXXX() methods we provide implement the Tag * contract. setXXX() accessors are provided only for those * properties (attributes) that must be known at translation time, * on the premise that these accessors will vary less than the * others in terms of their interface with the page author. */ /* * (Purposely inherit JavaDoc and semantics from LoopTag. * Subclasses can override this if necessary, but such a need is * expected to be rare.) */ public Object getCurrent() { return item; } /* * (Purposely inherit JavaDoc and semantics from LoopTag. * Subclasses can override this method for more fine-grained control * over LoopTagStatus, but an effort has been made to simplify * implementation of subclasses that are happy with reasonable default * behavior.) */ public LoopTagStatus getLoopStatus() { // local implementation with reasonable default behavior class Status implements LoopTagStatus { /* * All our methods are straightforward. We inherit * our JavaDoc from LoopTagSupport; see that class * for more information. */ public Object getCurrent() { /* * Access the item through getCurrent() instead of just * returning the item our containing class stores. This * should allow a subclass of LoopTagSupport to override * getCurrent() without having to rewrite getLoopStatus() too. */ return (LoopTagSupport.this.getCurrent()); } public int getIndex() { return (index + begin); // our 'index' isn't getIndex() } public int getCount() { return (count); } public boolean isFirst() { return (index == 0); // our 'index' isn't getIndex() } public boolean isLast() { return (last); // use cached value } public Integer getBegin() { if (beginSpecified) { return (new Integer(begin)); } else { return null; } } public Integer getEnd() { if (endSpecified) { return (new Integer(end)); } else { return null; } } public Integer getStep() { if (stepSpecified) { return (new Integer(step)); } else { return null; } } } /* * We just need one per invocation... Actually, for the current * implementation, we just need one per instance, but I'd rather * not keep the reference around once release() has been called. */ if (status == null) { status = new Status(); } return status; } /* * We only support setter methods for attributes that need to be * offered as Strings or other literals; other attributes will be * handled directly by implementing classes, since there might be * both rtexprvalue- and EL-based varieties, which will have * different signatures. (We can't pollute child classes by having * base implementations of those setters here; child classes that * have attributes with different signatures would end up having * two incompatible setters, which is illegal for a JavaBean. */ /** * Sets the 'var' attribute. * * @param id Name of the exported scoped variable storing the current item * of the iteration. */ public void setVar(String id) { this.itemId = id; } /** * Sets the 'varStatus' attribute. * * @param statusId Name of the exported scoped variable storing the status * of the iteration. */ public void setVarStatus(String statusId) { this.statusId = statusId; } //********************************************************************* // Protected utility methods /* * These methods validate attributes common to iteration tags. * Call them if your own subclassing implementation modifies them * -- e.g., if you set them through an expression language. */ /** * Ensures the "begin" property is sensible, throwing an exception * expected to propagate up if it isn't */ protected void validateBegin() throws JspTagException { if (begin < 0) { throw new JspTagException("'begin' < 0"); } } /** * Ensures the "end" property is sensible, throwing an exception * expected to propagate up if it isn't */ protected void validateEnd() throws JspTagException { if (end < 0) { throw new JspTagException("'end' < 0"); } } /** * Ensures the "step" property is sensible, throwing an exception * expected to propagate up if it isn't */ protected void validateStep() throws JspTagException { if (step < 1) { throw new JspTagException("'step' <= 0"); } } //********************************************************************* // Private utility methods /** * (Re)initializes state (during release() or construction) */ private void init() { // defaults for internal bookkeeping index = 0; // internal index always starts at 0 count = 1; // internal count always starts at 1 status = null; // we clear status on release() item = null; // item will be retrieved for each round last = false; // last must be set explicitly beginSpecified = false; // not specified until it's specified :-) endSpecified = false; // (as above) stepSpecified = false; // (as above) deferredExpression = null; // defaults for interface with page author begin = 0; // when not specified, 'begin' is 0 by spec. end = -1; // when not specified, 'end' is not used step = 1; // when not specified, 'step' is 1 itemId = null; // when not specified, no variable exported statusId = null; // when not specified, no variable exported } /** * Sets 'last' appropriately. */ private void calibrateLast() throws JspTagException { /* * the current round is the last one if (a) there are no remaining * elements, or (b) the next one is beyond the 'end'. */ last = !hasNext() || atEnd() || (end != -1 && (begin + index + step > end)); } /** * Exposes attributes (formerly scripting variables, but no longer!) * if appropriate. Note that we don't really care, here, whether they're * scripting variables or not. */ private void exposeVariables() throws JspTagException { if (deferredExpression == null) { /* * We need to support null items returned from next(); we * do this simply by passing such non-items through to the * scoped variable as effectively 'null' (that is, by calling * removeAttribute()). * * Also, just to be defensive, we handle the case of a null * 'status' object as well. * * We call getCurrent() and getLoopStatus() (instead of just using * 'item' and 'status') to bridge to subclasses correctly. * A subclass can override getCurrent() or getLoopStatus() but still * depend on our doStartTag() and doAfterBody(), which call this * method (exposeVariables()), to expose 'item' and 'status' * correctly. */ if (itemId != null) { if (getCurrent() == null) { pageContext.removeAttribute(itemId, PageContext.PAGE_SCOPE); } else { pageContext.setAttribute(itemId, getCurrent()); } } } else { //this is using a DeferredExpression ELContext myELContext = pageContext.getELContext(); VariableMapper vm = myELContext.getVariableMapper(); vm.setVariable(itemId, (ValueExpression) getCurrent()); } if (statusId != null) { if (getLoopStatus() == null) { pageContext.removeAttribute(statusId, PageContext.PAGE_SCOPE); } else { pageContext.setAttribute(statusId, getLoopStatus()); } } } /** * Removes page attributes that we have exposed and, if applicable, * restores them to their prior values (and scopes). */ private void unExposeVariables() { if (deferredExpression == null) { // "nested" variables are now simply removed if (itemId != null) { pageContext.removeAttribute(itemId, PageContext.PAGE_SCOPE); } } else { //we're deferred ... remove variable mapping ELContext myELContext = pageContext.getELContext(); VariableMapper vm = myELContext.getVariableMapper(); vm.setVariable(itemId, null); } if (statusId != null) { pageContext.removeAttribute(statusId, PageContext.PAGE_SCOPE); } } /** * Cycles through and discards up to 'n' items from the iteration. * We only know "up to 'n'", not "exactly n," since we stop cycling * if hasNext() returns false or if we hit the 'end' of the iteration. * Note: this does not update the iteration index, since this method * is intended as a behind-the-scenes operation. The index must be * updated separately. (I don't really like this, but it's the simplest * way to support isLast() without storing two separate inconsistent * indices. We need to (a) make sure hasNext() refers to the next * item we actually *want* and (b) make sure the index refers to the * item associated with the *current* round, not the next one. * C'est la vie.) */ private void discard(int n) throws JspTagException { /* * copy index so we can restore it, but we need to update it * as we work so that atEnd() works */ int oldIndex = index; while (n-- > 0 && !atEnd() && hasNext()) { index++; next(); } index = oldIndex; } /** * Discards items ignoring subsetting rules. Useful for discarding * items from the beginning (i.e., to implement 'begin') where we * don't want factor in the 'begin' value already. */ private void discardIgnoreSubset(int n) throws JspTagException { while (n-- > 0 && hasNext()) { next(); } } /** * Returns true if the iteration has past the 'end' index (with * respect to subsetting), false otherwise. ('end' must be set * for atEnd() to return true; if 'end' is not set, atEnd() * always returns false.) */ private boolean atEnd() { return ((end != -1) && (begin + index >= end)); } /** * Get the delimiter for string tokens. Used only for constructing * the deferred expression for it. */ protected String getDelims() { return ","; } }
哈哈 找到头了 doStartTag 这就好办了 哦 对了 在执行doStartTag之前 执行构造函数
/** * Constructs a new LoopTagSupport. As with TagSupport, subclasses * should not implement constructors with arguments, and no-arguments * constructors implemented by subclasses must call the superclass * constructor. */ public LoopTagSupport() { super(); init(); }
子类不要构建有参数的构造器 不然此父类构造器不会被调用 那么init()就没的执行了 哈哈 一般 我们也不会用 其实
/** * (Re)initializes state (during release() or construction) */ private void init() { // defaults for internal bookkeeping index = 0; // internal index always starts at 0 count = 1; // internal count always starts at 1 status = null; // we clear status on release() item = null; // item will be retrieved for each round last = false; // last must be set explicitly beginSpecified = false; // not specified until it's specified :-) endSpecified = false; // (as above) stepSpecified = false; // (as above) deferredExpression = null; // defaults for interface with page author begin = 0; // when not specified, 'begin' is 0 by spec. end = -1; // when not specified, 'end' is not used step = 1; // when not specified, 'step' is 1 itemId = null; // when not specified, no variable exported statusId = null; // when not specified, no variable exported }
看到了吧 我们在使用 c:forEach标签的时候 不指定 begin end step 它都会帮我们设定初始化的值
但是如果 你没有指定begin end step的值的话 在jsp页面里面使用${ps.end}就获取不到值啊 其实获取到空值 为什么呢 原因在这
public Integer getEnd() { if (endSpecified) { return (new Integer(end)); } else { return null; } }
看到了吧 根据endSpecified进行返回值 而在 上面的init中 设为了 false 只有当 你设置了end值的时候 才会调用 setEnd方法 进行修改此值
// for tag attribute public void setEnd(int end) throws JspTagException { this.endSpecified = true; this.end = end; validateEnd(); }
调用完构造器后 就调用doStartTag了
/** * Begins iterating by processing the first item. */ @Override public int doStartTag() throws JspException { if (end != -1 && begin > end) { // JSTL 1.1. We simply do not execute the loop. return SKIP_BODY; } // we're beginning a new iteration, so reset our counts (etc.) index = 0; count = 1; last = false; // let the subclass conduct any necessary preparation prepare(); // throw away the first 'begin' items (if they exist) discardIgnoreSubset(begin); // get the item we're interested in if (hasNext()) // index is 0-based, so we don't update it for the first item { item = next(); } else { return SKIP_BODY; } /* * now discard anything we have to "step" over. * (we do this in advance to support LoopTagStatus.isLast()) */ discard(step - 1); // prepare to include our body... exposeVariables(); calibrateLast(); return EVAL_BODY_INCLUDE; }
进来先会判断设定的begin end的大小 如果没有设定的话 end=-1 begin=0
然后会设定 index count last几个状态值的初始值
接着会调用子类即ForEachSupport的prepare方法
@Override protected void prepare() throws JspTagException { // produce the right sort of ForEachIterator if (rawItems == null) { // if no items were specified, iterate from begin to end items = new ToEndIterator(end); } else if (rawItems instanceof ValueExpression) { deferredExpression = (ValueExpression) rawItems; Object o = deferredExpression.getValue(pageContext.getELContext()); Iterator iterator = toIterator(o); if (isIndexed(o)) { items = new IndexedDeferredIterator(iterator, deferredExpression); } else { items = new IteratedDeferredIterator(iterator, new IteratedExpression(deferredExpression, getDelims())); } } else { items = toIterator(rawItems); } }
此方法和其他方法一起构建 迭代的对象items 因为参数类型不一致 所以需要多种类型辅助 看toIterator即可
protected Iterator items;
接着看doStartTag 中的就是 discardIgnoreSubset(begin);
/** * Discards items ignoring subsetting rules. Useful for discarding * items from the beginning (i.e., to implement 'begin') where we * don't want factor in the 'begin' value already. */ private void discardIgnoreSubset(int n) throws JspTagException { while (n-- > 0 && hasNext()) { next(); } }
此方法的作用 即是将迭代器的位置 遍历到begin 所指向的位置
接着是 获取到begin位置的值 给item
// get the item we're interested in if (hasNext()) // index is 0-based, so we don't update it for the first item { item = next(); } else { return SKIP_BODY; }
接着是 discard(step - 1);
private void discard(int n) throws JspTagException { /* * copy index so we can restore it, but we need to update it * as we work so that atEnd() works */ int oldIndex = index; while (n-- > 0 && !atEnd() && hasNext()) { index++; next(); } index = oldIndex; }
此方法的作用 就是将迭代器的指针指向下一个即将迭代对象的前一个
为了防止出现step不等于1的情况 这样处理后 迭代器拥有都是指向以begin为基础step为变化值的子集项
接下来 导出var和varStatus的值
/** * Exposes attributes (formerly scripting variables, but no longer!) * if appropriate. Note that we don't really care, here, whether they're * scripting variables or not. */ private void exposeVariables() throws JspTagException { if (deferredExpression == null) { /* * We need to support null items returned from next(); we * do this simply by passing such non-items through to the * scoped variable as effectively 'null' (that is, by calling * removeAttribute()). * * Also, just to be defensive, we handle the case of a null * 'status' object as well. * * We call getCurrent() and getLoopStatus() (instead of just using * 'item' and 'status') to bridge to subclasses correctly. * A subclass can override getCurrent() or getLoopStatus() but still * depend on our doStartTag() and doAfterBody(), which call this * method (exposeVariables()), to expose 'item' and 'status' * correctly. */ if (itemId != null) { if (getCurrent() == null) { pageContext.removeAttribute(itemId, PageContext.PAGE_SCOPE); } else { pageContext.setAttribute(itemId, getCurrent()); } } } else { //this is using a DeferredExpression ELContext myELContext = pageContext.getELContext(); VariableMapper vm = myELContext.getVariableMapper(); vm.setVariable(itemId, (ValueExpression) getCurrent()); } if (statusId != null) { if (getLoopStatus() == null) { pageContext.removeAttribute(statusId, PageContext.PAGE_SCOPE); } else { pageContext.setAttribute(statusId, getLoopStatus()); } } }
向page范围写入var和varStatus的值
接着 校正last的状态值
/** * Sets 'last' appropriately. */ private void calibrateLast() throws JspTagException { /* * the current round is the last one if (a) there are no remaining * elements, or (b) the next one is beyond the 'end'. */ last = !hasNext() || atEnd() || (end != -1 && (begin + index + step > end)); }
此时doStartTag方法完了后 因为返回值为 EVAL_BODY_INCLUDE
/** * Evaluate body into existing out stream. * Valid return value for doStartTag. */ public final static int EVAL_BODY_INCLUDE = 1;所以会解析forEach标签内的值 进行显示 当标签内的值 第一次解析完了之后 调用 doAfterBody
/** * Continues the iteration when appropriate -- that is, if we (a) have * more items and (b) don't run over our 'end' (given our 'step'). */ @Override public int doAfterBody() throws JspException { // re-sync the index, given our prior behind-the-scenes 'step' index += step - 1; // increment the count by 1 for each round count++; // everything's been prepared for us, so just get the next item if (hasNext() && !atEnd()) { index++; item = next(); } else { return SKIP_BODY; } /* * now discard anything we have to "step" over. * (we do this in advance to support LoopTagStatus.isLast()) */ discard(step - 1); // prepare to re-iterate... exposeVariables(); calibrateLast(); return EVAL_BODY_AGAIN; }此方法即是 forEach从第二次开始迭代的基础
通过 hasNext和atEnd判断此迭代是否该继续
/** * Returns true if the iteration has past the 'end' index (with * respect to subsetting), false otherwise. ('end' must be set * for atEnd() to return true; if 'end' is not set, atEnd() * always returns false.) */ private boolean atEnd() { return ((end != -1) && (begin + index >= end)); }
执行完后 返回
EVAL_BODY_AGAIN
此值是在IterationTag里面定义的 用于重复解析forEach标签中的内容
/** * Request the reevaluation of some body. * Returned from doAfterBody. * * For compatibility with JSP 1.1, the value is carefully selected * to be the same as the, now deprecated, BodyTag.EVAL_BODY_TAG, * */ public final static int EVAL_BODY_AGAIN = 2;
此即使forEach标签的执行流程
目前 只是看了这几个 等到有时间了 再看吧 记录学习的脚步
相关文章推荐
- maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)
- maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)
- maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)(二)
- (forEach url out set catch 等)javaweb中JSTL的Core 核心标签库知识点
- 【转】JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL常用标签的使用:if、forEach、out等标签
- JSTL标签参考手册,JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- Jstl表达式out、set、if、choose、forEach
- Jstl表达式out、set、if、choose、forEach
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL-core核心代码标签库中的if,set,out等的功能
- EL表达式的相关代码和jstl中的<c:if>、<c:foreach>标签
- java 中运用自定义标签仿照jstl的 ForEach() - 代码共享
- JSTL标签用法:<c:choose><c:forEach><c:if>
- JSP的<c:choose> <c:forEach> <c:if> <c:when> <c:set>标签
- <c:set><c:if><c:foreach>标签的应用