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

记录学习的脚步 


    本文是对构建自定义的标签有所涉及,对jstl标签库中的out、set、if、forEach、catch标签的原理进行相应的浅析

还是先贴参考文献

apache tablib 官网  http://tomcat.apache.org/taglibs/site/tutorial.html

tablib 源码check out  http://tomcat.apache.org/taglibs/site/building.html

jstl 标签使用    http://www.cnblogs.com/lihuiyy/archive/2012/02/24/2366806.html

遇到问题   el表达式不识别  http://blog.csdn.net/zskcy/article/details/2090263


1、还是先来看看自定义的标签 ,主要有四个步骤(四部曲) 

   a:编写TLD(标签库描述的xml文件)    hello_tag.tld  放在/web-inf/tld 目录下

    

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib
            PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" 
            "http://java.sun.com/j2ee/dtds/web-jsptaglib_1_1.dtd">
 <taglib>
 	<tlibversion>1.0</tlibversion>
 	<jspversion>2.1</jspversion>
 	<shortname>hello</shortname>
 	<uri>/hellotags</uri>
 	<info>自定义的hello标签</info>
 	
 	 <tag>
      <name>Hello</name>
      <tagclass>com.undergrowth.HelloTagHandler</tagclass>
      <bodycontent>empty</bodycontent>
      <info>
        Print Hello World
      </info>
    </tag>
    
    <tag>
    	<name>if</name>
    	<!-- 用于处理tag标签的java类 -->
    	<tagclass>com.undergrowth.IfTagHandler</tagclass>
    	<info>条件标签</info>
    	<attribute>
    		<name>judge</name>
    		<required>true</required>
    		<rtexprvalue>true</rtexprvalue>
    	</attribute>
    </tag>
    
 </taglib>          
 

上面是有一个标签库  有两个标签分别为Hello和if  if标签带有judge属性 


  b.编写标签处理类 HelloTagHandler和IfTagHandler  分别如下

   按照官网所说  标签处理类 主要的作用  就是与jsp页面进行交互并向服务器端添加额外的代码

package com.undergrowth;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

/**
 * 继承TagSupport类 用于处理标签
 * @author Administrator
 *
 */
public class HelloTagHandler extends TagSupport{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public int doEndTag() throws JspException {
		// TODO Auto-generated method stub
		return super.doEndTag();
	}

	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		try {
			//获取页面的out对象  输出内容 
			pageContext.getOut().print("hello world,第一个标签,开始");
		} catch (Exception e) {
			throw new JspException("io exception");
		}
		//表示不对标签内容进行解析
		return SKIP_BODY;
	}

	@Override
	public void release() {
		// TODO Auto-generated method stub
		//System.out.println("释放资源");
	}
	
}


package com.undergrowth;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class IfTagHandler extends BodyTagSupport {

	/**
	 * if自定义标签测试 
	 * 根据if标签的属性值确定是否计算标签内的表达式
	 */
	private static final long serialVersionUID = 1L;
    
	private boolean judge ;
	
	public boolean getJudge() {
		return judge;
	}

	public void setJudge(boolean judge) {
		this.judge = judge;
	}

	/**
	 * 显示标签内容到jsp页面中
	 */
	@Override
	public int doAfterBody() throws JspException {
		// TODO Auto-generated method stub
		try {
			//pageContext.getOut().append("我是if标签");
			//将标签中的内容输出到页面中
			bodyContent.writeOut(bodyContent.getEnclosingWriter());
			return SKIP_BODY;
		} catch (Exception e) {
			// TODO: handle exception
			throw new JspTagException(e.toString());
		}
	}
    
	/**
	 * 根据属性的值 来判断是否对标签中的内容进行求值显示
	 */
	@SuppressWarnings("deprecation")
	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		if(getJudge()) return EVAL_BODY_TAG;
		else return SKIP_BODY;
	}
	
	
	
}

  c:在web.xml中 指出你所定义的标签的位置

    

<taglib>
       <taglib-uri>
         /hellotags
       </taglib-uri>
       <taglib-location>
         /WEB-INF/tld/hello_tag.tld
       </taglib-location>
  </taglib>

  d:在jsp页面中进行引用  

   

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<h2>Hello World!</h2>
<h3>自定义标签测试</h3>
<hello:if judge="<%= 5==5 %>">
	<hello:Hello/>
</hello:if>

附pom和下面要用到的jsp页面代码

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.undergrowth</groupId>
  <artifactId>customjstl</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>customjstl Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <!-- 添加jsp依赖 -->
    <dependency>  
     <groupId>org.eclipse.jetty</groupId>  
     <artifactId>jetty-jsp</artifactId>  
     <version>9.2.0.M0</version>  
     <scope>provided</scope>  
    </dependency> 
    <!-- 添加servlet依赖 -->
    <dependency>  
     <groupId>org.eclipse.jetty</groupId>  
     <artifactId>jetty-servlet</artifactId>  
     <version>9.2.0.M0</version>  
     <scope>provided</scope>  
    </dependency> 
    <!-- 添加jstl依赖 -->
    <dependency>
    	<groupId>jstl</groupId>
    	<artifactId>jstl</artifactId>
    	<version>1.2</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>customjstl</finalName>
    <plugins>  
    <plugin>  
        <groupId>org.eclipse.jetty</groupId>  
        <artifactId>jetty-maven-plugin</artifactId>  
    <version>9.2.0.M0</version>  
    </plugin>  
    </plugins> 
  </build>
</project>

测试的index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8" isELIgnored="false" %>    
<%@ taglib uri="/hellotags" prefix="hello" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%    
String path = request.getContextPath();    
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";    
%>    
    
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<body>
<h2>Hello World!</h2>
<h3>自定义标签测试</h3>
<hello:if judge="<%= 5==5 %>">
	<hello:Hello/>
</hello:if>
<hr/>
<h3>jstl标签测试和源码解析</h3>
<c:out value="=========================测试jstl的out标签========================="></c:out>
<br/>
<c:out value="${null}" default="jstl的out的默认值"></c:out>
<br/>
<c:out value="${null}">jstl的out的默认值,标签内部值</c:out>
<br/>
<c:out value="${null}"></c:out>
<br/>
<c:out value="<,>,& 测试转义字符  不进行转义(其实这里的不进行转义 是不让浏览器自动进行转义 保留文本的原始值) 输入字符串是什么 就输出什么字符串" default="jstl的out的默认值" escapeXml="true"></c:out>
<br/>
<c:out value="<,>,& 测试转义字符 进行转义  这里就是让浏览器进行转义(如 < > & \' \" 5个字符)" default="jstl的out的默认值" escapeXml="false"></c:out>
<br/>
<c:out value="=========================测试jstl的out标签========================="></c:out>
<hr/>
<c:out value="=========================测试jstl的if标签========================="></c:out>
<br/>
<c:if test="${5==5 }" var="numJud" scope="page"></c:if>
<c:out value="scope可以为page/request/session/application 结果为:${pageScope.numJud }"></c:out>
<br/>
<c:if test="${5==5 }" var="numJud"></c:if>
<c:out value="和上面带有scope的属性一致   结果为:${pageScope.numJud }"></c:out>
<br/>
<c:out value="=========================测试jstl的if标签========================="></c:out>
<br/>
<hr/>
<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);
 %>
<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/>
<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="under" target="person" property="name"></c:set>
</c:catch> 
<c:out value="将值放入request中 然后取出 显示 ${requestScope.name }"></c:out><br/>
<c:out value="因为没有person对象 也没有setName方法  所以会报错  ${tryInfo2 }" escapeXml="false"></c:out>
<br/>
<c:out value="=========================测试jstl的catch和set标签========================="></c:out>
</body>
</html>

显示效果

maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)_第1张图片





2、上面即有jstl的out、set、if、forEach、catch标签的使用  现在来看看这几个标签 具体是如何来进行操作的  

   从在jsp页面里使用 到 tld 的映射 到最终的 标签处理类 是如何完成整个工作的

  在网上下的jstl的jar里面 在meta-inf里面 都会有jstl标签库相应的tld文件  像本文中涉及到的标签 都在c.tld文件中

 a:先来看看 c:out 标签  

     jsp页面

<c:out value="<,>,& 测试转义字符 进行转义  这里就是让浏览器进行转义(如 < > & \' \" 5个字符)" default="jstl的out的默认值" escapeXml="false"></c:out>

  tld文件里面

<tag>
    <description>
        Like <%= ... >, but for expressions.
    </description> 
    <name>out</name>
    <tag-class>org.apache.taglibs.standard.tag.rt.core.OutTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
        <description>
Expression to be evaluated.
        </description>
        <name>value</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
    <attribute>
        <description>
Default value if the resulting value is null.
        </description>
        <name>default</name>
        <required>false</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
    <attribute>
        <description>
Determines whether characters <,>,&,'," in the
resulting string should be converted to their
corresponding character entity codes. Default value is
true.
        </description>
        <name>escapeXml</name>
        <required>false</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>

对于上面的标签 做个详细的解释吧

tag对应于标签库中一个特定的标签 一个特定的动作  

name即是这个动作的唯一标示符

tag-class 是标签的处理类 所有的标签都是实现  javax.servlet.jsp.tagext.Tag接口 

body-content 表示标签内的内容jsp容器应该如何处理 

     默认为jsp 表示jsp容器应该处理标签的内容,可为空

    empty 表示标签的内容必须为空

   tagdependent  表示标签内的内容 标签自己处理  ,也可为空

attribute 属性 也就是这个动作可附带的特征   

    name 属性名 必须的

   required 表示此属性在使用此标签时是否必须出现

   rtexpvalue   表示  runtime expression  在运行的时候 计算此属性的值


  接着看 OutTag.java     (taglib的源码在上面源码地址下载)

/*
 * 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.OutSupport;

/**
 * <p>Tag handler for <out> in JSTL's rtexprvalue library.</p>
 *
 * @author Shawn Bayern
 */

public class OutTag extends OutSupport {

    private Object value;
    private String def;
    private boolean escapeXml = true;

    //*********************************************************************
    // Accessors

    @Override
    public void release() {
        value = null;
        def = null;
        escapeXml = false;
        super.release();
    }

    // for tag attribute

    public void setValue(Object value) {
        this.value = value;
    }

    // for tag attribute

    public void setDefault(String def) {
        this.def = def;
    }

    // for tag attribute

    public void setEscapeXml(boolean escapeXml) {
        this.escapeXml = escapeXml;
    }

    @Override
    protected Object evalValue() {
        return value;
    }

    @Override
    protected String evalDefault() {
        return def;
    }

    @Override
    protected boolean evalEscapeXml() {
        return escapeXml;
    }
}


上面的OutTag里面 仅有value default escapeXml三个属性的set方法    所以看OutSupport.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.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.BodyTagSupport;

import org.apache.taglibs.standard.util.EscapeXML;

/**
 * <p>Support for handlers of the <out> tag, which simply evalutes and
 * prints the result of the expression it's passed.  If the result is
 * null, we print the value of the 'default' attribute's expression or
 * our body (which two are mutually exclusive, although this constraint
 * is enforced outside this handler, in our TagLibraryValidator).</p>
 *
 * @author Shawn Bayern
 */
public abstract class OutSupport extends BodyTagSupport {

    /*
     * (One almost wishes XML and JSP could support "anonymous tags,"
     * given the amount of trouble we had naming this one!)  :-)  - sb
     */

    //*********************************************************************
    // Internal state

    private Object output;

    //*********************************************************************
    // Construction and initialization

    /**
     * Constructs a new handler.  As with TagSupport, subclasses should
     * not provide other constructors and are expected to call the
     * superclass constructor.
     */
    public OutSupport() {
        super();
    }

    // Releases any resources we may have (or inherit)

    @Override
    public void release() {
        output = null;
        super.release();
    }


    //*********************************************************************
    // Tag logic

    @Override
    public int doStartTag() throws JspException {

        this.bodyContent = null;  // clean-up body (just in case container is pooling tag handlers)

        // output value if not null
        output = evalValue();
        if (output != null) {
            return SKIP_BODY;
        }

        // output default if supplied
        output = evalDefault();
        if (output != null) {
            return SKIP_BODY;
        }

        // output body as default
        output = ""; // need to reset as doAfterBody will not be called with an empty tag
        // TODO: to avoid buffering, can we wrap out in a filter that performs escaping and use EVAL_BODY_INCLUDE?
        return EVAL_BODY_BUFFERED;
    }

    /**
     * Evaluates the "value" attribute.
     *
     * @return the actual value of the "value" attribute
     * @throws JspException if there was a problem evaluating the expression
     */
    protected abstract Object evalValue() throws JspException;

    /**
     * Evaluates the "default" attribute.
     *
     * @return the actual value of the "default" attribute
     * @throws JspException if there was a problem evaluating the expression
     */
    protected abstract String evalDefault() throws JspException;

    /**
     * Evaluates the "escapeXml" attribute.
     *
     * @return the actual value of the "escapeXml" attribute
     * @throws JspException if there was a problem evaluating the expression
     */
    protected abstract boolean evalEscapeXml() throws JspException;

    @Override
    public int doAfterBody() throws JspException {
        output = bodyContent.getString().trim();
        return SKIP_BODY;
    }

    @Override
    public int doEndTag() throws JspException {
        try {
            boolean escapeXml = evalEscapeXml();
            EscapeXML.emit(output, escapeXml, pageContext.getOut());
        } catch (IOException e) {
            throw new JspTagException(e);
        } finally {
            output = null;
        }
        return EVAL_PAGE;
    }
}

看到OutSupport为抽象类 

最主要的是doStartTag 和doEndTag两个方法 这两个方法 一个在标签开始时被调用 一个在标签结束时被调用  那么是谁在管理标签的生命周期呢 是谁负责来进行调用呢

这里接着看  BodyTagSupport.java

/*
 * 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;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;

/**
 * A base class for defining tag handlers implementing BodyTag.
 *
 * <p>
 * The BodyTagSupport class implements the BodyTag interface and adds
 * additional convenience methods including getter methods for the
 * bodyContent property and methods to get at the previous out JspWriter.
 *
 * <p>
 * Many tag handlers will extend BodyTagSupport and only redefine a
 * few methods.
 */

public class BodyTagSupport extends TagSupport implements BodyTag {

    /**
     * Default constructor, all subclasses are required to only define
     * a public constructor with the same signature, and to call the
     * superclass constructor.
     *
     * This constructor is called by the code generated by the JSP
     * translator.
     */

    public BodyTagSupport() {
	super();
    }

    /**
     * Default processing of the start tag returning EVAL_BODY_BUFFERED.
     *
     * @return EVAL_BODY_BUFFERED
     * @throws JspException if an error occurred while processing this tag
     * @see BodyTag#doStartTag
     */
 
    public int doStartTag() throws JspException {
        return EVAL_BODY_BUFFERED;
    }


    /**
     * Default processing of the end tag returning EVAL_PAGE.
     *
     * @return EVAL_PAGE
     * @throws JspException if an error occurred while processing this tag
     * @see Tag#doEndTag
     */

    public int doEndTag() throws JspException {
	return super.doEndTag();
    }


    // Actions related to body evaluation

    /**
     * Prepare for evaluation of the body: stash the bodyContent away.
     *
     * @param b the BodyContent
     * @see #doAfterBody
     * @see #doInitBody()
     * @see BodyTag#setBodyContent
     */

    public void setBodyContent(BodyContent b) {
	this.bodyContent = b;
    }


    /**
     * Prepare for evaluation of the body just before the first body evaluation:
     * no action.
     *
     * @throws JspException if an error occurred while processing this tag
     * @see #setBodyContent
     * @see #doAfterBody
     * @see BodyTag#doInitBody
     */

    public void doInitBody() throws JspException {
    }


    /**
     * After the body evaluation: do not reevaluate and continue with the page.
     * By default nothing is done with the bodyContent data (if any).
     *
     * @return SKIP_BODY
     * @throws JspException if an error occurred while processing this tag
     * @see #doInitBody
     * @see BodyTag#doAfterBody
     */

    public int doAfterBody() throws JspException {
 	return SKIP_BODY;
    }


    /**
     * Release state.
     *
     * @see Tag#release
     */

    public void release() {
	bodyContent = null;

	super.release();
    }

    /**
     * Get current bodyContent.
     *
     * @return the body content.
     */
    
    public BodyContent getBodyContent() {
	return bodyContent;
    }


    /**
     * Get surrounding out JspWriter.
     *
     * @return the enclosing JspWriter, from the bodyContent.
     */

    public JspWriter getPreviousOut() {
	return bodyContent.getEnclosingWriter();
    }

    // protected fields

    /**
     * The current BodyContent for this BodyTag.
     */
    protected BodyContent   bodyContent;
}

几个方法和一个属性 

doStartTag在标签开始时调用

doEndTag在标签结束时调用

doInitBody在标签内容被设置 计算标签内容之前调用

doAfterBody在标签内容被计算完后 调用

bodyContext属性 获取到内容上下文


其实 你追着  BodyTagSupport往下看  会发现 实现了  BodyTag  IterationTag 直至最终的Tag接口  

Tag接口 比较重要 它既是负责整个标签的生命周期管理  

/*
 * 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;

import javax.servlet.jsp.*;


/**
 * The interface of a classic tag handler that does not want to manipulate 
 * its body.  The Tag interface defines the basic protocol between a Tag 
 * handler and JSP page implementation class.  It defines the life cycle 
 * and the methods to be invoked at start and end tag.
 *
 * <p><B>Properties</B></p>
 *
 * <p>The Tag interface specifies the setter and getter methods for the core
 * pageContext and parent properties.</p>
 *
 * <p>The JSP page implementation object invokes setPageContext and
 * setParent, in that order, before invoking doStartTag() or doEndTag().</p>
 *
 * <p><B>Methods</B></p>
 *
 * <p>There are two main actions: doStartTag and doEndTag.  Once all
 * appropriate properties have been initialized, the doStartTag and
 * doEndTag methods can be invoked on the tag handler.  Between these
 * invocations, the tag handler is assumed to hold a state that must
 * be preserved.  After the doEndTag invocation, the tag handler is
 * available for further invocations (and it is expected to have
 * retained its properties).</p>
 *
 * <p><B>Lifecycle</B></p>
 *
 * <p>Lifecycle details are described by the transition diagram below,
 * with the following comments:
 * <ul>
 * <li> [1] This transition is intended to be for releasing long-term data.
 * no guarantees are assumed on whether any properties have been retained
 * or not.
 * <li> [2] This transition happens if and only if the tag ends normally
 * without raising an exception
 * <li> [3] Some setters may be called again before a tag handler is 
 * reused.  For instance, <code>setParent()</code> is called if it's 
 * reused within the same page but at a different level, 
 * <code>setPageContext()</code> is called if it's used in another page, 
 * and attribute setters are called if the values differ or are expressed 
 * as request-time attribute values.
 * <li> Check the TryCatchFinally interface for additional details related
 * to exception handling and resource management.
 * </ul></p>
 *
 * <IMG src="doc-files/TagProtocol.gif"
 *      alt="Lifecycle Details Transition Diagram for Tag"/>
 * 
 * <p>Once all invocations on the tag handler
 * are completed, the release method is invoked on it.  Once a release
 * method is invoked <em>all</em> properties, including parent and
 * pageContext, are assumed to have been reset to an unspecified value.
 * The page compiler guarantees that release() will be invoked on the Tag
 * handler before the handler is released to the GC.</p>
 *
 * <p><B>Empty and Non-Empty Action</B></p>
 * <p>If the TagLibraryDescriptor file indicates that the action must
 * always have an empty action, by an <body-content> entry of "empty",
 * then the doStartTag() method must return SKIP_BODY.</p>
 *
 * <p>Otherwise, the doStartTag() method may return SKIP_BODY or
 * EVAL_BODY_INCLUDE.</p>
 *
 * <p>If SKIP_BODY is returned the body, if present, is not evaluated.</p>
 * 
 * <p>If EVAL_BODY_INCLUDE is returned, the body is evaluated and
 * "passed through" to the current out.</p>
*/

public interface Tag extends JspTag {

    /**
     * Skip body evaluation.
     * Valid return value for doStartTag and doAfterBody.
     */
 
    public final static int SKIP_BODY = 0;
 
    /**
     * Evaluate body into existing out stream.
     * Valid return value for doStartTag.
     */
 
    public final static int EVAL_BODY_INCLUDE = 1;

    /**
     * Skip the rest of the page.
     * Valid return value for doEndTag.
     */

    public final static int SKIP_PAGE = 5;

    /**
     * Continue evaluating the page.
     * Valid return value for doEndTag().
     */

    public final static int EVAL_PAGE = 6;

    // Setters for Tag handler data


    /**
     * Set the current page context.
     * This method is invoked by the JSP page implementation object
     * prior to doStartTag().
     * <p>
     * This value is *not* reset by doEndTag() and must be explicitly reset
     * by a page implementation if it changes between calls to doStartTag().
     *
     * @param pc The page context for this tag handler.
     */

    void setPageContext(PageContext pc);


    /**
     * Set the parent (closest enclosing tag handler) of this tag handler.
     * Invoked by the JSP page implementation object prior to doStartTag().
     * <p>
     * This value is *not* reset by doEndTag() and must be explicitly reset
     * by a page implementation.
     *
     * @param t The parent tag, or null.
     */


    void setParent(Tag t);


    /**
     * Get the parent (closest enclosing tag handler) for this tag handler.
     *
     * <p>
     * The getParent() method can be used to navigate the nested tag
     * handler structure at runtime for cooperation among custom actions;
     * for example, the findAncestorWithClass() method in TagSupport
     * provides a convenient way of doing this.
     *
     * <p>
     * The current version of the specification only provides one formal
     * way of indicating the observable type of a tag handler: its
     * tag handler implementation class, described in the tag-class
     * subelement of the tag element.  This is extended in an
     * informal manner by allowing the tag library author to
     * indicate in the description subelement an observable type.
     * The type should be a subtype of the tag handler implementation
     * class or void.
     * This addititional constraint can be exploited by a
     * specialized container that knows about that specific tag library,
     * as in the case of the JSP standard tag library.
     *
     * @return the current parent, or null if none.
     * @see TagSupport#findAncestorWithClass
     */

    Tag getParent();


    // Actions for basic start/end processing.


    /**
     * Process the start tag for this instance.
     * This method is invoked by the JSP page implementation object.
     *
     * <p>
     * The doStartTag method assumes that the properties pageContext and
     * parent have been set. It also assumes that any properties exposed as
     * attributes have been set too.  When this method is invoked, the body
     * has not yet been evaluated.
     *
     * <p>
     * This method returns Tag.EVAL_BODY_INCLUDE or
     * BodyTag.EVAL_BODY_BUFFERED to indicate
     * that the body of the action should be evaluated or SKIP_BODY to
     * indicate otherwise.
     *
     * <p>
     * When a Tag returns EVAL_BODY_INCLUDE the result of evaluating
     * the body (if any) is included into the current "out" JspWriter as it
     * happens and then doEndTag() is invoked.
     *
     * <p>
     * BodyTag.EVAL_BODY_BUFFERED is only valid  if the tag handler
     * implements BodyTag.
     *
     * <p>
     * The JSP container will resynchronize the values of any AT_BEGIN and
     * NESTED variables (defined by the associated TagExtraInfo or TLD)
     * after the invocation of doStartTag(), except for a tag handler
     * implementing BodyTag whose doStartTag() method returns
     * BodyTag.EVAL_BODY_BUFFERED.
     *
     * @return EVAL_BODY_INCLUDE if the tag wants to process body, SKIP_BODY 
     *     if it does not want to process it.
     * @throws JspException if an error occurred while processing this tag
     * @see BodyTag
     */
 
    int doStartTag() throws JspException;
 

    /**
     * Process the end tag for this instance.
     * This method is invoked by the JSP page implementation object
     * on all Tag handlers.
     *
     * <p>
     * This method will be called after returning from doStartTag. The
     * body of the action may or may not have been evaluated, depending on
     * the return value of doStartTag.
     *
     * <p>
     * If this method returns EVAL_PAGE, the rest of the page continues
     * to be evaluated.  If this method returns SKIP_PAGE, the rest of
     * the page is not evaluated, the request is completed, and 
     * the doEndTag() methods of enclosing tags are not invoked.  If this
     * request was forwarded or included from another page (or Servlet),
     * only the current page evaluation is stopped.
     *
     * <p>
     * The JSP container will resynchronize the values of any AT_BEGIN and
     * AT_END variables (defined by the associated TagExtraInfo or TLD)
     * after the invocation of doEndTag().
     *
     * @return indication of whether to continue evaluating the JSP page.
     * @throws JspException if an error occurred while processing this tag
     */

    int doEndTag() throws JspException;

    /**
     * Called on a Tag handler to release state.
     * The page compiler guarantees that JSP page implementation
     * objects will invoke this method on all tag handlers,
     * but there may be multiple invocations on doStartTag and doEndTag in between.
     */

    void release();

}


其实BodyTagSupport.java 能够处理标签中的内容 来源于两个接口 

一个是BodyTag 定义了 void doInitBody() throws JspException;  方法

一个是TagSupport  类实现IterationTag 提供了 int doAfterBody() throws JspException;  方法

               而IterationTag即是forEach标签能够实现的重要组成部分 因为   public final static int EVAL_BODY_AGAIN = 2;  属性


还是回到OutSupport中 来看   

我们在上面知道了是Tag接口负责管理标签的生命周期  在标签开始时调用doStartTag  在标签结束时调用doEndTag  那么我们

 @Override
    public int doStartTag() throws JspException {

        this.bodyContent = null;  // clean-up body (just in case container is pooling tag handlers)

        // output value if not null
        output = evalValue();
        if (output != null) {
            return SKIP_BODY;
        }

        // output default if supplied
        output = evalDefault();
        if (output != null) {
            return SKIP_BODY;
        }

        // output body as default
        output = ""; // need to reset as doAfterBody will not be called with an empty tag
        // TODO: to avoid buffering, can we wrap out in a filter that performs escaping and use EVAL_BODY_INCLUDE?
        return EVAL_BODY_BUFFERED;
    }

先是获取在页面上定义的value属性的值  如果不为空  则略过标签内容的计算

    如果value没有定义 则使用 default属性的值 如果不为空  则略过标签内容的计算

         如果value和default属性都没有定义 没关系 定义一个标签内容缓存获取标签内的值

在上面的doStartTag方法调用完了后  正常情况下Tag就应该调用doEndTag  但是因为OutSupport间接实现了BodyTag接口 所以应该调用

doInitBody和doAfterBody两个方法

 @Override
    public int doAfterBody() throws JspException {
        output = bodyContent.getString().trim();
        return SKIP_BODY;
    }
 

要注意一点 想要调用doInitBody和doAfterBody方法 还有一个条件   只有当doStartTag的返回值不是EVAL_BODY的时候 才能调用

如上面的 当value和default属性都没设定的时候  就会调用 doAfterBody方法 获取标签内内容作为显示值   最后调用doEndTag 

@Override
    public int doEndTag() throws JspException {
        try {
            boolean escapeXml = evalEscapeXml();
            EscapeXML.emit(output, escapeXml, pageContext.getOut());
        } catch (IOException e) {
            throw new JspTagException(e);
        } finally {
            output = null;
        }
        return EVAL_PAGE;
    }

在这里 只是判断是否应该转义输入的文本    取决于escapeXml属性 也可以看看  

 /**
     * Emit the supplied object to the specified writer, escaping characters if needed.
     *
     * @param src       the object to write
     * @param escapeXml if true, escape unsafe characters before writing
     * @param out       the JspWriter to emit to
     * @throws IOException if there was a problem emitting the content
     */
    public static void emit(Object src, boolean escapeXml, JspWriter out) throws IOException {
        if (src instanceof Reader) {
            emit((Reader) src, escapeXml, out);
        } else {
            emit(String.valueOf(src), escapeXml, out);
        }
    }
文本的话 如下面
 /**
     * Emit the supplied String to the specified writer, escaping characters if needed.
     *
     * @param src       the String to write
     * @param escapeXml if true, escape unsafe characters before writing
     * @param out       the JspWriter to emit to
     * @throws IOException if there was a problem emitting the content
     */
    public static void emit(String src, boolean escapeXml, JspWriter out) throws IOException {
        if (escapeXml) {
            emit(src, out);
        } else {
            out.write(src);
        }
    }


可以看到 如果escapeXml为false的话 即 输出输入的文本 不然则进行转义  其实这里的转义 是为了 输入字符串是什么 就输出什么字符串  因为当输入 &lt;  &gt;  '  "  之类的特殊字符时 浏览器会进行转义  为< > 

那么接着看  

/**
     * Emit escaped content into the specified JSPWriter.
     *
     * @param src the string to escape; must not be null
     * @param out the JspWriter to emit to
     * @throws IOException if there was a problem emitting the content
     */
    public static void emit(String src, JspWriter out) throws IOException {
        int end = src.length();
        int from = 0;
        for (int to = from; to < end; to++) {
            String escape = getEscape(src.charAt(to));
            if (escape != null) {
                if (to != from) {
                    out.write(src, from, to - from);
                }
                out.write(escape);
                from = to + 1;
            }
        }
        if (from != end) {
            out.write(src, from, end - from);
        }
    }

上面的方法  理解from比较关键  from 都是指向有转义字符的下一个字符的位置  (第一次除外)

这里还有一个方法 即是

 static {
        int size = '>' + 1; // '>' is the largest escaped value
        ESCAPES = new String[size];
        ESCAPES['<'] = "<";
        ESCAPES['>'] = ">";
        ESCAPES['&'] = "&";
        ESCAPES['\''] = "'";
        ESCAPES['"'] = """;
    }

    private static String getEscape(char c) {
        if (c < ESCAPES.length) {
            return ESCAPES[c];
        } else {
            return null;
        }
    }


其实上面的数组  构建的是63个字符串的数组 因为>符号的ascii为62  其他的特殊字符的ascii都小于62



可以看到 其实c:out标签 即是把value default 的值  根据escapeXml属性 决定是否进行转义  从而将value/default/标签内的内容  

输出到JSPWriter对象中去 即向服务器端添加代码

 

此即是c:out标签的过程  写不动了 吃饭去了  





你可能感兴趣的:(foreach)