JSP自定义标签【第二章】

书接上回:我们讲过了自定义test标签、自定义foreach标签、自定义数据标签。这一章节我讲讲自定义out/if标签(so easy)、自定义下拉框标签(满足所有应用场景)

案例:(最后统一配置mytag.tld文件)

1、实践操作 out标签

首先,创建一个out标签类OutTag.java并继承BodyTagSupport(标签助手类),定义一个out私有属性value,为Object类型。

package com.zking.jspTag.tag;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class OutTag extends BodyTagSupport{
	private Object value;
	
	@Override
	public int doStartTag() throws JspException {
		JspWriter out = pageContext.getOut();
		try {
			out.write(value.toString());
		} catch (IOException e) {
			//异常信息
			e.printStackTrace();
		}
		return SKIP_BODY;
	}
	
	@Override
	public int doEndTag() throws JspException {
		return EVAL_PAGE;
	}
	
	public Object getValue() {
		return value;
	}

	public void setValue(Object value) {
		this.value = value;
	}

}

JspWriter out = pageContext.getOut();  // 拿到page上下文然后进行输出,需要抛异常

JSP页面的out对象类型为JspWriter,相当于一种带缓存的PrintWriter,可以在page指令里设置缓存的大小甚至关闭它的缓存。 

作用域(小->大):page(当前页面)-> request -> session ->application


2、if标签 

 if标签非常简单且基础,操作步骤同out标签类似,定义一个if私有属性test,为boolean类型。

package com.zking.jspTag.tag;

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

public class ifTag extends BodyTagSupport{
	private boolean test;
	
	@Override
	public int doStartTag() throws JspException {
		if(test) {//如果条件成立打印标签体
			return EVAL_BODY_INCLUDE;
		}else {
			return SKIP_BODY;
		}
	}
	
	@Override
	public int doEndTag() throws JspException {
		return super.doEndTag();
	}

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

}

若满足条件就返回 EVAL_BODY_INCLUDE(计算主体内容并包含在输出中);不满足则返回SKIP_BODY(跳过主体内容不输出)

不过呢,它还有另一种写法,代码量极少,那就是使用三元运算符进行判断,一起看看(只展示重点部分)...

条件成立执行 EVAL_BODY_INCLUDE,不成立就执行SKIP_BODY

    @Override
	public int doStartTag() throws JspException {
		return test?EVAL_BODY_INCLUDE:SKIP_BODY;
	}

 3、自定义下拉框标签

要想做 满足所有应用场景的下拉框,那我们得先来了解了解 下拉框

一种是在<select>标签下的<option>标签中直接定义值,俗称“静态的数据”

另一种是在<option>标签中动态绑值,我们主要讲讲如何给辛苦了动态绑值,实现通用。

<select>
    <option value="1">管理员</option>
</select>

1、创建实体类Role.java 提供roleId/roleName两个字段,给上get/set方法

2、自定义下拉框标签需要用到自定义数据标签内来接收数值

3、创建下拉框标签类并继承BodyTagSupport(标签助手类),提供3个字段

4、代码都有详细的解释,可多多理解

    //被遍历的集合或者数组List<Role>  ->Dept[roleId,roleName]
	private List items;
	//用于指定绑定到option标签中的value属性的值,值可以理解为循环遍历对象中的属性名
	//例如:optionValue="roleId"
	private String optionValue;
	//用于指定绑定到option标签中的标签体的值,值可以理解为循环遍历对象中的属性名
	//例如:optionText="roleName"
	private String optionText;

package com.zking.jspTag.tag;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class SelectTag extends BodyTagSupport{
	//被遍历的集合或者数组List
	private List items;
	//用于指定绑定到option标签中的value属性的值,值可以理解为循环遍历对象中的属性名
	private String optionValue;
	//用于指定绑定到option标签中的标签体的值,值可以理解为循环遍历对象中的属性名
	private String optionText;
	
	@Override
	public int doStartTag() throws JspException {
		try {
			JspWriter out = pageContext.getOut();
			//编写逻辑
			out.write(toSelect());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return SKIP_BODY;
	}
	
	private String toSelect() throws IllegalAccessException, Exception {
		StringBuffer sb = new StringBuffer();
		//拼接<select>
		sb.append("<select>");
		//循环遍历Items,用于循环生成option标签
		//obj就是Dept
		for (Object obj : items) {
			//question:如何从obj中取出对应的属性的值,由optionValue和optionText决定
			String value=this.getObjValue(obj, this.optionValue);
			String text=this.getObjValue(obj, this.optionText);
			
			//拼接option标签
			sb.append("<option value='"+value+"'>"+text+"</option>");
		}
		//拼接</select>
		sb.append("</select>");
		//返回
		return sb.toString();
	}
	
	private String getObjValue(Object obj,String fileName) throws Exception, IllegalAccessException {
		//一切与反射相关的代码都从获取类对象开始
		String value=null;
		//获取类对象
		Class cls = obj.getClass();
		//获取对象的属性数组
		Field[] fields = cls.getDeclaredFields();
		//循环遍历数组
		for (Field field : fields) {
			//将对象中的属性名与传入fileName进行对比;如果相同,则获取数据;不相同,则不获取
			if(field.getName().toUpperCase().equals(fileName.toUpperCase())) {
				//设置访问权限
				field.setAccessible(true);
				//获取数据
				value=field.get(obj).toString();
			}
		}
		return value;
	}
	
	@Override
	public int doEndTag() throws JspException {
		return EVAL_PAGE;
	}
	
	public SelectTag() {
		super();
	}
	public SelectTag(String optionValue, String optionText) {
		super();
		this.optionValue = optionValue;
		this.optionText = optionText;
	}
	public SelectTag(List items, String optionValue, String optionText) {
		super();
		this.items = items;
		this.optionValue = optionValue;
		this.optionText = optionText;
	}
	public List getItems() {
		return items;
	}
	public void setItems(List items) {
		this.items = items;
	}
	public String getOptionValue() {
		return optionValue;
	}
	public void setOptionValue(String optionValue) {
		this.optionValue = optionValue;
	}
	public String getOptionText() {
		return optionText;
	}
	public void setOptionText(String optionText) {
		this.optionText = optionText;
	}
}

RoleTag.java        (自定义标签类)

package com.zking.jspTag.tag;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import com.zking.jspTag.util.Dept;
import com.zking.jspTag.util.Role;

public class DeptTag extends BodyTagSupport{
	//将查询出来的数据保存到指定作用域中,并以var属性命名
	private String var;
	//可以通过该参数指明你所要存储的作用域(page/request/session/application)默认page
	private String scope;//作用域
	
	@Override
	public int doStartTag() throws JspException {
		//本案例的静态数据填充
		List<Role> list = new ArrayList<>();
		list.add(new Role(1,"管理员"));
		list.add(new Role(2,"普通人员"));
		list.add(new Role(3,"高级人员"));
		
		//获取Request对象
		HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
		//获取Session对象
		HttpSession session = pageContext.getSession();
		//获取Application对象
		ServletContext application = pageContext.getServletContext();
		
		//判断scope
		if(null==scope) {
			pageContext.setAttribute(var, list);
		}else if(scope.equals("request")) {
			request.setAttribute(var, list);
		}else if(scope.equals("session")) {
			session.setAttribute(var, list);
		}else if(scope.equals("application")) {
			application.setAttribute(var, list);
		}else {
			pageContext.setAttribute(var, list);
		}
		return SKIP_BODY;
	}
	
	@Override
	public int doEndTag() throws JspException {
		return EVAL_PAGE;
	}
	
	public String getVar() {
		return var;
	}
	public void setVar(String var) {
		this.var = var;
	}
	public String getScope() {
		return scope;
	}
	public void setScope(String scope) {
		this.scope = scope;
	}	
}

 index.jsp

引用 <%@taglib uri="/zking" prefix="z" %>

<%@page import="java.util.Arrays"%>
<%@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/core" prefix="c" %>
<%@taglib uri="/zking" prefix="z" %>
<!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>
<%
	List<String> list = Arrays.asList(new String[]{"sum","xy","time"});
	//作用域(小->大):page -> request -> session ->application
	request.setAttribute("list", list);
	request.setAttribute("name", "lxy");
%>

<h2>1、自定义out标签</h2>
<!-- 空标签也行value="${name }"既接收纯文本也能接收动态值 -->
<z:out value="life"></z:out>

<h2>2、自定义if标签</h2>
<!-- test="${name eq 'lxy' }判断是否是正确的,是就输出;反之亦然 -->
<!-- false(不输出): 111()/zzz  -->
<z:if test="true">
	人生处处都是戏,戏里处处看人生。
</z:if>

<h2>3、自定义数据标签</h2>
<z:dept var="data" scope="request"/>
${requestScope.data}

<h2>4、自定义下拉框标签</h2>
<z:select items="${data }" optionValue="roleId" optionText="roleName"/>

</body>
</html>

效果图: 

总结:

    1、在有标签体的情况下,默认会调用助手类的doStartTag、doAfterBody、doEndTag方法;
    2、若开始标签doStartTag返回值是SKIP_BODY,则标签体doAfterBody就不会调用执行;
    3、若将开始标签doStartTag的返回值改为EVAL_BODY_INCLUDE,则标签体doAfterBody会继续执行;

   4、doAfterBody()方法本身就会循环,若返回值先执行SKIP_BODY(break)再执行EVAL_BODY_AGAIN(continue),则会进入死循环。

doStartTag():表示<开始标签>所对应执行的动作

         SKIP_BODY:跳过主体内容不输出

         EVAL_BODY_INCLUDE:计算主体内容并包含在输出中

doAfterBody():介于<开始标签>标签体与<结束标签>之间执行的动作

        SKIP_BODY:跳过主体内容不输出

        EVAL_BODY_AGAIN:再次计算主体内容并包含在输出中

 doEndTag():表示<结束标签>所对应执行的动作

        SKIP_PAGE:跳过页面的后续内容

        EVAL_PAGE:计算页面的后续内容

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值