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 目录下
上面是有一个标签库 有两个标签分别为Hello和if if标签带有judge属性
b.编写标签处理类 HelloTagHandler和IfTagHandler 分别如下
按照官网所说 标签处理类 主要的作用 就是与jsp页面进行交互并向服务器端添加额外的代码
c:在web.xml中 指出你所定义的标签的位置
d:在jsp页面中进行引用
附pom和下面要用到的jsp页面代码
pom.xml
测试的index.jsp
显示效果
2、上面即有jstl的out、set、if、forEach、catch标签的使用 现在来看看这几个标签 具体是如何来进行操作的
从在jsp页面里使用 到 tld 的映射 到最终的 标签处理类 是如何完成整个工作的
在网上下的jstl的jar里面 在meta-inf里面 都会有jstl标签库相应的tld文件 像本文中涉及到的标签 都在c.tld文件中
a:先来看看 c:out 标签
jsp页面
tld文件里面
对于上面的标签 做个详细的解释吧
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的源码在上面源码地址下载)
上面的OutTag里面 仅有value default escapeXml三个属性的set方法 所以看OutSupport.java
看到OutSupport为抽象类
最主要的是doStartTag 和doEndTag两个方法 这两个方法 一个在标签开始时被调用 一个在标签结束时被调用 那么是谁在管理标签的生命周期呢 是谁负责来进行调用呢
这里接着看 BodyTagSupport.java
几个方法和一个属性
doStartTag在标签开始时调用
doEndTag在标签结束时调用
doInitBody在标签内容被设置 计算标签内容之前调用
doAfterBody在标签内容被计算完后 调用
bodyContext属性 获取到内容上下文
其实 你追着 BodyTagSupport往下看 会发现 实现了 BodyTag IterationTag 直至最终的Tag接口
Tag接口 比较重要 它既是负责整个标签的生命周期管理
其实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 那么我们
先是获取在页面上定义的value属性的值 如果不为空 则略过标签内容的计算
如果value没有定义 则使用 default属性的值 如果不为空 则略过标签内容的计算
如果value和default属性都没有定义 没关系 定义一个标签内容缓存获取标签内的值
在上面的doStartTag方法调用完了后 正常情况下Tag就应该调用doEndTag 但是因为OutSupport间接实现了BodyTag接口 所以应该调用
doInitBody和doAfterBody两个方法
要注意一点 想要调用doInitBody和doAfterBody方法 还有一个条件 只有当doStartTag的返回值不是EVAL_BODY的时候 才能调用
如上面的 当value和default属性都没设定的时候 就会调用 doAfterBody方法 获取标签内内容作为显示值 最后调用doEndTag
在这里 只是判断是否应该转义输入的文本 取决于escapeXml属性 也可以看看
可以看到 如果escapeXml为false的话 即 输出输入的文本 不然则进行转义 其实这里的转义 是为了 输入字符串是什么 就输出什么字符串 因为当输入 < > ' " 之类的特殊字符时 浏览器会进行转义 为< >
那么接着看
上面的方法 理解from比较关键 from 都是指向有转义字符的下一个字符的位置 (第一次除外)
这里还有一个方法 即是
其实上面的数组 构建的是63个字符串的数组 因为>符号的ascii为62 其他的特殊字符的ascii都小于62
可以看到 其实c:out标签 即是把value default 的值 根据escapeXml属性 决定是否进行转义 从而将value/default/标签内的内容
输出到JSPWriter对象中去 即向服务器端添加代码
此即是c:out标签的过程 写不动了 吃饭去了
本文是对构建自定义的标签有所涉及,对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标签的过程 写不动了 吃饭去了
相关文章推荐
- maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)
- maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)(二)
- maven之构建自定义标签和jstl源码浅析(涉及out、set、if、forEach、catch)(二)
- (forEach url out set catch 等)javaweb中JSTL的Core 核心标签库知识点
- 【转】JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL常用标签的使用:if、forEach、out等标签
- JSTL标签参考手册,JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- Jstl表达式out、set、if、choose、forEach
- Jstl表达式out、set、if、choose、forEach
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- JSTL-core核心代码标签库中的if,set,out等的功能
- EL表达式的相关代码和jstl中的<c:if>、<c:foreach>标签
- java 中运用自定义标签仿照jstl的 ForEach() - 代码共享
- JSTL标签用法:<c:choose><c:forEach><c:if>
- JSP的<c:choose> <c:forEach> <c:if> <c:when> <c:set>标签
- <c:set><c:if><c:foreach>标签的应用