记录学习的脚步
本文是对构建自定义的标签有所涉及,对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; } }
<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.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>
<%@ 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>
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>
<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; } }
最主要的是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没有定义 则使用 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; }
/** * 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); } }
那么接着看
/** * 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); } }
这里还有一个方法 即是
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; } }
可以看到 其实c:out标签 即是把value default 的值 根据escapeXml属性 决定是否进行转义 从而将value/default/标签内的内容
输出到JSPWriter对象中去 即向服务器端添加代码
此即是c:out标签的过程 写不动了 吃饭去了