您的位置:首页 > Web前端 > JavaScript

maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)(二)

2014-09-14 00:12 381 查看
接着上一篇 /article/11572651.html 写了jstl的out标签的实现过程 本文接着看set 、 if 、catch、forEach标签的实现过程

1、set标签 -- 向page/request/session/application中添加值 或者是给javabean的属性赋值 或者是向map中添加key,value键值对

jsp页面

<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标签的执行流程

目前 只是看了这几个 等到有时间了 再看吧 记录学习的脚步
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: