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

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

2014-09-13 19:23 337 查看
记录学习的脚步

本文是对构建自定义的标签有所涉及,对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 标签使用 /article/4964225.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;
}
}


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

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

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

此即是c:out标签的过程 写不动了 吃饭去了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: