自定义标签的详细使用讲解【下】

简介: 自定义标签的详细使用讲解【下】

更多有关自定义标签的详细讲解欢迎观看点击查看详情

 

这里讲解两个实用的案例

先将我们所需要的实体准备好

package Tag;
/**
 * 书的实体类
 * 
 * @author Java方文山
 *
 */
public class Book {
  private int id;
  private String sname;
  public Book() {
    // TODO Auto-generated constructor stub
  }
  public Book(int id, String sname) {
    super();
    this.id = id;
    this.sname = sname;
  }
  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getSname() {
    return sname;
  }
  public void setSname(String sname) {
    this.sname = sname;
  }
  @Override
  public String toString() {
    return "Book [id=" + id + ", sname=" + sname + "]";
  }
}
package Tag;
/**
 * 用于测试自定义标签foreach
 * 
 * @author Java方文山
 *
 */
public class Student {
  private int sid;
  private String sname;
  public Student() {
    // TODO Auto-generated constructor stub
  }
  public Student(int sid, String sname) {
    super();
    this.sid = sid;
    this.sname = sname;
  }
  public int getSid() {
    return sid;
  }
  public void setSid(int sid) {
    this.sid = sid;
  }
  public String getSname() {
    return sname;
  }
  public void setSname(String sname) {
    this.sname = sname;
  }
  @Override
  public String toString() {
    return "Student [sid=" + sid + ", sname=" + sname + "]";
  }
}
package Tag;
import java.util.ArrayList;
import java.util.List;
/**
 * 初始化Student数据
 * 
 * @author Java方文山
 *
 */
public class TestForeach {
  public static List<Student> AddStudent() {
    List<Student> list = new ArrayList<Student>();
    Student s1 = new Student(1, "xw");
    Student s2 = new Student(2, "wh");
    Student s3 = new Student(3, "sb");
    list.add(s1);
    list.add(s2);
    list.add(s3);
//    for (Student student : list) {
//      System.out.println(student);
//    }
    return list;
  }
  public static void main(String[] args) {
    System.out.println(new TestForeach().AddStudent());
  }
}

一、自定义“数据标签”

标签需求:我们在Web项目开发的时候,有很多页面需要用到同一组数据,尽管保存在域对象里,但jsp页面也还是逃不过foreach遍历的痛苦,那么我们就可以自定义一个“数据标签”,供我们开发人员使用,只需要将从数据库传过来的值保存在“助手类”即可。

看代码更加直观!!

①编写助手类

package Tag;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
 * 数据标签:用于存储数据库传递过来的值
 * @author Java方文山
 *
 */
public class SqlTag extends BodyTagSupport{
  /*
   * 在开发中有很多数据需要在很多页面供大家使用,就可以开发一个“数据标签”,
   * 存放数据,当我们需要在那个页面使用的时候就直接写标签即可。
   */
  //任意名称主要用于EL表达式使用
  private String var;
  public String getVar() {
    return var;
  }
  public void setVar(String var) {
    this.var = var;
  }
  @Override
  public int doStartTag() throws JspException {
    //数据库数据模拟
    List<Student> stu=TestForeach.AddStudent();
    pageContext.setAttribute(var, stu);
    return SKIP_BODY;
  }
}

②编写tld文件

<tag>
    <!-- 标签名 -->
    <name>sqlList</name>
    <!-- 标签助手类 -->
    <tag-class>Tag.SqlTag</tag-class>
    <!-- 标签的内容类型:empty表示空标签,jsp表示可以为任何合法的JSP元素 -->
    <body-content>jsp</body-content>
    <attribute>
      <!-- 属性名, SqlTag类中的val属性相匹配 -->
      <name>var</name>
      <!-- 表示该属性是否为必要的属性 -->
      <required>true</required>
      <!-- 该属性是否可以接受EL表示式的值 -->
      <rtexprvalue>true</rtexprvalue>
      <!-- 标签描述,用于说明标签的作用 -->
      <description>用于存储数据库的数据,便于jsp页面使用</description>
    </attribute>
    </tag>

③页面引入标签

<%@page import="java.util.ArrayList"%>
<%@page import="Tag.Book"%>
<%@page import="Tag.Student"%>
<%@page import="Tag.TestForeach"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@taglib prefix="xw" uri="/xw" %>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>自定义标签</title>
</head>
<body>
<!------------------------------------------------------------------------------------->
<h1>自定义数据标签</h1>
<xw:sqlList var="xw"></xw:sqlList>
${xw }
</body>
</html>
</body>
</html>

浏览器的显示结果

不论在任何jsp页面我们只有引入了自定义标签并且写了这个“数据标签”就可以向浏览器页面输出

二、自定义select标签

标签需求:我们在做某某某管理系统的时候肯定需要通过select标签和option标签,显示对应的数据,例如在新增页面的时候有地址下拉列表选择地址,修改页面的时候就要回显该用户的地址让其选中,这种代码重复率高,我们就可以封装成自定义标签供我们使用。

先看对比

以往的写法

<c:if test="${not empty list}">
<select>
<c:forEach items="${list }" var="s">
<option value="${s.id }">${s.name}</option>
</c:forEach>
</select>
</c:if>

现在我们利用自定义标签封装后

package 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;
/**封装select标签简便我们使用
 * 
 * @author Java方文山
 *
 */
public class SelectTag extends BodyTagSupport{
  //传递过来的数据源(通过助手类解析添加到<option>标签内)
  private List<Object> intems=new ArrayList<Object>();
  //<option>标签的value属性
  private String optionValue;
  //<option>标签的context值
  private String optionContext;
  //数据库的值(主要用于默认选中该值)
  private String selectvar;
  public List<Object> getIntems() {
    return intems;
  }
  public void setIntems(List<Object> intems) {
    this.intems = intems;
  }
  public String getoptionValue() {
    return optionValue;
  }
  public void setoptionValue(String optionValue) {
    this.optionValue = optionValue;
  }
  public String getoptionContext() {
    return optionContext;
  }
  public void setoptionContext(String optionContext) {
    this.optionContext = optionContext;
  }
  public String getselectvar() {
    return selectvar;
  }
  public void setselectvar(String selectvar) {
    this.selectvar = selectvar;
  }
  @Override
  public int doStartTag() throws JspException {
    System.out.println(optionValue+"  "+optionContext);
    //因为自定义的select标签不需要标签体,但是需要向页面输出内容所以需要获取out对象
    JspWriter out = pageContext.getOut();
    try {
      out.print(toHTML());
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return SKIP_BODY;
  }
  /**用于拼接需要向页面输出的内容
   * @return String字符串
   * @throws SecurityException 
   * @throws NoSuchFieldException 
   * @throws IllegalAccessException 
   * @throws IllegalArgumentException 
   */
  private String toHTML() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
    //这里拼接用StringBuffer的好处就是节约内容,提高性能
    StringBuffer strb=new StringBuffer();
    strb.append("<select>");
    //遍历数据源,将内容追加到<option>标签上
    for (Object object : intems) {
      //获取jsp页面的数据源和数据源属性值
      String var=getObjectValue(object,optionValue);
      String text=getObjectValue(object,optionContext);
      //将拿到的属性以及text文本值赋值给option标签,并拿到模拟数据库的值与已有的value值做比对,如果一样就选中
      strb.append("<option "+(var.equals(selectvar) ?"selected" : "")+"  value='"+var+"'>"+text+"</option>");
    }
    strb.append("</select>");
    return strb.toString();
  }
  /**获取数据源的属性值
   * @param object 数据源
   * @param objectvalue 属性值
   * @return
   * @throws SecurityException 
   * @throws NoSuchFieldException 
   * @throws IllegalAccessException 
   * @throws IllegalArgumentException 
   */
  private String getObjectValue(Object object, String objectvalue) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
    /**
     * 因为我们不知道到时候是student还是goods...数据源或者属性存在,
     * 所以我们用反射机制动态实例化并获取数据源的属性值
     */
    //获取类加载器
    Class cl = object.getClass();
    //获取指定的成员变量名
    Field declaredField = cl.getDeclaredField(objectvalue);
    //打开访问权限
    declaredField.setAccessible(true);
    //返回该成员变量的属性值
    return declaredField.get(object).toString();
  }
}
  <tag>
    <!-- 标签名 -->
    <name>Select</name>
    <!-- 标签助手类 -->
    <tag-class>Tag.SelectTag</tag-class>
    <!-- 标签的内容类型:empty表示空标签,jsp表示可以为任何合法的JSP元素 -->
    <body-content>jsp</body-content>
    <attribute>
      <!-- 属性名, SelectTag类中的intems属性相匹配 -->
      <name>intems</name>
      <!-- 表示该属性是否为必要的属性 -->
      <required>true</required>
      <!-- 该属性是否可以接受EL表示式的值 -->
      <rtexprvalue>true</rtexprvalue>
      <!-- 标签描述,用于说明标签的作用 -->
      <description>需要遍历的数据源</description>
    </attribute>
    <attribute>
      <!-- 属性名, SelectTag类中的optionValue属性相匹配 -->
      <name>optionValue</name>
      <!-- 表示该属性是否为必要的属性 -->
      <required>true</required>
      <!-- 该属性是否可以接受EL表示式的值 -->
      <rtexprvalue>true</rtexprvalue>
      <!-- 标签描述,用于说明标签的作用 -->
      <description>option标签的value值</description>
    </attribute>
    <attribute>
      <!-- 属性名, SelectTag类中的optionContext属性相匹配 -->
      <name>optionContext</name>
      <!-- 表示该属性是否为必要的属性 -->
      <required>true</required>
      <!-- 该属性是否可以接受EL表示式的值 -->
      <rtexprvalue>true</rtexprvalue>
      <!-- 标签描述,用于说明标签的作用 -->
      <description>option标签的text值</description>
    </attribute>
    <attribute>
      <!-- 属性名, SelectTag类中的selectedvalue属性相匹配 -->
      <name>selectvar</name>
      <!-- 表示该属性是否为必要的属性 -->
      <required>false</required>
      <!-- 该属性是否可以接受EL表示式的值 -->
      <rtexprvalue>false</rtexprvalue>
      <!-- 标签描述,用于说明标签的作用 -->
      <description>数据库的值需要让已有的下拉列表选中该值</description>
    </attribute>
    </tag>
<%@page import="java.util.ArrayList"%>
<%@page import="Tag.Book"%>
<%@page import="Tag.Student"%>
<%@page import="Tag.TestForeach"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@taglib prefix="xw" uri="/xw" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>自定义标签</title>
</head>
<body>
<h1>自定义select标签</h1>
<!-- 模拟数据 -->
<%
/* 书籍*/
List<Book> listbook=new ArrayList();
listbook.add(new Book(1,"《红楼梦》"));
listbook.add(new Book(2,"《西游记》"));
listbook.add(new Book(3,"《水浒传》"));
listbook.add(new Book(4,"《三国演义》"));
request.setAttribute("listbook", listbook);
/* 学生*/
List<Student> stuu=TestForeach.AddStudent();
request.setAttribute("stuu", stuu);
%>
<xw:Select optionContext="sname" intems="${listbook}" optionValue="id"></xw:Select>
<xw:Select optionContext="sname" intems="${stuu}" optionValue="sid"></xw:Select>
<!-- 默认选中水浒传 -->
<xw:Select optionContext="sname" intems="${listbook}" optionValue="id" selectvar="3"></xw:Select>
</body>
</html>
</body>
</html>

浏览器的显示结果

看到这里可能有些疑惑,这不是代码量比以往的写法多吗?但是我们只需要写一次自定义标签,后期如果需要下拉框都可以通过“传值”的形式就可以达到下拉框显示的效果。而且省略了判断数据库的值与我下拉框的值一致然后selected选中的需求。

三、注意事项

1.助手类的成员遍历名不能以大写开头

如果在自定义标签助手类中的属性名称以大写字母开头,会导致JSP引擎在解析标签时找不到对应的setter方法,因为JavaBean规范要求属性的setter方法名称应该以set开头,后面跟着属性名的驼峰命名。所以,当属性名称以大写字母开头时,JSP引擎无法和标签属性建立起对应关系。

2.利用out对象回显客户端拼接问题

细心的朋友可能观察到了,我的回显拼接不是定义一个String对象然后通过“+=”的方式进行拼接,而是通过StringBuffer对象的append方法进行拼接,至于为什么这样做呢?

String是不可变的,StringBuffer和StringBuilder是可变的。

具体来说,String类是一个不可变的类,它的每一个操作都会产生一个新的String对象。因此,如果需要频繁对字符串进行修改操作,使用String会导致频繁的对象创建和回收,对性能造成影响。而StringBuffer则是可变的字符串缓冲区,它们允许在原有的字符串上修改,而不是每次都创建新的字符串,大大的减少服务器的内存。

后期我会出一篇关于StringBuffer和StringBuilder的区别,关注博主不迷路!!!

♥对这篇文章感兴趣或者有疑问都可以在评论区留言哦♥


相关文章
|
10月前
|
XML Java 数据格式
自定义jsp标签1
自定义jsp标签1
27 0
|
8月前
|
XML Java 程序员
自定义JSP标签
自定义JSP标签
|
8月前
|
Java
自定义标签(下)
自定义标签(下)
|
9月前
|
XML Java 数据格式
自定义标签
自定义标签
|
9月前
|
Java 程序员 开发者
自定义标签-下
自定义标签-下
25 0
|
9月前
|
XML Java API
自定义JSP标签【上】
自定义JSP标签【上】
30 0
|
9月前
|
存储 安全 Java
自定义JSP标签【下】
自定义JSP标签【下】
31 0
|
9月前
|
Java
自定义JSP标签02
自定义JSP标签02
25 0
|
10月前
|
XML 设计模式 Java
自定义标签的详细使用讲解【上】(一)
自定义标签的详细使用讲解【上】
86 0
|
10月前
自定义标签的详细使用讲解【上】(二)
自定义标签的详细使用讲解【上】(二)
24 0