今天我们继续学习JSP自定义
一.面试题:String、StringBuffer和StringBuilder的异同?
相同点:底层都是通过char数组实现的
不同点:
1.String对象一旦创建,其值是不能修改的,如果要修改,会重新开辟内存空间来存储修改之后的对象;而StringBuffer和StringBuilder对象的值是可以被修改的;
2.StringBuffer几乎所有的方法都使用synchronized实现了同步,线程比较安全,在多线程系统中可以保证数据同步,但是效率比较低。
3.而StringBuilder 没有实现同步,线程不安全,在多线程系统中不能使用 StringBuilder,但是效率比较高。
4.总结:如果我们在实际开发过程中需要对字符串进行频繁的修改。不要使用String,否则会造成内存空间的浪费;当需要考虑线程安全的场景下使用 StringBuffer,如果不需要考虑线程安全,追求效率的场景下可以使用 StringBuilder。
二.自定义foreach标签
我们在写jsp页面时会经常使用c标签的foreach标签。那么我们可不可以自己制定一个foreach标签提供自己使用呢?
下面我们来制定一个自定义foreach标签。
我们根据步骤来:
2.1 创建一个标签助手类(继承BodyTagSupport) 标签属性必须与助手类的属性对应、且要提供对应get/set方法
2.2 创建标签库描述文件(tld),添加自定义标签的配置 注:tld文件必须保存到WEB-INF目录或其子目录 jstl标签库
2.3 在JSP通过taglib指令导入标签库,并通过指定后缀访问自定义标签
运行效果:
2.1 创建一个标签助手类 ①(继承BodyTagSupport) ,且要②提供对应get/set方法,③重写doStartTag,doAfterBody,doEndTag方法
分析:定义两个私有的属性:
items;------ 用来遍历的
var;--------用来保存添加的内容
代码如下:
package com.lya.tag; import java.util.Iterator; import java.util.List; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.BodyTagSupport; /** * @author :JAVA-李永安 * @date :2023年6月26日 下午6:44:00 */ public class Foreachtag extends BodyTagSupport { private List items;// 用来遍历的 private String var;// 用来保存添加的内容 //提供对应get/set方法 public List getItems() { return items; } public void setItems(List items) { this.items = items; } public String getVar() { return var; } public void setVar(String var) { this.var = var; } @Override public int doStartTag() throws JspException { if (items == null) {// 对集合判空 return SKIP_BODY;// 为空就跳过 } else { @SuppressWarnings("rawtypes") Iterator iterator = items.iterator();// 迭代遍历 Object next = iterator.next();// 获取遍历内容 pageContext.setAttribute(var, next);// 通过var保存遍历内容 pageContext.setAttribute("iterator", iterator);// 保存未遍历的内容 return EVAL_BODY_INCLUDE; } } @Override public int doAfterBody() throws JspException { // 将未遍历的内容继续遍历出来 @SuppressWarnings("rawtypes") Iterator iterator = (Iterator) pageContext.getAttribute("iterator");// 获取未遍历的内容 if (iterator.hasNext()) {// 对集合判空 Object itp = iterator.next();// 获取遍历内容 pageContext.setAttribute(var, itp);// 通过var保存遍历内容 pageContext.setAttribute("iterator", iterator);// 保存未遍历的内容 return EVAL_BODY_AGAIN; } return SKIP_BODY; } @Override public int doEndTag() throws JspException { // TODO Auto-generated method stub return SKIP_BODY; } }
2.2 创建标签库描述文件(tld)
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <!-- 标签库描述符 --> <taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor"> <!-- 代表标签库的版本号 --> <tlib-version>1.0</tlib-version> <!-- 代表jsp的版本 --> <jsp-version>1.2</jsp-version> <!-- 你的标签库的简称 --> <short-name>test</short-name> <!-- 你标签库的引用uri --> <uri>/zking</uri><!-- 在jsp页面调用tld的路径 --> <tag> <!-- 标签名 --> <name>out</name> <!-- 标签工具类 --> <tag-class>com.lya.tag.outtag</tag-class> <!-- 标签的内容类型:empty表示空标签,jsp表示可以为任何合法的JSP元素 --> <body-content>jsp</body-content> <!-- 自定义标签的属性定义,请注意一定要在标签类中提供对应的get/set方法 --> <attribute> <!-- 自定义标签的属性名称 --> <name>aa</name> <!-- true表示必填 --> <required>true</required> <!-- true支持动态值,可以向值里面填jsp表达式、EL表达式,false则不支持 --> <rtexprvalue>true</rtexprvalue> <description>out标签aa属性,输出aa</description> </attribute> <attribute> <!-- 自定义标签的属性名称 --> <name>bb</name> <!-- true表示必填 --> <required>false</required> <!-- true支持动态值,可以向值里面填jsp表达式、EL表达式,false则不支持 --> <rtexprvalue>true</rtexprvalue> <description>默认输出</description> </attribute> </tag> <tag> <!-- 标签名 --> <name>if</name> <!-- 标签工具类 --> <tag-class>com.lya.tag.iftag</tag-class> <!-- 标签的内容类型:empty表示空标签,jsp表示可以为任何合法的JSP元素 --> <body-content>jsp</body-content> <!-- 自定义标签的属性定义,请注意一定要在标签类中提供对应的get/set方法 --> <attribute> <!-- 自定义标签的属性名称 --> <name>cc</name> <!-- true表示必填 --> <required>true</required> <!-- true支持动态值,可以向值里面填jsp表达式、EL表达式,false则不支持 --> <rtexprvalue>true</rtexprvalue> <description>if标签cc属性,输出cc</description> </attribute> </tag> <tag> <!-- 标签名 --> <name>foreach</name> <!-- 标签工具类 --> <tag-class>com.lya.tag.Foreachtag</tag-class> <!-- 标签的内容类型:empty表示空标签,jsp表示可以为任何合法的JSP元素 --> <body-content>jsp</body-content> <!-- 自定义标签的属性定义,请注意一定要在标签类中提供对应的get/set方法 --> <attribute> <!-- 自定义标签的属性名称 --> <name>var</name> <!-- true表示必填 --> <required>true</required> <!-- true支持动态值,可以向值里面填jsp表达式、EL表达式,false则不支持 --> <rtexprvalue>false</rtexprvalue><!-- var是string类型 --> <description>foreach标签cc属性,输出cc</description> </attribute> <attribute> <!-- 自定义标签的属性名称 --> <name>items</name> <!-- true表示必填 --> <required>true</required> <!-- true支持动态值,可以向值里面填jsp表达式、EL表达式,false则不支持 --> <rtexprvalue>true</rtexprvalue> <description>foreach标签cc属性,输出cc</description> </attribute> </tag> </taglib>
2.3 在JSP通过taglib指令导入标签库,使用自定义标签
<%@page import="java.util.Arrays"%> <%@page import="java.util.ArrayList"%> <%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="z" uri="/zking"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!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> <% request.setAttribute("name", "zs"); List list = new ArrayList<>(); list.add(0, "zs"); list.add(1, "ls"); list.add(2, "ww"); List list2 = Arrays.asList(new String[] { "zs", "ls", "ww" }); request.setAttribute("list", list);//通过request作用域保存list %> <pre> 使用c标签的foreach标签遍历。 </pre> <c:forEach items="${list }" var="a"> ${a }<hr> </c:forEach> <pre> 使用自定义foreach标签遍历。 </pre> <z:foreach items="${list }" var="a"> ${a }<hr> </z:foreach> </body> </html>
三.自定义select标签
3.1为啥我们需要自定义select标签?
1.以前我们使用jsp显示内容是使用servlet传递参数(步骤比较繁琐),只要写两行代码。
以前写的:
使用自定义select标签后写的:
显示的效果:
2.那么我们用一种简洁的代码来完成。
3.提升我们代码的可维护能力。
3.2创建自定义select标签步骤:
3.2.1 创建一个标签助手类 ①(继承BodyTagSupport) ,且要②提供对应get/set方法,③重写doStartTag,doAfterBody,doEndTag方法
分析:1.定义两个私有的属性:
items;---- 用来遍历的
textval;----用来显示下拉的内容
textvalue;----- 传递下拉对应的value
seleteval;------- 传递值,使页面显示对应的内容
2.提供无参构造(因为这个select标签要被作为公共的标签,可是对应的对象我们又不知道,这时候我们得用到反射来实现)
3.重写doStartTag方法
4.使用stringbuffer拼接
代码如下:
package com.lya.tag; import java.io.IOException; import java.lang.reflect.Field; import java.util.Iterator; import java.util.List; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.BodyTagSupport; import com.lya.entity.Dpat; /** * 下拉框的标签助手类 * * @author :JAVA-李永安 * @date :2023年6月26日 下午7:52:46 */ public class SeleteTag extends BodyTagSupport{ private List items;// 用来遍历的 private String textval;// 用来显示下拉的内容 private String textvalue;// 传递下拉对应的value private String seleteval;// 传递值,使页面显示对应的内容 //提供无参构造(因为这个select标签要被作为公共的标签,可是对应的对象我们又不知道,这时候我们得用到反射来实现) //提供对应get/set方法 public List getItems() { return items; } public void setItems(List items) { this.items = items; } public String getTextval() { return textval; } public void setTextval(String textval) { this.textval = textval; } public String getTextvalue() { return textvalue; } public void setTextvalue(String textvalue) { this.textvalue = textvalue; } public String getSeleteval() { return seleteval; } public void setSeleteval(String seleteval) { this.seleteval = seleteval; } @Override public int doStartTag() throws JspException { //1.使用页面输出流 JspWriter out = pageContext.getOut(); //2.定义一个空变量,通过字符串的方式保存向页面显示的内容 try { out.print(html()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return EVAL_BODY_INCLUDE; } private String html() throws Exception { //1.实例stringbuffer,用于去拼接字符串。 StringBuffer sb = new StringBuffer(); sb.append("<select>"); //2.使用反射 (输出的内容取决于items集合的内容) for (Object object : items) { //3.取出对象的属性值 String var = getobjvalue(object,textval);//显示内容 String value = getobjvalue(object,textvalue);//value sb.append("<option "+(value.equals(seleteval) ? "selected" : "")+" value='"+value+"'>"+var+"</option>"); } sb.append("</select>"); return sb.toString();//转成string输出 } /** * 取出对象的属性值 * @param object * @param value * @return * @throws SecurityException * @throws NoSuchFieldException */ private String getobjvalue(Object object, String value) throws Exception { //实例类类 Class class1 = object.getClass(); //取得的对应的属性 Field declaredField = class1.getDeclaredField(value); //获取权限 declaredField.setAccessible(true); return declaredField.get(object).toString(); } @Override public int doEndTag() throws JspException { // TODO Auto-generated method stub return SKIP_BODY; } }
3.2.2 创建标签库描述文件(tld)
将助手类对应的属性进行描述:注意不要copy错误了!!!
代码如下:
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <!-- 标签库描述符 --> <taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor"> <!-- 代表标签库的版本号 --> <tlib-version>1.0</tlib-version> <!-- 代表jsp的版本 --> <jsp-version>1.2</jsp-version> <!-- 你的标签库的简称 --> <short-name>test</short-name> <!-- 你标签库的引用uri --> <uri>/zking</uri><!-- 在jsp页面调用tld的路径 --> <tag> <!-- 标签名 --> <name>select</name> <!-- 标签工具类 --> <tag-class>com.lya.tag.SeleteTag</tag-class> <!-- 标签的内容类型:empty表示空标签,jsp表示可以为任何合法的JSP元素 --> <body-content>jsp</body-content> <!-- 自定义标签的属性定义,请注意一定要在标签类中提供对应的get/set方法 --> <attribute> <!-- 自定义标签的属性名称 --> <name>items</name> <!-- true表示必填 --> <required>true</required> <!-- true支持动态值,可以向值里面填jsp表达式、EL表达式,false则不支持 --> <rtexprvalue>true</rtexprvalue> <description>select标签保存的集合</description> </attribute> <attribute> <!-- 自定义标签的属性名称 --> <name>textval</name> <!-- true表示必填 --> <required>true</required> <!-- true支持动态值,可以向值里面填jsp表达式、EL表达式,false则不支持 --> <rtexprvalue>false</rtexprvalue><!-- var是string类型 --> <description>select标签输出的内容</description> </attribute> <attribute> <!-- 自定义标签的属性名称 --> <name>textvalue</name> <!-- true表示必填 --> <required>true</required> <!-- true支持动态值,可以向值里面填jsp表达式、EL表达式,false则不支持 --> <rtexprvalue>false</rtexprvalue><!-- var是string类型 --> <description>select标签输出的value值</description> </attribute> <attribute> <!-- 自定义标签的属性名称 --> <name>seleteval</name> <!-- true表示必填 --> <required>false</required> <!-- true支持动态值,可以向值里面填jsp表达式、EL表达式,false则不支持 --> <rtexprvalue>false</rtexprvalue><!-- var是string类型 --> <description>select传递值,使页面显示对应的内容</description> </attribute> </tag> </taglib>
3.2.3 在JSP通过taglib指令导入标签库,使用自定义标签
注意不要写标签时不要写错误了,最好通过快捷方式!!!
代码如下:
<%@page import="com.lya.entity.Dpat"%> <%@page import="java.util.Arrays"%> <%@page import="java.util.ArrayList"%> <%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="z" uri="/zking"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!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> <% request.setAttribute("name", "zs"); List list = new ArrayList<>(); list.add(0, "zs"); list.add(1, "ls"); list.add(2, "ww"); List<Dpat> list2 = new ArrayList<Dpat>(); list2.add(new Dpat("0", "学历部")); list2.add(new Dpat("1", "产品部")); list2.add(new Dpat("2", "电竞部")); list2.add(new Dpat("3", "...")); request.setAttribute("list", list);//通过request作用域保存list request.setAttribute("list2", list2);//通过request作用域保存list %> <pre> 使用自定义select标签。 </pre> <select> <z:foreach items="${list }" var="a"> <option>${a }</option> </z:foreach> </select> <hr> <z:select items="${list2 }" textvalue="did" textval="dname"></z:select> <z:select items="${list2 }" textvalue="did" textval="dname" seleteval="1"></z:select> <z:select items="${list2 }" textvalue="did" textval="dname" seleteval="2"></z:select> <z:select items="${list2 }" textvalue="did" textval="dname" seleteval="3"></z:select> </body> </html>
四.总结
我们在写助手类时,1.要牢记自己定义的属性名称,
2.使用StringBuffer进行拼接要小心,注意单引号双引号的转换
3.编写描述类时不要将助手类的名称写错!!!
最后注意逻辑的梳理!!!
希望本篇讲解能帮助到你!!!😀