书接上回:我们讲过了自定义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:计算页面的后续内容