javaWeb之jstl的自定义标签的底层实现(4种类型标签)

本文详细介绍了在JSP中实现自定义标签的方法,包括带属性、带标签体及有父标签的标签,并展示了如何开发EL自定义函数。通过具体实例,读者可以学习到标签库API的使用,以及如何在JSP页面中有效利用这些自定义组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、带属性的自定义标签的实现

       前提须知:

       标签库 API

 关于SimpleTagSupport类

public class HelloSimpleTag implements SimpleTag
	
	public class SimpleTagSupport implements SimpleTag {
 
    private JspTag parentTag;
    
    
    private JspContext jspContext;
   
    private JspFragment jspBody;    
    
    @Override
    public void doTag() throws JspException, IOException {
        // NOOP by default
    }
    
 
    @Override
    public void setParent( JspTag parent ) {
        this.parentTag = parent;
    }
    
    
    @Override
    public JspTag getParent() {
        return this.parentTag;
    }
 
    @Override
    public void setJspContext( JspContext pc ) {
        this.jspContext = pc;
    }
    
   
    protected JspContext getJspContext() {
        return this.jspContext;
    }
                
  
    @Override
    public void setJspBody( JspFragment jspBody ) {
        this.jspBody = jspBody;
    }
    
   
    protected JspFragment getJspBody() {
        return this.jspBody;
    }

 
    public static final JspTag findAncestorWithClass(
        JspTag from, Class<?> klass) 
    {
        boolean isInterface = false;

        if (from == null || klass == null
                || (!JspTag.class.isAssignableFrom(klass)
                    && !(isInterface = klass.isInterface()))) {
            return null;
        }

        for (;;) {
            JspTag parent = null;
            if( from instanceof SimpleTag ) {
                parent = ((SimpleTag)from).getParent();
            }
            else if( from instanceof Tag ) {
                parent = ((Tag)from).getParent();
            }
            if (parent == null) {
                return null;
            }

            if (parent instanceof TagAdapter) {
                parent = ((TagAdapter) parent).getAdaptee();
            }

            if ((isInterface && klass.isInstance(parent))
                    || klass.isAssignableFrom(parent.getClass())) {
                return parent;
            }

            from = parent;
        }
    }    
}

在第一种实现中只需要知道

1、setJspContext 方法:该方法把代表 JSP 页面的 pageContext 对象传递给标签处理器对象。

2、doTag 方法:该方法用于完成所有的标签逻辑。该方法可以抛出 javax.servlet.jsp.SkipPageException 异常,用于通知 web 容器不再执行 JSP 页面中位于结束标记后面的内容。

 3、只要在tld声明属性名,然后再java代码中实现Set属性名的方法就能够得到该属性值

具体实现如下3个带属性的自定义标签:

kk.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 <!-- 使用 wjc 指令导入标签库描述文件 -->
 <%@taglib prefix="wjc"  uri="http://www.baidu.com/mytag/core"%>
<!DOCTYPE html >
<html>
<head>
<meta  charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- 循环count次输出value值 -->
	<wjc:hello value="${param.name }" count="3"/>  
	
	<!-- 获取num1和num2的最大值 -->
	<wjc:max num1="23" num2="32"/>
	<!-- 将WEB-INF下的a.txt输出再浏览器上 -->
	<wjc:read src="WEB-INF/a.txt"/>
</body>
</html>

tag.tld(必须放在WEB-INF目录下面)

<?xml version="1.0" encoding="UTF-8" ?>

<!-- 指定标签库的相关信息 -->
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
   
  <description>Tag 1.1 core library</description>
  
  <display-name>Tag core</display-name>
   <!-- 指定标签库的版本号  -->
  <tlib-version>1.0</tlib-version>
  <!-- 指定标签库的默认前缀名  -->
  <short-name>wjc</short-name>
  <!-- 指定标签库的唯一uri  -->
  <uri>http://www.baidu.com/mytag/core</uri>

<!-- 描述自定义的  标签 -->
	<tag>
		<!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
		<name>hello</name>
		<!-- 标签所在的全类名 -->
		<tag-class>cn.itcast.tag.HelloSimpleTag</tag-class>
			<!-- 标签体的类型 一共有3种类型 -->
		<body-content>empty</body-content>
		<!-- 描述当前标签的属性 -->
		<attribute>
		<!-- 属性名, 需和标签处理器类的 setter 方法定义的属性相同 -->
			<name>value</name>
			<!-- 该属性是否被必须 
			就是说在jsp中一定包含value属性
		-->
			<required>true</required>
		<!-- rtexprvalue: runtime expression value 
			当前属性是否可以接受运行时表达式的动态值
			${param.name}
			 -->
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>count</name>
			<required>false</required>
			<rtexprvalue>false</rtexprvalue>
		</attribute>
	</tag>
	
	<tag>
		<name>max</name>
		<tag-class>cn.itcast.tag.HelloSimpleTag2</tag-class>
		<body-content>empty</body-content>
		<attribute>
			<name>num1</name>
			<required>true</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<attribute>
			<name>num2</name>
			<required>false</required>
			<rtexprvalue>false</rtexprvalue>
		</attribute>
	</tag>
	
		<tag>
		<name>read</name>
		<tag-class>cn.itcast.tag.HelloSimpleTag3</tag-class>
		<body-content>empty</body-content>
		<attribute>
			<name>src</name>
			<required>true</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		
	</tag>
</taglib>

HelloSimpleTag.java

package cn.itcast.tag;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.SimpleTag;

public class HelloSimpleTag implements SimpleTag {

	private PageContext pageContext;
	private String value;
	private String count;
	
	public void setValue(String value) {
		this.value = value;
	}


	public void setCount(String count) {
		this.count = count;
	}

	//标签体逻辑实际应该编写到该方法中
	@Override
	public void doTag() throws JspException, IOException {
		// TODO Auto-generated method stub
		/*System.out.println("value:"+value+"  count:"+count);
		HttpServletRequest httpServletRequest = (HttpServletRequest)pageContext.getRequest();
		String name = httpServletRequest.getParameter("name");
		try {
			pageContext.getOut().println("hello "+name);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		
		JspWriter out = pageContext.getOut();
		int k = Integer.valueOf(count);
		for(int i = 0; i<k ;i++){
			out.println("hello "+value+"<br/>");
		}
	}

	@Override
	public void setParent(JspTag parent) {
		// TODO Auto-generated method stub
		System.out.println("setParent");
	}

	@Override
	public JspTag getParent() {
		// TODO Auto-generated method stub
		System.out.println("getParent");
		return null;
	}

	//jsp引擎调用,把代表jsp页面的PageContext对象传入
	//PageContext可以获取jsp页面的其他八个隐含对象
	//所以凡是jsp页面可以做的,标签处理器都可以完成
	@Override
	public void setJspContext(JspContext pc) {//pc是属于pageContext
		// TODO Auto-generated method stub
		pageContext = (PageContext)pc;
		
	}

	@Override
	public void setJspBody(JspFragment jspBody) {
		// TODO Auto-generated method stub
		System.out.println("setJspBody");
	}

}

HelloSimpleTag2.java

package cn.itcast.tag;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.JspTag;
import javax.servlet.jsp.tagext.SimpleTag;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class HelloSimpleTag2 extends SimpleTagSupport {

	private PageContext pageContext;
	private String num1;
	private String num2;
	
	public void setNum1(String num1) {
		this.num1 = num1;
	}
	public void setNum2(String num2) {
		this.num2 = num2;
	}

	//标签体逻辑实际应该编写到该方法中
	@Override
	public void doTag() throws JspException, IOException {
		// TODO Auto-generated method stub
		/*System.out.println("value:"+value+"  count:"+count);
		HttpServletRequest httpServletRequest = (HttpServletRequest)pageContext.getRequest();
		String name = httpServletRequest.getParameter("name");
		try {
			pageContext.getOut().println("hello "+name);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		pageContext = (PageContext) getJspContext();
		JspWriter out = pageContext.getOut();
		int k1 = Integer.valueOf(num1);
		int k2 = Integer.valueOf(num2);
		
		out.print((k1>=k2)?k1:k2);
	}

	

}

HelloSimpleTag3.java

package cn.itcast.tag;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class HelloSimpleTag3 extends SimpleTagSupport {

	private PageContext pageContext;
	private String src;
	private BufferedReader reader;


	public void setSrc(String src) {
		this.src = src;
	}



	//标签体逻辑实际应该编写到该方法中
	@Override
	public void doTag() throws JspException, IOException {
		pageContext = (PageContext)getJspContext();
		
		InputStream in = pageContext.getServletContext().getResourceAsStream(src);
		System.out.println(src);
		reader = new BufferedReader(new InputStreamReader(in));
		JspWriter out = pageContext.getOut();
		
		String line = null;
		while((line = reader.readLine())!=null){
			/*line = Pattern.compile("<").matcher(line).replaceAll("&lt");
			line = Pattern.compile(">").matcher(line).replaceAll("&gt");*/
			
			line = line.replaceAll("<", "&lt");
			line = line.replaceAll(">", "&gt");
			out.println(line+"<br/>");
		}
	}

	

}

二、带标签体的自定义标签的实现

     前提须知

1)若一个标签有标签体
	<wjc:test time="10">hello lhh</wjc:test>
在自定义标签的标签处理器中使用JspFragment对象封装标签体信息。
2)若设置了标签含有标签体,则jsp引擎会调用setJspBody( JspFragment jspBody )方法把JspFragment传递给标签处理器。
再SimpleTagSupport中还定义了一个protected JspFragment getJspBody()方法,用于返回JspFragment对象。
3)JspFragment的invoke(Writer)方法:把标签体内容从Writer中输出,若为null,
则等同于invoke(getJspContext().getOut()),即直接把标签体内容输出到页面上。
有时可以借助StringWriter,可以再标签处理器类中先得到标签体的内容。

//JspFragment的invoke:Writer即为标签体内容输出的字符流,若为Null,则
//输出到getJspContext().getOut():即输出到页面上。
StringWriter sw = new StringWriter();
bodyContext.invoke(sw);
//把标签体的内容变成大写的
System.out.println(sw.toString().toUpperCase());
4)在tld文件中,使用body-content节点描述标签体类型:
<body-content>:指定标签体的类型。可能取值有 3 种:
empty:没有标签体	
scriptless:标签体可以包含 el 表达式和 JSP 动作元素,但不能包含 JSP 的脚本元素(<%=request.getAttribute("name")%>)
tagdependent:表示标签体交由标签本身去解析处理。若指定 tagdependent,
在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器

<body-content>scriptless</body-content>

具体实现如下()功能:

<%@page import="cn.itcast.bean.UserBean"%>
<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="wjc" uri="http://www.baidu.com/mytag2/core" %>
<!DOCTYPE html >
<html>
<head>
<meta  charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<wjc:test time="10">hello lhh</wjc:test>
	
	<%
		List<UserBean> list = new ArrayList<UserBean>();
	
		list.add(new UserBean(1,"a","a"));
		list.add(new UserBean(2,"b","b"));
		list.add(new UserBean(3,"c","c"));
		list.add(new UserBean(4,"d","d"));
		
		request.setAttribute("customer", list);
	%>
	<!-- 其实在标签体中的cust是放在page的属性中 类似于java中的pageContext.setAttribute("cust",customer类对象)  -->
	<wjc:foreach items="${requestScope.customer }" var="cust">
		${pageScope.cust.uid } -- ${cust.uname } <br/>
	</wjc:foreach>
	
</body>
</html>

输出结果

1 -- a 
2 -- b 
3 -- c 
4 -- d 

maytag2.tld

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
    
  <description>Tag 1.1 core library</description>
  <display-name>Tag core</display-name>
  <tlib-version>1.0</tlib-version>
  <short-name>wjc</short-name>
  <uri>http://www.baidu.com/mytag2/core</uri>
  
  	
  	
  	<tag>
  		<name>foreach</name>
  		<tag-class>cn.itcast.tag.TestJspFragmentx</tag-class>
  		<body-content>scriptless</body-content>
  		
  		<attribute>
  			<name>items</name>
  			<required>true</required>
  			<rtexprvalue>true</rtexprvalue>
  		</attribute>
  		
  		<attribute>
  			<name>var</name>
  			<required>false</required>
  			<rtexprvalue>false</rtexprvalue>
  		</attribute>
  	</tag>
  	
  
  </taglib>

TestJspFragmentx.java

package cn.itcast.tag;

import java.io.IOException;
import java.util.Collection;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;


public class TestJspFragmentx extends SimpleTagSupport {

	private Collection<?> items;
	private String var;
	


	public void setItems(Collection<?> items) {
		this.items = items;
	}


	public String getVar() {
		return var;
	}


	public void setVar(String var) {
		this.var = var;
	}


	@Override
	public void doTag() throws JspException, IOException {
		// TODO Auto-generated method stub
		System.out.println(items);
		for(Object ll:items){
			getJspContext().setAttribute(var, ll);
           //获取标签体
			JspFragment bodyContext = getJspBody();
            //实现表签体内的功能
			bodyContext.invoke(null);
		}
	}
	
	
	
	
}

三、开发有父标签的标签

    前提须知

1).父标签无法获取子标签的引用,父标签仅把子标签作为标签体来使用
2).子标签可以通过getParent()方法获取父标签的引用(需继承SimpleTagSupport或自实现SimpleTag接口的方法)
若子标签的确有父标签,jsp引擎会把代表父标签的引用通过setParent( JspTag parent )赋给标签处理器。
3).注意:父标签的类型是JspTag类型,该接口是一个空接口,但是来统一Tag和SimpleTag的,实际使用需要进行类型的强制转换
4).在tld配置文件中,无需为父标签有额外的配置。但子标签是以标签体的形式存在的,所以父标签要
<body-content>scriptless</body-content>

具体实现如下功能

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="http://www.baidu.com/mytag2/core"  prefix="wjc"%>
<!DOCTYPE html >
<html>
<head>
<meta  charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<%
	request.setAttribute("age", 21);
	%>
	
	<wjc:choose>
		<wjc:when test="${requestScope.age>24 }">大学毕业</wjc:when>
		<wjc:when test="${requestScope.age>20 }">高中毕业</wjc:when>
		<wjc:otherwise>高中以下....</wjc:otherwise>
	</wjc:choose>
</body>
</html>
	
		>开发3个标签:choose、when、otherwise
		>其中when标签有一个boolean类型的属性:test
		>choose是when和otherwise的父标签
		>when在otherwise之前使用
		
	> 在父标签 choose 中定义一个 "全局" 的 boolean 类型的 flag: 用于判断子标签在满足条件的情况下是否执行. 
	
		* 若 when 的 test 为 true, 且 when 的父标签的 flag 也为 true, 则执行 when 的标签体(正常输出标签体的内容), 
		     同时把 flag 设置为 false
		* 若 when 的 test 为 true, 且 when 的父标签的 flag 为 false, 则不执行标签体. 
		* 若 flag 为 true, otherwise 执行标签体. 

输出结果

高中毕业 

mytag3.tld

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
    
  <description>Tag 1.1 core library</description>
  <display-name>Tag core</display-name>
  <tlib-version>1.0</tlib-version>
  <short-name>wjc</short-name>
  <uri>http://www.baidu.com/mytag2/core</uri>
  
  	
  	
  	<tag>
  		<name>choose</name>
  		<tag-class>cn.itcast.tag.ChooseTag</tag-class>
  		<body-content>scriptless</body-content>
  	</tag>
  	<tag>
  		<name>when</name>
  		<tag-class>cn.itcast.tag.WhenTag</tag-class>
  		<body-content>scriptless</body-content>
  		
  		<attribute>
  			<name>test</name>
  			<required>true</required>
  			<rtexprvalue>true</rtexprvalue>
  		</attribute>
  	</tag>
  		<tag>
  		<name>otherwise</name>
  		<tag-class>cn.itcast.tag.OtherWise</tag-class>
  		<body-content>scriptless</body-content>
  	</tag>
  	
  

  </taglib>

ChooseTag.java

package cn.itcast.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class ChooseTag extends SimpleTagSupport {

	boolean flag = true;	
	@Override
	public void doTag() throws JspException, IOException {
		// TODO Auto-generated method stub
		JspFragment js = getJspBody();
		js.invoke(null);
	}
	
	
}

WhenTag.java

package cn.itcast.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class WhenTag extends SimpleTagSupport {
	
	boolean test;
	
	
	public void setTest(boolean test) {
		this.test = test;
	}


	@Override
	public void doTag() throws JspException, IOException {
		// TODO Auto-generated method stub
		ChooseTag ctag = (ChooseTag)getParent();
		boolean flag = ctag.flag;
		if(test && flag){
			JspFragment js = getJspBody();
			js.invoke(null);
			ctag.flag = false;
		}
	}
}

OtherWise.java

package cn.itcast.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class OtherWise extends SimpleTagSupport {

	@Override
	public void doTag() throws JspException, IOException {
		// TODO Auto-generated method stub
		ChooseTag ctag = (ChooseTag)getParent();
		boolean flag = ctag.flag;
		if(flag){
			JspFragment js = getJspBody();
			js.invoke(null);
			
		}
	}
}

四、EL 自定义函数的实现

这边其实只要看一下实现就行。前提在前面3个类型标签中已经讲过了。

具体实现

<%@page import="java.util.ArrayList"%>
<%@page import="cn.itcast.bean.UserBean"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@taglib uri="http://java.sun.com/jsp/jstl/functions"  prefix="fn"%>
<%@taglib uri="http://www.baidu.com/mytag2/core"  prefix="wjc"%>
<!DOCTYPE html >
<html>
<head>
<meta  charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	
<!-- http://localhost:8080/SimpaleCode/test2.jsp?name=234erdsfg -->
	${fn:length(param.name) }<!-- 9 -->
	<br/>
	<!-- el自定义函数 -->
	<!-- 字符串的拼接 -->
	${wjc:concat(param.name,param.name1) }
	<br/>
	<!-- 字符串的截取 -->
	${wjc:substr("asdsdf",3,2) }
</body>
</html>

url地址栏为

http://localhost:8080/SimpaleCode/test2.jsp?name=234erdsfg

输出结果

9 
234erdsfg 
sd 

mytag4.tld

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
    
  <description>Tag 1.1 core library</description>
  <display-name>Tag core</display-name>
  <tlib-version>1.0</tlib-version>
  <short-name>wjc</short-name>
  <uri>http://www.baidu.com/mytag2/core</uri>

  	
  	<!-- 描述el的自定函数 -->
  	<function>
  		<name>concat</name>
  		<function-class>cn.itcast.tag.MyFunction</function-class>
  		<function-signature>java.lang.String concat(java.lang.String ,java.lang.String )</function-signature>
  	</function>
  	
  	<!-- 描述el的自定函数 -->
  	<function>
  		<name>substr</name>
  		<function-class>cn.itcast.tag.MyFunction</function-class>
  		<function-signature>java.lang.String substr(java.lang.String ,java.lang.Integer, java.lang.Integer)</function-signature>
  	</function>
  </taglib>

MyFunction.java

package cn.itcast.tag;

public class MyFunction {
	
	public static String concat(String s1,String s2){
		return s1+s2;
	}
	
	public static String substr(String s,Integer begin,Integer length){
		return s.substring(begin, begin+length);
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值