自定义标签

在mvc模式中,我们可以使用别人提供的标签,例如c标签,就是比较常用的,不过,如果别人提供的标签不适用我们的项目开发了,那么我们也可以自己根据不同需求定义标签,也就是自定义标签。

1.标签语言的特点 

 例如a标签:<a>bbb</a>

                <a>就是开始标签

                bbb就是标签体

                </a>就是结束标签

 还有一些不同的,没有标签体或者结束标签,例如<br><hr>

 2.自定义标签的步骤

<body>

	<c:if test="true">
		天气不错
	</c:if>
	
	<f:syso>aaaa</f:syso>

</body>

这个<c:if>是标签库提供的,<f:syso>就是我自己定义的

1.tld文件怎么写

按住Ctrl键点击c:if就会跳转至标签库的tld文件,把他的格式抠出来

 <tag>
    <name>syso</name>
    <tag-class>com.csf.my_jsp_f</tag-class>
    <body-content>JSP</body-content>
 </tag>

 <tag-class>填tag助手类的全类名

自己写的时候只需要保留这几个标签,就可以定义出最简单的页面标签

2.tag助手类

package com.csf;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class my_jsp_f extends BodyTagSupport {

	@Override
	public int doStartTag() throws JspException {
//		return super.doStartTag();//默认返回值,如果是这个,在页面上不会显示标签内容
		return EVAL_BODY_INCLUDE;
	}
	
	@Override
	public int doAfterBody() throws JspException {
		return super.doAfterBody();
	}
	
	@Override
	public int doEndTag() throws JspException {
		return super.doEndTag();
	}
	
}

在类继承了BodyTagSupport类之后,需要重写三个方法,分别对应标签运行到了,开始标签,标签体,结束标签。当然,不同的标签,它们的运行方式不同,所以也就有对应的生命生命周期,这个等下讲。

写完这个,标签就算写完了,就可以在jsp页面使用了。

和标签库一样,需要taglib引入。

<%@ taglib prefix="f" uri="/WEB-INF/f.tld" %>

之后就可以使用了,也就是

<f:syso>aaaa</f:syso>

这种写法。

3.标签的生命周期

 结构图

 

 

<%@page import="com.csf.tag.User"%>
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://jsp.veryedu.cn" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<c:dome1>tttttttttttt</c:dome1>
<c:dome2>tttttt</c:dome2>
<%-- <z:dome3>t</z:dome3> --%>
<c:if test="true">输出</c:if>
<c:if test="false">不输出</c:if>
<hr>
<c:set var="name" value="zs"></c:set>
<c:out value="${name }"></c:out>
<hr>
<%
	List users=new ArrayList<>();
	users.add(new User("u001","zs"));
	users.add(new User("u002","zs1"));
	users.add(new User("u003","zs2"));
	request.setAttribute("users", users);
%>
<%-- <c:forEach items="${users }" var="user">
	${user.id }:${user.name }<br>
</c:forEach> --%>
<br>
<!-- 模拟新增场景 -->
<c:select textVal="name" items="${users }" textKey="id"></c:select>
<!-- //当前学生的教员id外键是教员表中的主键id -->
<!-- 默认显示 -->
模拟修改场景
<c:select selectedVal="u002" textVal="name" items="${users }" textKey="id"></c:select>
模拟查询场景  查询所有
<c:select headerTextKey="" headerTextVal="===请选择===" textVal="name" items="${users }" textKey="id"></c:select>

美化后
<c:select cssStyle="font-size; 26px;color: red;" textVal="name" items="${users }" textKey="id"></c:select>
</body>
</html>

 

 标签写法在下文

 

 4.典型标签

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>c 1.1 core library</description>
  <display-name>c core</display-name>
  <tlib-version>1.1</tlib-version>
  <short-name>c</short-name>
  <uri>http://jsp.veryedu.cn</uri>
  
  <tag>
    <name>dome1</name>
    <tag-class>com.csf.tag.Dome1</tag-class>
    <body-content>JSP</body-content>
  </tag>
  <tag>
    <name>dome2</name>
    <tag-class>com.csf.tag.Dome2</tag-class>
    <body-content>JSP</body-content>
  </tag>
  <tag>
    <name>dome3</name>
    <tag-class>com.csf.tag.Dome3</tag-class>
    <body-content>JSP</body-content>
  </tag>
  
  <tag>
    <name>if</name>
    <tag-class>com.csf.tag.IfTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
    <!-- 自定义标签的成员变量名称 -->
        <name>test</name>
        <!-- 该成员变量是否必传 -->
        <required>true</required>
        <!-- 是否支持EL表达式 -->
        <rtexprvalue>false</rtexprvalue>
    </attribute>
  </tag>
  <tag>
    <name>set</name>
    <tag-class>com.csf.tag.SetTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
    <!-- 自定义标签的成员变量名称 -->
        <name>var</name>
        <!-- 该成员变量是否必传 -->
        <required>true</required>
        <!-- 是否支持EL表达式 -->
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
    <!-- 自定义标签的成员变量名称 -->
        <name>value</name>
        <!-- 该成员变量是否必传 -->
        <required>true</required>
        <!-- 是否支持EL表达式 -->
        <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>
  <tag>
    <name>out</name>
    <tag-class>com.csf.tag.OutTag</tag-class>
    <body-content>JSP</body-content>
    
    <attribute>
    <!-- 自定义标签的成员变量名称 -->
        <name>value</name>
        <!-- 该成员变量是否必传 -->
        <required>true</required>
        <!-- 是否支持EL表达式 -->
        <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>
  <tag>
    <name>forEach</name>
    <tag-class>com.csf.tag.ForEachTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
    <!-- 自定义标签的成员变量名称 -->
        <name>var</name>
        <!-- 该成员变量是否必传 -->
        <required>true</required>
        <!-- 是否支持EL表达式 -->
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
    <!-- 自定义标签的成员变量名称 -->
        <name>items</name>
        <!-- 该成员变量是否必传 -->
        <required>true</required>
        <!-- 是否支持EL表达式 -->
        <rtexprvalue>true</rtexprvalue>
    </attribute>
  </tag>
  <tag>
    <name>select</name>
    <tag-class>com.csf.tag.SelectTag</tag-class>
    <body-content>JSP</body-content>
    <attribute>
        <name>id</name>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <name>cssStyle</name>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <name>className</name>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <name>items</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
    <attribute>
        <name>textKey</name>
        <required>true</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <name>textVal</name>
        <required>true</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <name>headerTextKey</name>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <name>headerTextVal</name>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
        <name>selectedVal</name>
        <required>false</required>
        <rtexprvalue>false</rtexprvalue>
    </attribute>
  </tag>

</taglib>

 (所有标签配置,全在)

1.set/out

 set标签

package com.csf.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
 * 案例3和案例4
 * 开发一个数据标签
 * 1.第一条以及路线
 * 只不过set out标签本身没有标签体,需要在页面上输出内容,需要借助一个类JspWriter
 * 2.在没有标签体的情况下是通过JspWriter来输出内容
 * @author T440s
 *
 */
public class SetTag extends BodyTagSupport{
	//存放标签的键
	private String var;
	//存放标签对应的值
	private Object value;
	public String getVar() {
		return var;
	}
	public void setVar(String var) {
		this.var = var;
	}
	public Object getValue() {
		return value;
	}
	public void setValue(Object value) {
		this.value = value;
	}
	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		//将value值保存到var对应的变量中
		//jsp传递name给var  假如传递zs给value那么需要把zs赋值给name
		//四大作用域:pageContext request session application
		pageContext.setAttribute(var, value);
		return super.doStartTag();
	}
	

}

 out标签

package com.csf.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class OutTag extends BodyTagSupport{
	private Object value;

	public Object getValue() {
		return value;
	}

	public void setValue(Object value) {
		this.value = value;
	}
	
	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		JspWriter out = pageContext.getOut();
		try {
			out.print(value);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		return super.doStartTag();
	}

}

2.foreach

package com.csf.tag;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
 * 案例五 foreach
 * 熟悉第二第三路线的流程
 * 2.doStartTag---->EVAL_BODY_INCLUDE---->doAfterBody----->EVAL_PAGE----->doEndtag
 * 3.doStartTag---->EVAL_BODY_INCLUDE (---->doAfterBody----->EVAL_BODY_AGAIN)可能会执行多次 ----->doEndtag
 * 		将取集合元素的过程看成指针下移取值的过程,如果指针还会指向下一个那么返回值为EVAL_BODY_AGAIN,如果指针没有下一个元素那么返回值为EVAL_PAGE
 * 		指针是迭代器的东西,所以我们要将迭代器保存并提供到doAfterBody中使用
 * 
 * 实现思路
 * 1:最少接受两个参数 var/items
 * 2:一定要有标签体的,那么对应需要重写doAfterBody方法
 * 3:必定有判断条件决定doAfterBody的返回值是EVAL_PAGE还是EVAL_BODY_AGAIN
 * @author T440s
 *
 */
public class ForeachTag extends BodyTagSupport{
	private String var;
	private List<Object> items=new ArrayList<Object>();
	

	public String getVar() {
		return var;
	}


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


	public List<Object> getItems() {
		return items;
	}


	public void setItems(List<Object> items) {
		this.items = items;
	}


	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		//在此处保存迭代器,供doAfterBody中使用
		Iterator<Object> it = items.iterator();
		pageContext.setAttribute("it", it);
		return EVAL_BODY_INCLUDE;
	}
	@Override
	public int doAfterBody() throws JspException {
		// TODO Auto-generated method stub
		Iterator<Object> it=(Iterator<Object>) pageContext.getAttribute("it");
		if(it.hasNext()) {
			//在页面上需要通过var将集合中的元素值拿到
			//it.next()让指针下移
			pageContext.setAttribute(var, it.next());
			//保存指针下移的状态
			pageContext.setAttribute("it", it);
			//还有下一个就继续循环
			return EVAL_BODY_AGAIN;
		}else {
			//没有就结束循环
			return EVAL_PAGE;
		}
	}

}

3.if  控制标签

package com.csf.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;

/**
 * 针对第一条第二条路线做一个实际应用
 * 作用:开发一个控制标签
 * 
 * c:if test=true  是要输出标签体的     要输出第二条路线
 * c:if test=true  是不要输出标签体的     要输出第一条路线
 * 
 * 1.doStartTag---->skipBody---->doEndtag
 * 2.doStartTag---->EVAL_BODY_INCLUDE---->doAfterBody----->EVAL_PAGE----->doEndtag
 * @author T440s
 *
 */
public class IfTag extends BodyTagSupport{
	private boolean test;
	
	
	public boolean isTest() {
		return test;
	}


	public void setTest(boolean test) {
		this.test = test;
	}


	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		//需要一个变量来控制返回值,从而控制走第一个路线还是第二个路线
		return test ? EVAL_BODY_INCLUDE :SKIP_BODY;
	}

}

4.select 

比较难,因为要根据自己的要求实现

package com.csf.tag;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;

import org.apache.commons.beanutils.PropertyUtils;
/**
 * 目的:将所学的自定义标签的知识点用于实际项目开发
 * 不管是if/set/out/foreach标签那是别人已经具备的功能,直接用别人的就行
 * 学习自定义标签理解其底层结构,就是弥补现成C标签没有的功能
 * 
 * 调堂的最终体现形式
 *  以前
 *		查询下拉框
 *		<select>
 *			<option value=''>-==请选择==m</option>
 *			<option value= '1'>晓哥</option>
 *			<option checked value='2'>胡哥</option>
 *			<option value='3'>娜姐</option>
 *		</ select>
 *		修改回显,在这里面有大量的c:foreach. c:if到断
 *		不足之处:代码过大。以及凡是涉及到下拉框以及复选框。粗类似的代码过多
 *	目前:
 *		<z:selectc/z: select>
 *		目的:通过上述标签能够实现上述代码相同的功能
 *	分析属性:
 * 		1.数据源属懂items,用于遍历展示的  users->List<User>->id=option>value;name=option>text
 * 		2.对象key属性textKey,用于对应option>value
 * 		3.对象value属性textval。用于对应option>text
 *		4.对象默认key属性headerTextKey,用于对应默认的option>value
 *		5.对象默认value属性headerTextval,用于对应默认的option>text
 *		6.对象回显值属性selectedVal,用于判断是否数据回显选中
 *	没有标签体又需要往页面输出内容
 * @author T440s
 *
 */
public class SelectTag extends BodyTagSupport{
	private List<Object> items=new ArrayList<Object>();//用于遍历展示的
	private String textKey;//用于对应option>value
	private String textVal;//用于对应option>text
	private String headerTextKey;//用于对应默认的option>value
	private String headerTextVal;//用于对应默认的option>text
	private String selectedVal;//用于判断是否数据回显选中
	
	//定义属性美化、拓展/操作标签
	private String cssStyle;//美化
	private String id;//绑定事件
	private String className;//美化
	
	public String getCssStyle() {
		return cssStyle;
	}

	public void setCssStyle(String cssStyle) {
		this.cssStyle = cssStyle;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getClassName() {
		return className;
	}

	public void setClassName(String className) {
		this.className = className;
	}

	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		JspWriter out = pageContext.getOut();
		try {
			out.print(toHTML());
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		return super.doStartTag();
	}
	
	private String toHTML() throws Exception{
		// TODO Auto-generated method stub
		StringBuffer sb=new StringBuffer();
		sb.append("<select id='"+id+"' class='"+className+"' style='"+cssStyle+"'>");
		//拼接默认显示标签
		if(headerTextVal!=null&&!"".equals(headerTextVal)) {
			sb.append("<option value='"+headerTextKey+"'>"+headerTextVal+"</option>");
		}
		//循环显示数据源
		if(items.size()>0) {
			for (Object obj : items) {
				//obj对应的user
				//希望拿到当前user的id放入option中的value,name放入option中的text
				//<option value= '1'>晓哥</option>
				//通过反射获取id对应的属性对象
				Field textKeyField = obj.getClass().getDeclaredField(textKey);
				textKeyField.setAccessible(true);
				//获取id对应的值
				//textKeyField.get(obj);
				//此代码等于上面三行代码
//				PropertyUtils.getProperty(obj, textVal);
				String value = textKeyField.get(obj).toString();
				
				//修改页面下拉框回显选中
				//当下拉框的value值等于selectedVal,那么就要默认下拉框选中
				if(value.equals(selectedVal)) {
					sb.append("<option selected value= '"+value+"'>"+PropertyUtils.getProperty(obj, textVal)+"</option>");
				}else {
					sb.append("<option value= '"+value+"'>"+PropertyUtils.getProperty(obj, textVal)+"</option>");
				}
			}
		}
		sb.append("</select>");
		return sb.toString();
	}

//	<select>
//	<option value=''>-==请选择==m</option>
//	<option value= '1'>晓哥</option>
//	<option checked value='2'>胡哥</option>
//	<option value='3'>娜姐</option>
//	</ select>
	
	
	public List<Object> getItems() {
		return items;
	}
	public void setItems(List<Object> items) {
		this.items = items;
	}
	public String getTextKey() {
		return textKey;
	}
	public void setTextKey(String textKey) {
		this.textKey = textKey;
	}
	public String getTextVal() {
		return textVal;
	}
	public void setTextVal(String textVal) {
		this.textVal = textVal;
	}
	public String getHeaderTextKey() {
		return headerTextKey;
	}
	public void setHeaderTextKey(String headerTextKey) {
		this.headerTextKey = headerTextKey;
	}
	public String getHeaderTextVal() {
		return headerTextVal;
	}
	public void setHeaderTextVal(String headerTextVal) {
		this.headerTextVal = headerTextVal;
	}
	public String getSelectedVal() {
		return selectedVal;
	}
	public void setSelectedVal(String selectedVal) {
		this.selectedVal = selectedVal;
	}
	
}

ok自定义标签的目的是为了根据不同的需求开发更简单适用的标签,典型中的select标签例子就是。


版权声明:本文为weixin_55966394原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。