更多有关自定义标签的详细讲解欢迎观看点击查看详情
这里讲解两个实用的案例
先将我们所需要的实体准备好
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的区别,关注博主不迷路!!!
♥对这篇文章感兴趣或者有疑问都可以在评论区留言哦♥