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标签的执行流程
目前 只是看了这几个 等到有时间了 再看吧 记录学习的脚步
posted on 2014-09-14 00:12 liangxinzhi 阅读(272) 评论(0) 编辑 收藏 举报