zoukankan      html  css  js  c++  java
  • maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)(二)

       接着上一篇 http://blog.csdn.net/undergrowth/article/details/39253657  写了jstl的out标签的实现过程 本文接着看set 、 if 、catch、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标签的执行流程      




    目前 只是看了这几个 等到有时间了 再看吧   记录学习的脚步   






  • 相关阅读:
    Android ClearEditText:输入用户名、密码错误时整体删除及输入为空时候晃动提示
    Activity界面切换动画特效。
    点击事件的确认取消对话框。
    安卓菜单的实现,各种添加菜单的方法。
    联系人的侧边字母索引ListView 将手机通讯录姓名通过首字母排序。
    获取手机屏幕密度。
    Android统计图表MPAndroidChart.
    性能测试
    自动化框架
    排序算法
  • 原文地址:https://www.cnblogs.com/liangxinzhi/p/4275559.html
Copyright © 2011-2022 走看看