第3章Tag文件与Tag标记 本章导读 主要内容 Tag文件的结构 Tag标记 Tag文件中的常用指令 难点 Tag文件中的attribute指令 Tag文件中的variable指令 关键实践 解析单词 一个Web应用中的许多JSP页面可能需要使用某些相同的信息,如都需要使用相同的导航栏、标题等。如果能将许多页面都需要的共同的信息形成一种特殊文件,而且各个JSP页面都可以使用这种特殊的文件,那么这样的特殊文件就是可复用的代码。代码复用是软件设计的一个重要方面、是衡量软件可维护性的重要指标之一。 第2章学习了include指令标记和include动作标记,使用这两个标记可以实现代码的复用。但是,在某些情况下,使用include指令标记和include动作标记有一定的缺点,比如,如果include指令标记或动作标记要处理的文件是一个JSP文件,那么用户可以在浏览器的地址栏中直接输入该JSP文件所在Web服务目录访问这个JSP文件,这可能不是Web应用所希望发生的,因为该JSP文件也许仅仅是个导航条,仅仅供其他JSP文件使用include指令标记或动作标记来嵌入或动态加载的,而不是让用户直接访问的。另外,include指令标记和include动作标记允许所要处理的文件存放在Web服务目录中的任意子目录中,不仅显得杂乱无章,而且使得include标记和所处理文件的所在目录的结构形成了耦合,不利于Web应用的维护。 本章我们将学习一种特殊的文本文件: Tag文件。Tag文件和JSP文件很类似,可以被JSP页面动态加载调用,实现代码的复用(但用户不能通过该Tag文件所在Web服务目录直接访问Tag文件)。 本章在webapps目录下新建一个Web服务目录ch3,除非特别约定,例子中的JSP页面均保存在ch3目录中。 视频讲解 3.1Tag文件 3.1.1Tag文件的结构 Tag文件是扩展名为.tag的文本文件,其结构和JSP文件类似。一个Tag文件中可以有普通的HTML标记符、某些特殊的指令标记(见3.4节)、成员变量声明和方法的定义、Java程序片和Java表达式。以下是一个简单的Tag文件oddNumberSum.tag,负责计算100内的全部奇数的代数和。 oddNumberSum.tag <%@ tag pageEncoding="utf-8"%> <p style="font-family:宋体;font-size:36"> 1~100内的奇数之和: <%int sum=0,i=1; for(i=1;i<=100;i++){ if(i%2==1) sum=sum+i; } out.println(sum); %> </p> 3.1.2Tag文件的保存 Tag文件所在目录 Tag文件可以实现代码的复用,即Tag文件可以被许多JSP页面使用。为了能让一个Web应用中的JSP页面使用某一个Tag文件,必须把这个Tag文件存放到Tomcat服务器指定的目录中,也就是说,如果某个Web服务目录下的JSP页面准备调用一个Tag文件,那么必须在该Web服务目录下,建立如下的目录结构: Web服务目录\WEB-INF\tags 例如: ch3\WEB-INF\tags 其中的WEBINF(字母大写)和tags都是固定的目录名称,而tags下的子目录的名称可由用户给定。 一个Tag文件必须保存到tags目录或其下的子目录中。这里把3.1.1节中的oddNumberSum.tag保存到 ch3\WEBINF\tags 目录中。 Tag文件的编码 保存Tag文件时按照Tag文件指定的编码保存,例如Tag文件使用tag指令(见稍后的3.4节): <%@ tag pageEncoding="utf-8"%> 指定的编码是UTF8,因此需要按照UTF8编码保存Tag文件。例如,用文本编辑器“记事本”编辑Tag文件,在保存该Tag文件时,将“保存类型(T)”选择为“所有文件(*.*)”,将“编码(E)”选择为“UTF8”。 3.2Tag标记 3.2.1Tag标记与Tag文件 视频讲解 某个Web服务目录下的Tag文件只能由该Web服务目录中的JSP页面调用,JSP页面必须通过Tag标记来调用一个Tag文件。 Tag标记的名字和Tag文件的名字一致,也就是说,当我们编写了一个Tag文件并保存到特定目录中后(见3.1.2节),也就给出了一个Tag标记,该标记的格式为: <Tag文件的名字 /> 或 <Tag 文件的名字 > 其他内容(称为标体内容)</Tag文件的名字> 一个Tag文件对应着一个Tag标记,把全体Tag标记称之为一个自定义标记库或简称为标记库。 3.2.2Tag标记的使用 一个JSP页面通过使用Tag标记来调用一个Tag文件。Web服务目录下的一个JSP页面在使用Tag标记来调用一个Tag文件之前,必须首先使用taglib指令标记引入该Web服务目录下的标记库,只有这样,JSP页面才可以使用Tag标记调用相应的Tag文件。 taglib指令的格式如下: <%@ taglib tagdir="标记库的位置" prefix="前缀"> 例如: <%@ taglib tagdir="/WEB-INF/tags" prefix="computer"%> 引入标记库后,JSP页面就可以使用带前缀的Tag标记调用相应的Tag文件,其中的前缀由<taglib>指令中的prefix属性指定。例如JSP如下使用Tag标记调用相应的Tag文件: <computer:oddNumberSum /> taglib指令中的prefix给出的前缀由用户自定义,其好处是,通过前缀可以有效地区分不同标记库中具有相同名字的标记文件。 注: JSP页面使用Tag标记时,冒号:的左右不要有空格。 例3_1中的JSP页面使用Tag标记调用oddNumberSum.tag(该Tag文件见3.1.1节)计算100之内的奇数和。 例3_1 example3_1.jsp(效果如图3.1所示) <%@ page contentType="text/html"%> <%@ page pageEncoding="utf-8"%> <%@ taglib tagdir="/WEB-INF/tags" prefix="computer"%> <HTML><body bgcolor=cyan> <h1>调用Tag文件计算100内奇数和: </h1> <computer:oddNumberSum /> <%-- 使用Tag标记 --%> </body></HTML> 图3.1使用Tag标记 如果把3.1.1节中的oddNumberSum.tag保存在 ch3\WEBINF\tags\example1 目录中,那么只要将上述JSP页面的taglib指令修改为: <%@ taglib tagdir="/WEB-INF/tags/example1" prefix="computer"%> 即可。 3.2.3Tag标记的运行原理 Tomcat服务器处理JSP页面中的Tag标记的原理如下: 如果该Tag标记对应的Tag文件是首次被JSP页面调用,那么Tomcat服务器会将Tag文件转译成一个Java文件,并编译这个Java文件生成字节码文件,然后执行这个字节码文件(这和执行JSP页面的原理类似)。 如果该Tag文件已经被转编译为字节码文件,Tomcat服务器将直接执行这个字节码文件。 如果对Tag文件进行了修改,那么Tomcat服务器会重新将Tag文件转译成一个Java文件,并编译这个Java文件生成字节码文件,然后执行这个字节码文件。 3.3Tag文件中的常用指令 视频讲解 与JSP文件类似,Tag文件中也有一些常用指令,这些指令将影响Tag文件的行为。Tag文件中经常使用的指令有tag、taglib、include、attribute、variable。 以下将分别讲述上述指令在Tag文件中的作用和用法。 3.3.1tag指令 Tag文件中的tag指令类似于JSP文件中的page指令。Tag文件通过使用tag指令可以指定某些属性的值,以便从总体上影响Tag文件的处理和表示。tag指令的语法如下: <%@ tag属性1="属性值" 属性2="属性值" …属性n="属性值"%> 在一个Tag文件中可以使用多个tag指令,因此我们经常使用多个tag指令为属性指定需要的值: <%@ tag属性1="属性值"%> <%@ tag属性2="属性值"%> … <%@ tag属性n="属性值"%> language属性 language属性的值指定Tag文件使用的脚本语言,目前只能取值Java,其默认值就是Java,因此在编写Tag文件时,没有必要使用tag指令指定language属性的值。 import属性 import属性的作用是为Tag文件引入包中的类,这样就可以在Tag文件的程序片部分、变量及方法定义部分、表达式部分使用包中的类。import属性可以取多个值,import属性默认已经有如下值: "java.lang.*""javax.servlet.*""javax.servlet.jsp.*""javax.servlet.http.*"。 pageEncoding 该属性指定Tag文件的字符编码,其默认值是ISO88591。目前,为了避免显示信息出现乱码现象,Tag文件需要将该属性值设置为UTF8。 3.3.2include指令 在Tag文件中也有和JSP文件类似的include指令标记,其使用方法和作用与JSP文件中的include指令标记类似。 3.3.3attribute指令 Tag文件充当着可复用代码的角色,如果一个Tag文件允许使用它的JSP页面向该Tag文件传递数据,就使得Tag文件的功能更为强大。在Tag文件中通过使用attribute指令让使用它的JSP页面向该Tag文件传递需要的数据。attribute指令的格式如下: <%@ attribute name="对象名字" required="true"|"false" type="对象的类型"%> 例如Tag文件myTag.tag中有如下attribute指令: <%@ attribute name="result" required="true" type="java.lang.Double"%> 那么就相当于Tag文件中有了一个名字是result的对象,但Tag文件不需要创建该对象result,而是等待JSP页面将一个Double型的对象的引用传递给result。 attribute指令中的name属性是必需的,该属性的值是一个对象的名字。JSP页面在调用Tag文件时,可向name属性指定的对象传递一个引用。需要特别注意的是,type在指定对象类型时,必须使用包名,比如,不可以将java.lang.Double简写为Double。如果attribute指令中没有使用type指定对象的类型,那对象的类型默认是java.lang.String类型。 JSP页面使用Tag标记向所调用的Tag文件中name指定的对象传递一个引用,方式如下: <前缀: Tag文件名字 对象名字="对象的引用" /> 比如,JSP页面使用Tag标记(假设标记的前缀为computer)调用myTag.tag: <computer:myTag result="new Double(3.1415926)" /> 就向myTag.tag中attribute指令给出的对象result对象传递了一个Double对象的引用。 attribute指令中的required属性也是可选的,如果省略required属性,那么required的默认值是false。当指定required的值是true时,调用该Tag文件的JSP页面必须向该Tag文件中attribute指令中的name属性给出的对象传递一个引用,当指定required的值是false时,调用该Tag文件的JSP可以向该Tag文件中attribute指令中的name属性给出的对象传递或不传递对象的引用。 注: 在Tag文件中不可以再定义和attribute指令中的name属性给出的对象具有相同名字的变量,否则将隐藏attribute指令中给出的对象,使其失效。 在下面的例3_2中,triangle.tag存放在ch3\WEBINF\tags\example2目录中,该Tag文件负责计算、显示三角形的面积。example3_2.jsp页面保存在ch3中,使用Tag标记调用triangle.tag文件,并且向triangle.tag传递三角形三边的长度。 例3_2 example3_2.jsp(效果如图3.2所示) <%@ page contentType="text/html"%> <%@ page pageEncoding="utf-8"%> <%@ taglib tagdir="/WEB-INF/tags/example2" prefix="getTriangleArea"%> <HTML><body bgcolor=yellow > <p style="font-family:宋体;font-size:36;color:blue"> <%--使用Tag标记: --%> <getTriangleArea:triangle sideA="15" sideB="16" sideC="20"/> </p> </body></HTML> triangle.tag <%@ tag pageEncoding="utf-8"%> <%@ attribute name="sideA" required="true"%> <%@ attribute name="sideB" required="true"%> <%@ attribute name="sideC" required="true"%> <%! public String getArea(double a,double b,double c) { if(a+b>c&&a+c>b&&c+b>a) { double p=(a+b+c)/2.0; double area=Math.sqrt(p*(p-a)*(p-b)*(p-c)) ; String result=String.format("%.2f",area); return "<br>三角形面积(小数点保留2位):"+result; } else return("<br>"+a+","+b+","+c+"不能构成一个三角形,无法计算面积"); } %> <%out.println("<BR>三边: "+sideA+","+sideB+","+sideC); double a=Double.parseDouble(sideA); double b=Double.parseDouble(sideB); double c=Double.parseDouble(sideC); out.println(getArea(a,b,c)); %> 图3.2调用Tag文件计算面积 下面的例3_3中,JSP页面example3_3.jsp只负责将一组随机数据存放到链表(java.util.LinkedList类型对象)中,然后将链表传递给sort.tag,sort.tag负责按低到高顺序显示链表中的数据。sort.tag存放在ch3\WEBINF\tags\example3目录中,example3_3.jsp保存在ch3目录中。 例3_3 example3_3.jsp(效果如图3.3所示) <%@ page contentType="text/html"%> <%@ page pageEncoding="utf-8"%> <%@ page import="java.util.LinkedList"%> <%@ page import="java.util.Random"%> <%@ taglib tagdir="/WEB-INF/tags/example3" prefix="sortNumber"%> <HTML><body bgcolor=#CCCCCC> <%LinkedList<Double> listNumber=new LinkedList<Double>(); Random random=new Random(); for(int i =0;i<3;i++) { double d=random.nextDouble(); //[0,1)之间的随机数 listNumber.add(d); } %> <p style="font-family:宋体;font-size:36;color:blue"> 排序数据 <sortNumber:sort list="<%= listNumber %>"/><%-- 使用Tag标记 --%> </body></HTML> sort.tag <%@ attribute name="list" required="true" type="java.util.LinkedList"%> <%@ tag import="java.util.Collections"%> <%@ tag import="java.util.Iterator"%> <%Collections.sort(list); //排序链表 Iterator<Double> ite=list.iterator(); //得到迭代器 while(ite.hasNext()) {//遍历链表 out.print("<br>"+ite.next()); } %> 图3.3调用Tag文件排序数据 3.3.4variable指令 Tag文件通过使用attribute指令,可以使得调用该Tag文件的JSP页面动态地向其传递数据。在某些Web应用中,JSP页面不仅希望向Tag文件传递数据,而且希望Tag文件能返回数据给JSP页面。比如,许多JSP页面可能都需要调用某个Tag文件对某些数据进行基本的处理,但不希望Tag文件做进一步的特殊处理以及显示数据,因为各个JSP页面对数据的进一步处理或显示格式的要求是不同的。因此,JSP页面希望Tag文件将数据的基本处理结果存放在某些对象中,将这些对象返回给当前JSP页面即可。 Tag文件通过使用variable指令可以将Tag文件中的对象返回给调用该Tag文件的JSP页面。 variable指令的格式 variable指令的格式如下: <%@ variable name-given="对象名" variable-class="对象类型" scope="有效范围"%> variable指令中属性namegiven的值就是Tag文件返回给JSP页面的对象。该对象的名字必须符合标识符规定,即名字可以由字母、下画线、美元符号和数字组成,并且第一个字符不能是数字字符。variable指令中属性variableclass的值是返回的对象的类型,对象的类型必须带有包名,比如java.lang.Double、java.time. LocalDate等类型。如果variable指令中没有使用variableclass给出对象的类型,那么对象的类型是java.lang.String类型。 variable指令中scope属性的值指定对象的有效范围,scope的值可以取AT_BEGIN、NESTED和AT_END。当scope的值是AT_BEGIN时,JSP页面一旦开始使用Tag标记,就得到了variable指令返回给JSP页面的对象,JSP页面就可以在Tag标记的标记体中或Tag标记结束后的各个部分中使用variable指令返回给JSP页面的对象。当scope的值是NESTED时,JSP页面只可以在Tag标记的标记体中使用variable指令返回给JSP页面的对象。当scope的值是AT_END时,JSP页面只可以在Tag标记结束后。才可以使用variable指令返回给JSP页面的对象。 下面的variable指令给出的对象的名字是time,类型为java.time.LocalDate,有效范围是AT_END: <%@ variable name-given="time" variable-class="java.time.LocalDate" scope="AT_END"%> 对象的返回 Tag文件为了给JSP页面返回一个对象,就必须将返回的对象的名字以及该对象的引用存储到Tomcat服务器提供的内置对象jspContext中。Tag文件只有将对象的名字及其引用存储到jspContext中,JSP页面才可以使用该对象。比如,Tag文件的variable指令: <%@ variable name-given="time" variable-class="java.time.LocalDate" scope="AT_END"%> 为JSP页面返回名字是time的LocalDate对象。那么Tag文件中必须让jspContext调用 setAttribute("对象名",对象的引用); 方法存储名字是time的对象以及该对象的引用,例如: jspContext.setAttribute("time",LocalDate .now()); 将名字是time的LocalDate对象存储到jspContext中。 下面的例3_4中,JSP页面example3_4.jsp将String对象交给Tag文件handleData.tag,handleData.tag解析出String对象的字符序列中的全部数字,并计算出数字总和,将数字总和放在Double对象price中,然后返回给JSP页面example3_4.jsp。example3_4.jsp输出price对象中的数字总和。handleData.tag.tag存放在ch3\WEBINF\tags\example4目录中,example3_4.jsp保存在ch3目录中。 例3_4 example3_4.jsp(效果如图3.4所示) <%@ page contentType="text/html"%> <%@ page pageEncoding="utf-8"%> <%@ taglib tagdir="/WEB-INF/tags/example4" prefix="getPrice"%> <HTML><body bgcolor=#FFCCFF> <% String str="麻辣豆腐:20.6元,红烧肉:68.9元,烤鸭:199元"; %> <getPrice:handleData mess="<%= str %>"/><%--使用Tag标记 --%> <p style="font-family:宋体;font-size:36"> 菜单: <br>"<%= str %>"<br>价格总和: <%= price %><%--使用Tag标记返回的Double对象price --%> </p> <% str="毛巾:2.6元,香皂:6.9元,牙刷:12.3元"; %> <getPrice:handleData mess="<%= str %>"/> <p style="font-family:黑体;font-size:36;color:blue"> 购物小票: <br>"<%= str %>"<br>价格总和: <%= price %> </p> </body></HTML> handleData.tag <%@ attribute name="mess" required="true" type="java.lang.String"%> <%@ tag import="java.util.regex.Pattern"%> <%@ tag import="java.util.regex.Matcher"%> <%@ variable name-given="price" variable-class="java.lang.Double" scope="AT_BEGIN"%> <%! public Double getPriceSum(String input){//定义方法 Pattern pattern;//模式对象 Matcher matcher;//匹配对象 String regex="-?[0-9][0-9]*[.]?[0-9]*" ; //匹配数字的正则表达式 pattern=Pattern.compile(regex);//初始化模式对象 matcher=pattern.matcher(input);//初始化匹配对象,用于检索input double sum=0; while(matcher.find()) { String str=matcher.group(); sum += Double.parseDouble(str); } return new Double(sum); } %> <%//将返回的Double对象放在jspContext中,用名字price返回给JSP页面 jspContext.setAttribute("price",getPriceSum(mess)); %> 图3.4向JSP页面返回对象 注: 在JSP页面中不可以再定义与Tag文件返回的对象具有相同名字的变量,否则Tag文件无法将variable指令给出的对象返回给JSP页面(并将出现编译错误)。如果Tag文件同时使用variable指令和attribute指令,那么variable指令中namegiven和attribute指令中name给出的对象不能相同(否则将出现编译错误)。 3.3.5taglib指令 JSP页面或Tag文件都可以使用taglib指令引入标记库(如前面各个例子所示)。taglib指令格式如下: <%@ taglib tagdir="自定义标记库的位置" prefix="前缀"> 一个Tag文件也可以使用几个taglib指令标记引入若干个标记库,例如: <%@ taglib tagdir="/WEB-INF/tags" prefix="beijing"%> <%@ taglib tagdir="/WEB-INF/tags/tagsTwo" prefix="dalian"%> 视频讲解 3.4上机实验 提供了详细的实验步骤要求,按步骤完成,提升学习效果,积累经验,不断提高Web设计能力。 3.4.1实验1解析单词 实验目的 掌握JSP页面中使用Tag文件,向Tag文件传递对象,Tag文件负责处理对象中的数据。 实验要求 (1) JSP页面giveText.jsp负责将String对象,比如String str ="how are you",传递给所调用的Tag文件backWords.tag。 (2) backWords.tag文件负责解析出String对象中的单词,并将这些单词返回给JSP页面giveText.jsp。 (3) JSP页面giveText.jsp负责显示backWords.tag返回给它的单词。 (4) 在Tomcat服务器的webapps目录下(比如,D:\apachetomcat9.0.26\webapps)新建一个名字是ch3_practice_one的Web服务目录。把giveText.jsp文件保存到ch3_practice_one目录中。在ch3_practice_one目录下再建立目录结构: \WEBINF\tags\ practice1,将backWords.tag保存在practice1目录中。 (5) 用浏览器访问JSP页面giveText.jsp。 参考代码 参考代码运行效果如图3.5所示。 图3.5解析单词 giveText.jsp <%@ page contentType="text/html"%> <%@ page pageEncoding="utf-8"%> <%@ page import="java.util.Iterator"%> <%@ taglib tagdir="/WEB-INF/tags/practice1" prefix="getWords"%> <HTML><body bgcolor=#CCCCFF> <% String str="How are you,are you student? where are you from? "; %> <getWords:backWords okString ="<%= str %>" /> <%--使用Tag标记 --%> <p style="font-family:宋体;font-size:26"> <%= str %><br> <% Iterator<String> ite=words.iterator(); //使用Tag标记返回的对象words out.print("使用了"+ words.size()+"个单词:<br>"); while(ite.hasNext()) {//遍历集合 out.print(" "+ite.next()); } %> </p></body></HTML> backWords.tag <%@ tag import="java.util.HashSet"%> <%@ tag import="java.util.regex.Pattern"%> <%@ tag import="java.util.regex.Matcher"%> <%@ attribute name="okString" required="true" type="java.lang.String"%> <%@ variable name-given="words" variable-class="java.util.HashSet " scope="AT_BEGIN"%> <% HashSet<String>set=new HashSet<String>(); //集合不允许有相同的元素 Pattern pattern;//模式对象 Matcher matcher;//匹配对象 String regex="[a-zA-Z]+" ;//匹配英文单词 pattern=Pattern.compile(regex);//初始化模式对象 matcher=pattern.matcher(okString);//初始化匹配对象,用于检索okString while(matcher.find()) { String str=matcher.group(); set.add(str); } //将返回的set对象放在jspContext中,用名字words返回给JSP页面 jspContext.setAttribute("words",set); %> 3.4.2实验2显示日历 实验目的 和实验1的目的相同,进一步强化掌握JSP页面使用Tag文件,即向Tag文件传递对象,Tag文件负责处理数据。 实验要求 (1) Tag文件calendar.tag负责显示日历。 (2) 编写JSP页面useCalendar.jsp,要求useCalendar.jsp使用Tag标记使用calendar.tag,并将日期的年份和月份传递给calendar.tag。 (3) 在Tomcat服务器的webapps目录下(比如,D:\apachetomcat9.0.26\webapps)新建一个名字是ch3_practice_two的Web服务目录。把useCalendar.jsp文件保存到ch3_practice_two目录中。在ch3_practice_two目录下再建立目录结构: \WEBINF\tags\ practice2,将calendar.tag保存在practice2目录中。 (4) 用浏览器访问JSP页面useCalendar.jsp。 参考代码 参考代码运行效果如图3.6所示。 图3.6显示日历 useCalendar.jsp <%@ page contentType="text/html"%> <%@ page pageEncoding="utf-8"%> <%@ taglib tagdir="/WEB-INF/tags/practice2" prefix="getCalendar"%> <HTML><body bgcolor=#CCCCFF> <getCalendar:calendar year ="2025" month="2" /> <%-- 使用Tag标记 --%> </body></HTML> calendar.tag <%@ tag import="java.time.LocalDate"%> <%@ tag import="java.time.DayOfWeek"%> <%@ attribute name="year" required="true" type="java.lang.String"%> <%@ attribute name="month" required="true" type="java.lang.String"%> <% int y=Integer.parseInt(year); int m=Integer.parseInt(month); LocalDate date=LocalDate.of(y,m,1); int days=date.lengthOfMonth(); //得到该月有多少天 int space=0;//存放空白字符的个数 DayOfWeek dayOfWeek=date.getDayOfWeek(); //得到1号是星期几 switch(dayOfWeek) { case SUNDAY:space=0; break; case MONDAY:space=1; break; case TUESDAY:space=2; break; case WEDNESDAY: space=3; break; case THURSDAY:space=4; break; case FRIDAY:space=5; break; case SATURDAY:space=6; break; } String [] calendar=new String[space+days]; //用于存放日期和1号前面的空白 for(int i=0;i<space;i++) calendar[i]="--"; for(int i=space,n=1;i<calendar.length;i++){ calendar[i]=String.valueOf(n) ; n++; } %> <h3> <%=year %>年<%=month %>月的日历:</h3> <table border=0> <tr><th>星期日</th><th>星期一</th><th>星期二</th><th>星期三</th> <th>星期四</th><th>星期五</th><th>星期六</th> </tr> <% int n=0; while(n<calendar.length){ out.print("<tr>"); int increment=Math.min(7,calendar.length-n); for(int i=n;i<n+increment;i++) { out.print("<td>"+calendar[i]+"</td>"); } out.print("</tr>"); n=n+increment; } %> </table> 习 题 3 1. 用户可以使用浏览器直接访问一个Tag文件吗? 2. Tag文件应当存放在怎样的目录中? 3. Tag文件中的tag指令可以设置哪些属性的值? 4. Tag文件中的attribute指令有怎样的作用? 5. Tag文件中的variable指令有怎样的作用? 6. 编写两个Tag文件Rect.tag和Circle.tag。Rect.tag负责计算并显示矩形的面积,Circle.tag负责计算并显示圆的面积。编写一个JSP页面lianxi6.jsp,该JSP页面使用Tag标记调用Rect.tag和Circle.tag。调用Rect.tag时,向其传递矩形的两个边的长度; 调用Circle.tag时,向其传递圆的半径。 7. 编写一个Tag文件: GetArea.tag负责求出三角形的面积,并使用variable指令返回三角形的面积给调用该Tag文件的JSP页面。JSP页面负责显示Tag文件返回的三角形的面积。JSP在调用Tag文件时,使用attribute指令将三角形三边的长度传递给Tag文件。one.jsp和two.jsp都使用Tag标记调用GetArea.tag。one.jsp返回的三角形的面积保留最多3位小数,two.jsp返回的三角形的面积保留最多6位小数。