记录学习的脚步
本文是对构建自定义的标签有所涉及,对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>
显示效果
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的话 即 输出输入的文本 不然则进行转义 其实这里的转义 是为了 输入字符串是什么 就输出什么字符串 因为当输入 < > ' " 之类的特殊字符时 浏览器会进行转义 为< >
那么接着看
/**
* 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;
}
}
可以看到 其实c:out标签 即是把value default 的值 根据escapeXml属性 决定是否进行转义 从而将value/default/标签内的内容
输出到JSPWriter对象中去 即向服务器端添加代码
此即是c:out标签的过程 写不动了 吃饭去了