jsp自定义标签

时间:2023-01-01 19:56:47


jsp自定义标签


    需求: 向浏览器输出当前客户的IP地址 (只能使用jsp标签)



1. 自定义标签开发步骤


       1. 编写一个普通的java类,继承SimpleTagSupport类,叫做 标签处理器类

package com.cn.mytag;
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.SimpleTagSupport;
/**
* 标签处理器类
* @author liuzhiyong
*
*/
/*
* 1.继承SimpleTagSupport类
*/
public class ShowIpTag extends SimpleTagSupport{

/*private JspContext context;


* 传入JspContext(等同于传入的是子类PageContext)

@Override
public void setJspContext(JspContext pc) {
this.context = pc;
}*/

/**
* 以上代码,在SimpleTagSupport类中已经做了,这里不需要重复做。
*/

/*
* 2. 覆盖doTag方法
*/
@Override
public void doTag() throws JspException, IOException {
//获取PageContext
PageContext pageContext = (PageContext)this.getJspContext();
HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();

String ip = request.getRemoteHost();

JspWriter out = pageContext.getOut();
out.write("使用自定义标签输出客户的ip地址:" + ip);


       2.在web项目的WEB-INF目录下建立myTag.tld文件(标签库的声明文件,具体写法,参考核心标签库的tld文件)。


<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">

<!-- 标签库版本 -->
<tlib-version>1.1</tlib-version>
<!-- 标签库前缀 -->
<short-name>myTag</short-name>
<uri>http://com.cn.myTag</uri>
<!-- 一个标签的声明 -->
<tag>
<!-- 标签的名称 -->
<name>showIp</name>
<tag-class>com.cn.mytag.ShowIpTag</tag-class>
<!-- 输出标签体内容的格式 -->
<body-content>scriptless</body-content>
</tag>
</taglib>


       3.在jsp页面的头部导入自定义标签库。使用taglib指令


​​<%@ taglib uri="http://com.cn.myTag" prefix="myTag" %>​​


       4.在jsp中使用自定义标签


​​ <%-- 使用自定义标签库中的标签 --%>​​​​     <myTag:showIp></myTag:showIp>​​


2.自定义标签的执行过程



    问题: ​​http://localhost:8080/jstl/mytag/tag1.jsp​​ 如何访问到自定义标签? 



前提:tomcat服务器启动时,加载到每个web应用,加载每个web应用的WEB-INF目录下的所有文件。例如:web.xml , tld文件。



    1)访问tag1.jsp资源。



    2)tomcat服务器把jsp文件翻译成java源文件->编译class->构造类对象->调用_jspService()方法。



    3)检查jsp文件的taglib指令,是否存在一个uri为http://com.cn.myTag 的tld文件。若果没有则报错。



    4)在上一步已经读到了myTag.tld文件,在读到<myTag:showIp>标签时,会到myTag.tld文件中查询是否存在<name>为showIp的<tag>标签。



    5)找到对应 的<tag>标签,则读到<tag-class>内容。



    6)得到对应的java类com.cn.mytag.ShowIpTag。



    7)构造ShowIpTag对象,然后调用ShowIpTag里面的方法。



3.自定义标签处理器类的生命周期

    SimpleTag接口: 



void  setJspContext(JspContext pc)  --设置pageContext对象,传入pageContext(一定调用)。通过getJspCotext()方法得到pageContext对象



void  setParent(JspTag parent)  --设置父标签对象,传入父标签对象,如果没有父标签,则不调用此方法。通过getParent()方法得到父标签对象。



void     setXXX(值)      --设置属性值。



void  setJspBody(JspFragment jspBody) --设置标签体内容。标签体内容封装到JspFragment对象中,然后传入JspFragment对象。通过getJspBody()方法得到标签体内容。如果没有标签体内容,则不会调用此方法



void  doTag()   --执行标签时调用的方法。(一定调用)



4.自定义标签的作用



1)控制标签体内容是否输出

输出:调用JspFragment.invoke(null);  或者 .invoke(this.getJspContext().getOut());     


不输出:不调用JspFragment.invoke(null);

2)控制标签余下内容是否输出

输出:什么都不干!

           不输出:抛出SkipPageException异常。  

3)控制重复输出标签体内容
4)改变标签体内容        

//创建Writer(StringWriter)临时容器

        StringWriter writer = new StringWriter();

        //把标签体拷贝到临时容器

        body.invoke(writer);

        //从临时容器中得到标签体内容 

        String content = writer.toString() + name;

        //改变内容

        content = content.toLowerCase();

        //把改变的内容手动输出到浏览器

      // body.invoke(null); //不能使用此方法输出,因为jspBody内容没有改变过

5)带属性的标签
步骤:


1.在标签处理器中添加一个成员变量和setter方法,用于给标签属性赋值。


//声明属性的成员变量
private String name;
//关键点:必须提供公开的setter方法,用于给属性赋值
public void setName(String name) {
this.name = name;
}



    2.在tld文件中声明属性




<!-- 声明属性 -->
<attribute>
<!-- 属性名称 -->
<name>name</name>
<!-- 是否必填 -->
<required>true</required>
<!-- 是否支持EL表达式 。false:不支持。true:支持-->
<rtexprvalue>false</rtexprvalue>
</attribute>


5.输出标签体内容格式





 在传统标签中使用的。可以写和执行jsp的java代码。






scriptless:  标签体不可以写jsp的java代码





empty:    必须是空标签。





tagdependent : 标签体内容可以写jsp的java代码,但不会执行。




案例Demo1如下:



    标签处理器类:


package com.cn.mytag;
import java.io.IOException;
import java.io.StringWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* 标签处理器
* @author liuzhiyong
*
*/
/*
* 1.编写一个普通的java类,继承SimpleTagSupport类
*/
public class DemoTag extends SimpleTagSupport {

//声明属性的成员变量
private String name;

//关键点:必须提供公开的setter方法,用于给属性赋值
public void setName(String name) {
this.name = name;
}
/*
* 2.重写doTag()方法
*/
public void doTag() throws JspException, IOException {

System.out.println("DemoTag.doTag()执行了。。。。。。。。");

/**
* 1)控制标签内容是否输出
* 输出:调用JspFragment.invoke();
* 不输出:不调用JspFragment.invoke();
*/
/*
* 1.1得到标签体内容
*/
JspFragment body = this.getJspBody();
/*
* 1.2执行invoke方法,把标签体内容输出到指定的Writer对象中
*/
//往浏览器输出,如果writer为null,,默认就是往浏览器输出。
// body.invoke(this.getJspContext().getOut());
// body.invoke(null);//等价于上面的代码。

/**
* 3)控制重复输出标签体内容
* 方法:执行多次JspBody.invoke()方法
*/
for(int i=0; i<5; i++){
body.invoke(null);
}

/**
* 4)改变标签体内容
*/
//创建Writer(StringWriter)临时容器
StringWriter writer = new StringWriter();
//把标签体拷贝到临时容器
body.invoke(writer);
//从临时容器中得到标签体内容
String content = writer.toString() + name;
//改变内容
content = content.toLowerCase();
//把改变的内容输出到浏览器
// body.invoke(null); //不能使用此方法输出,因为jspBody内容没有改变过
this.getJspContext().getOut().write(content);

/**
* 2)控制标签余下内容是否输出。这种方法可以做权限管理
* 输出:什么都不干!
* 不输出:抛出SkipPageException异常。
*/
// throw new SkipPageException();
}

   myTag.tl文件:


<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">

<!-- 标签库版本 -->
<tlib-version>1.1</tlib-version>
<!-- 标签库前缀 -->
<short-name>m</short-name>
<uri>http://com.cn.myTag</uri>
<!-- 一个标签的声明 -->
<tag>
<!-- 标签的名称 -->
<name>showIp</name>
<tag-class>com.cn.mytag.ShowIpTag</tag-class>
<!-- 输出标签体内容的格式
scriptless:接受文本,EL表达式,静态HTML
-->
<body-content>scriptless</body-content>
</tag>

<tag>
<!-- 标签的名称 -->
<name>demoTag</name>
<tag-class>com.cn.mytag.DemoTag</tag-class>
<!-- 输出标签体内容的格式 -->
<body-content>scriptless</body-content>
<!-- 声明属性 -->
<attribute>
<!-- 属性名称 -->
<name>name</name>
<!-- 是否必填 -->
<required>true</required>
<!-- 是否支持EL表达式 。false:不支持。true:支持-->
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>


  demoTag.jsp测试页面:


<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://com.cn.myTag" prefix="m"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>My JSP 'demoTag.jsp' starting page</title>

<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>

<body>
<%-- 使用自定义的标签 --%>
<%-- <m:demoTag name="我是属性里面的值哦。">标签体内容ABCDEFGghiJK<br/></m:demoTag><hr/> --%>
<%-- <m:demoTag name="我是属性里面的值哦。2222">xxxxXXX${3*11 }<br/></m:demoTag><hr/> --%>
<%-- <m:demoTag name="我是属性里面的值哦。2222"><h6>3333${2*2 }</h6></m:demoTag> --%>
<m:demoTag name="我是属性里面的值哦。2222">这里输出的是什么鬼${3*2 }<br/></m:demoTag>
标签余下内容
</body>
</html>


效果:



 

jsp自定义标签


案例Demo2如下:模仿c:forEach标签


    标签处理类ForEach.java

package com.cn.mytag;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import com.cn.entity.Student;
public class ForEach extends SimpleTagSupport {

private Object items;//需要遍历的数据
private String var;//每个元素的名称

public void setItems(Object items) {
this.items = items;
}
public void setVar(String var) {
this.var = var;
}
@Override
public void doTag() throws JspException, IOException {

PageContext pageContext = (PageContext)this.getJspContext();
/*
//遍历List集合的数据
if(items instanceof List){

List list = (List)items;
for(Object object : list){
//把每个对象防御域对象(pageContext)中
pageContext.setAttribute(var, object);

//显示标签体内容
this.getJspBody().invoke(null);
}
}

//遍历Map集合的数据
if(items instanceof Map){

Map map = (Map)items;
Set<Map.Entry<String, Student>> entrySet = map.entrySet();

for(Map.Entry<String, Student> entry : entrySet){
//把每个对象防御域对象(pageContext)中
pageContext.setAttribute(var, entry);
//显示标签体内容
this.getJspBody().invoke(null);
}
}
*/
/**
* 简化代码
* 思路:
* 1. list ->Collection
* 2. map.entrySet() ->Collection
*/
Collection col = null;
if(items instanceof List){
col = (List)items;
}

if(items instanceof Map){
Map map = (Map)items;
col = map.entrySet();
}

for(Object object : col){
//把每个对象防御域对象(pageContext)中
pageContext.setAttribute(var, object);

//显示标签体内容
this.getJspBody().invoke(null);
}
}


tld文件mytag.tld

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">

<!-- 标签库版本 -->
<tlib-version>1.1</tlib-version>
<!-- 标签库前缀 -->
<short-name>m</short-name>
<uri>http://com.cn.myTag</uri>

<tag>
<name>forEach</name>
<tag-class>com.cn.mytag.ForEach</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>

测试forEach.jsp

<%@ page language="java" import="java.util.*, com.cn.entity.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://com.cn.myTag" prefix="m" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>My JSP 'forEach.jsp' starting page</title>

<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>

<body>
<%
//创建Student对象
Student stu1 = new Student(1, "木丁西", '男', 24);
Student stu2 = new Student(2, "小龙女", '女', 14);
Student stu3 = new Student(3, "张馨予", '女', 28);
Student stu4 = new Student(4, "刘先生", '男', 35);
//创建集合对象
List<Student> list = new ArrayList<Student>();
list.add(stu1);
list.add(stu2);
list.add(stu3);
list.add(stu4);
//保存集合对象到page域对象中。
pageContext.setAttribute("list", list);

//创建Map集合对象
Map<String, Student> map = new HashMap<String, Student>();
//添加数据到 Map结合中
map.put("001", stu1);
map.put("002", stu2);
map.put("003", stu3);
map.put("004", stu4);
//保存集合对象到page域对象中。
pageContext.setAttribute("map", map);
%>

<m:forEach items="${list }" var="stu">
${stu.id }#${stu.name }#${stu.sex }#${stu.age }<br/>
</m:forEach>

<m:forEach items="${map }" var="entry">
${entry.key }=${entry.value.id }#${entry.value.name }#${entry.value.sex }#${entry.value.age }<br/>
</m:forEach>
</body>
</html>

效果:



jsp自定义标签