1.自定义jsp标签概念
在jsp开发时,当jsp内置标签以及JSTL标签库都满足不了需求时,可以根据自己的需求来自定义标签。其实在jsp页面使用标签就等同于调用某个对象的某个方法。
2.标签语音特点及类型
- <开始标签 属性="属性值">标签体</结束标签>
- ui标签:input out ....
- 空标签:br hr ......
- 控制标签:if/foreach...
- 数据标签:set/out...
3.标签生命周期
路线1:doStarTag-->SKIP_BODY-->doEndTag
路线2:doStartTag-->EVAL_BODY_INCLUDE-->doAfterBody-->EVAL PAGE-->doEndTag
线路3:doStartTag-->EVAL_BODY_INCLUDE-->doAfterBody-->EVAL_BODY_AGAIN-->doAfterBody...(循环)-->满足条件后doEndTag
4.自定义标签步骤
- 每个标签,必须创建一个标签助手类(继承BodyTagSupport),标签属性必须和助手类的属性对应、且要提供对应get/set方法
- 创建标签库描述文件(tld),添加自定义标签的配置,而且tld文件必须保存到WEB-INF目录或其子目录
- 在JSP通过taglib指令导入标签库,并通过指定后缀访问自定义标签
4.1 论证自定义标签生命周期
tld文件
引入方式:<%@taglib prefix="名" uri="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>JSTL 1.1 core library</description>
<display-name>JSTL core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>c</short-name>
<!-- tld文件路径 -->
<uri>http://zking.sum</uri>
<tag>
<!-- 标签名 -->
<name>Dome1</name>
<!-- 助手类全路径名 -->
<tag-class>com.wyy.Dome1</tag-class>
<body-content>JSP</body-content>
<attribute>
<!-- 标签属性名 -->
<name>var</name>
<!-- 是否是必写属性 -->
<required>false</required>
<!-- 是否可以使用EL表达式 -->
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
<tag>
<name>Dome2</name>
<tag-class>com.wyy.Dome2</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
<tag>
<name>Dome3</name>
<tag-class>com.wyy.Dome3</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
<tag>
<name>set</name>
<tag-class>com.wyy.Dome4</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>out</name>
<tag-class>com.wyy.Dome5</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>if</name>
<tag-class>com.wyy.Dome6</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>text</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>forEach</name>
<tag-class>com.wyy.forEach</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
助手类
路线1验证
package com.wyy;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
* 三种路线
* 1.doStartag........SKIPBODY......doEndtag
* @author T440s
*在jsp页面使用自定义标签,如果打印以下输出则正确
*
*/
public class Dome1 extends BodyTagSupport {
@Override
public int doStartTag() throws JspException {
// TODO Auto-generated method stub
System.out.println("Demo1_doStartTag_进来了");
return SKIP_BODY;
}
@Override
public int doEndTag() throws JspException {
// TODO Auto-generated method stub
System.out.println("Demo1_doEndTag_进来了");
return super.doEndTag();
}
}
路线2验证
package com.wyy;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
* 2.doStartag........EVAL_BOOY_INCLUDE.....doAfterbody........EVAL_PAGE....doEndtag
* @author T440s
*
*/
public class Dome2 extends BodyTagSupport {
@Override
public int doStartTag() throws JspException {
// TODO Auto-generated method stub
System.out.println("Demo2_doStartTag_进来了");
return EVAL_BODY_INCLUDE;
}
@Override
public int doAfterBody() throws JspException {
// TODO Auto-generated method stub
System.out.println("Demo2_doAfterBody_进来了");
return EVAL_PAGE;
}
@Override
public int doEndTag() throws JspException {
// TODO Auto-generated method stub
System.out.println("Demo2_doEndTag_进来了");
return super.doEndTag();
}
}
路线3验证
package com.wyy;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
*3.doStartag........EVAL_BOOY_INCLUDE.....doAfterbody........EVAL_BOYY_AGAIN.....doAfterbody........EVAL_BOYY_AGAINdoAfterbody........EVAL_BOYY_AGAIN.....doEndtag
*
* @author T440s
*
*/
public class Dome3 extends BodyTagSupport {
@Override
public int doStartTag() throws JspException {
// TODO Auto-generated method stub
System.out.println("Demo3_doStartTag_进来了");
return EVAL_BODY_INCLUDE;
}
@Override
public int doAfterBody() throws JspException {
// TODO Auto-generated method stub
System.out.println("Demo3_doAfterBody_进来了");
return EVAL_BODY_AGAIN;
}
@Override
public int doEndTag() throws JspException {
// TODO Auto-generated method stub
System.out.println("Demo3_doEndTag_进来了");
return super.doEndTag();
}
}
5.案列实践
if标签
助手类:
package com.wyy;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
* 开发if标签
* <z:if test="${empty user}">标签体</z:if>
*
* 成员变量:test 必传(使用if标签test属性必传值),支持EL表达式
* 路线分析:
* test=true 执行标签体
* test=false 不执行标签体
* @author T440s
*
*/
public class Dome6 extends BodyTagSupport {
private boolean text;
public boolean isText() {
return text;
}
public void setText(boolean text) {
this.text = text;
}
@Override
public int doStartTag() throws JspException {
// TODO Auto-generated method stub
//需要一个变量控制返回值,达到标签路线控制
return text?EVAL_BODY_INCLUDE:SKIP_BODY;
}
}
在tld文件中进行配置(每个标签都有对应的助手类以及tld配置)
<tag>
<name>if</name>
<tag-class>com.wyy.Dome6</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>text</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
set/out 标签
助手类
package com.wyy;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
* 开发数据标签
* 第一条路线
* set标签 没有标签体 需要借助JSPWriter
* @author T440s
*
*/
public class Dome4 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
//通过pageContext保存
pageContext.setAttribute(var, value);
return SKIP_BODY;
}
@Override
public int doEndTag() throws JspException {
// TODO Auto-generated method stub
return super.doEndTag();
}
}
out
package com.wyy;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
*
* 自定义out标签:数据标签
* 成员变量: value 存放标签对应的值 必传,支持EL表达式
* 注意:无标签体,要在页面上输出内容,需要借助一个类JSPWrite。
* 路线分析:
* 执行doStartTag,将内容输出到网页上
* @author T440s
*
*/
public class Dome5 extends BodyTagSupport{
private Object var ;
public Object getVar() {
return var;
}
public void setVar(Object var) {
this.var = var;
}
@Override
public int doStartTag() throws JspException {
// TODO Auto-generated method stub
JspWriter out = pageContext.getOut();
try {
out.print(var);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return SKIP_BODY;
}
@Override
public int doEndTag() throws JspException {
// TODO Auto-generated method stub
return super.doEndTag();
}
}
tld文件配置
<tag>
<name>set</name>
<tag-class>com.wyy.Dome4</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>out</name>
<tag-class>com.wyy.Dome5</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
foreach标签
助手类
package com.wyy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
* foreach开发
* 第二、第三路径
* 1.最少接受两个参数
* 2.一定有标签体
* 3.必定有条件觉得doafterbody返回值是EVAL_PAGE还是 EVAL_BOYY_AGAIN
* @author T440s
*
*/
public class forEach extends BodyTagSupport{
private String var;
private List<Object> items=new ArrayList<>();
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
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将集合数据拿出
pageContext.setAttribute(var, it.next());
//保存下移状态
pageContext.setAttribute("it", it);
//继续循环
return EVAL_BODY_AGAIN;
}else {
//结束循环
return EVAL_PAGE;
}
}
@Override
public int doEndTag() throws JspException {
// TODO Auto-generated method stub
return super.doEndTag();
}
public class student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "student [name=" + name + ", age=" + age + "]";
}
public student() {
// TODO Auto-generated constructor stub
}
public student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
}
tld文件配置
<tag>
<name>forEach</name>
<tag-class>com.wyy.forEach</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
select标签
在项目开发过程中经常会用到select标签,当你的select标签数量达到十个甚至更多时,发现代码量太多,所有我们现在来开发一个自己的select标签
助手类
package com.wyy;
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;
/**
* 查询下拉框
* 修改回显,在这里有大量的c:foreach/c:if判断
* 不足之处,代码量过大,以及凡是涉及到下拉框以及复选框,相类似代码量过多
* <zking:select></zking:select>通过该标签实现上述代码的相同功能
* 分析属性:
* 1、需要遍历展示,需要数据源属性items,用于遍历展示
* id=option>value;name=option>test
* 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 select 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;//美化
@Override
public int doStartTag() throws JspException {
JspWriter out = pageContext.getOut();
try {
out.print(html());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return super.doStartTag();
}
//<select>
//<option value="">--请选择--</option>
//<option value="1">hh</option>
// <option value="2">jj</option>
// </select>
private String html() throws Exception, Exception {
//获取可变字符串
StringBuffer sb=new StringBuffer();
sb.append("<select>");
//select默认显示
if(headerTextVal!=null&&!"".equals(headerTextVal)) {
sb.append("<option vlaue='"+headerTextKey+"'>"+headerTextVal+"</option>");
}
//循环显示数据
if(items.size()>0) {
for (Object obj : items) {
//obj就是页面传输过来集合的类型或对象
//通过反射获取类对象属性 getDeclaredField()获取所有(公共的、私有的)类对象属性
Field fidname = obj.getClass().getDeclaredField(textKey);
//打开权限 不了解反射的可以看我主页反射的文章
fidname.setAccessible(true);
//获取textKey 对应的属性值
String value = fidname.get(obj).toString();
Field fidtext = obj.getClass().getDeclaredField(textVal);
fidtext.setAccessible(true);
String text = fidtext.get(obj).toString();
//追加至<optoin>中 判断默认选中
if(text.equals(selectedVal)) {
sb.append("<option selected vlaue='"+value+"'>"+text+"</option>");
}else {
sb.append("<option vlaue='"+value+"'>"+text+"</option>");
}
}
}
sb.append("<option></option>");
sb.append("</select>");
return sb.toString();
}
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;
}
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;
}
}
tld文件配置
<tag>
<name>select</name>
<tag-class>com.wyy.select</tag-class>
<body-content>JSP</body-content>
<attribute>
<!-- 自定义标签的成员变量名称-->
<name>id</name>
<!-- 该成员变量是否必传-->
<required>false</required>
<!-- 是否支持EL表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<!-- 自定义标签的成员变量名称-->
<name>cssStyle</name>
<!-- 该成员变量是否必传-->
<required>false</required>
<!-- 是否支持EL表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<!-- 自定义标签的成员变量名称-->
<name>className</name>
<!-- 该成员变量是否必传-->
<required>false</required>
<!-- 是否支持EL表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<!-- 自定义标签的成员变量名称-->
<name>items</name>
<!-- 该成员变量是否必传-->
<required>true</required>
<!-- 是否支持EL表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<!-- 自定义标签的成员变量名称-->
<name>textKey</name>
<!-- 该成员变量是否必传-->
<required>true</required>
<!-- 是否支持EL表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<!-- 自定义标签的成员变量名称-->
<name>textVal</name>
<!-- 该成员变量是否必传-->
<required>true</required>
<!-- 是否支持EL表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<!-- 自定义标签的成员变量名称-->
<name>headerTextKey</name>
<!-- 该成员变量是否必传-->
<required>false</required>
<!-- 是否支持EL表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<!-- 自定义标签的成员变量名称-->
<name>headerTextVal</name>
<!-- 该成员变量是否必传-->
<required>false</required>
<!-- 是否支持EL表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<!-- 自定义标签的成员变量名称-->
<name>selectedVal</name>
<!-- 该成员变量是否必传-->
<required>false</required>
<!-- 是否支持EL表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
测试 index.jsp
<%@page import="com.wyy.student"%>
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://zking.sum" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<c:set var="name" value="张三"></c:set>
<hr>
<c:out var="${name}"></c:out>
<hr>
<c:if text="true">输出</c:if>
<c:if text="false">不输出</c:if>
<hr>
<%
List<student> list=new ArrayList<>();
list.add(new student("张三",12));
list.add(new student("ww",16));
list.add(new student("wyf",72));
list.add(new student("kjh",32));
request.setAttribute("list", list);
%>
<c:forEach items="${list }" var="f">
${f.name} ${f.age}
</c:forEach>
<hr>
<c:select textKey="age" textVal="name" items="${list}" selectedVal="ww"></c:select>
</body>
</html>
运行结果