JavaWeb JSTL
1. JSTL标签库概述
1.1. 什么是JSTL标签库
JSTL全称为JSP Standard Tag Library,即JSP标准标签库。JSTL最初是一套Java官方的标签库解决方案,自从JavaEE 5之后被定义为标准的标签库。JSTL规范由Sun公司定制,Apache的Jakarta小组负责实现,目前的版本是JSTL1.2。
EL最初定义在JSTL1.0规范中,在JSP2.0之后,EL已经正式成为JSP规范的一部分。在JSTL1.1规范中,已经没有了EL的内容,不过在JSTL中仍然可以使用EL。
JSTL的目标是为了简化JSP页面的设计,JSTL的使用为页面设计人员和程序开发人员的分工协作提供了便利。
JSTL虽然叫做标准标签库,但实际上是由五个不同功能的标签库组成的。分别为core标签库、fmt标签库、SQL标签库、XML标签库及Functions标签库,其中Functions标签库就是前面提到了EL表达式的函数库,下面主要学习core标签库和fmt标签库。
1.2. 百度百科
JSTL(JSP Standard Tag Library,JSP标准标签库)是一个不断完善的开放源代码的JSP标签库,是由apache的jakarta小组来维护的。JSTL只能运行在支持JSP1.2和Servlet2.3规范的容器上,如tomcat 4.x。在JSP 2.0中也是作为标准支持的。
JSTL 1.0 发布于 2002 年 6 月,由四个定制标记库(core、format、xml 和 sql)和一对通用标记库验证器(ScriptFreeTLV 和 PermittedTaglibsTLV)组成。core 标记库提供了定制操作,通过限制了作用域的变量管理数据,以及执行页面内容的迭代和条件操作。它还提供了用来生成和操作 URL 的标记。顾名思义,format 标记库定义了用来格式化数据(尤其是数字和日期)的操作。它还支持使用本地化资源束进行 JSP 页面的国际化。xml 库包含一些标记,这些标记用来操作通过 XML 表示的数据,而 sql 库定义了用来查询关系数据库的操作。
如果要使用JSTL,则必须将jstl.jar和 standard.jar文件放到classpath中,如果你还需要使用XML processing及Database access (SQL)标签,还要将相关JAR文件放到classpath中,这些JAR文件全部存在于下载回来的zip文件中。
1.3. *
JSP标准标签库(JSP Standard Tag Library)是Java EE网络应用程序开发平台的组成部分。它在JSP规范的基础上,扩充了一个JSP的标签库来完成一些通用任务,比如XML数据处理、条件执行、数据库访问、循环和国际化。
JSTL是在JCP下,作为JSR 52被开发出来的。2006年5月8日发布了JSTL 1.2,接下来是2011年12月7日的JSTL 1.2.1。
JSTL提供了一个有效的途径,以在JSP页面中嵌入逻辑,而不是直接嵌入Java代码。使用标准标签集,减少了Java代码导致的不连续,从而提高代码的可维护性,并达到应用软件代码开发与用户界面间的关注点分离。
2. JSTL的core标签库
2.1. <c:out>标签
<c:out>标签用于计算一个表达式并将结果输出到当前的JspWriter对象。<c:out>标签的功能类似于JSP的表达式<%=expression%>。
<c:out>标签的语法格式如下:
<c:out value="表达式" escapeXml="是否转义" default="默认值"></c:out>
- value属性:指定被计算的表达式。
- escapeXml属性:确定在结果字符中的字符“<”、“>”、“’”、“””和“&”是否应该被转换为对应的字符引用或预定义实体引用,默认值为true。
如果值为true,那么字符“<”、“>”、“’”、“””和“&”将按照如下表进行转换。
字符 |
字符实体代码 |
< |
\< |
> |
\>
|
> ‘ |
\' |
“ |
\" |
& |
\& |
- default属性:如果value为null,那么将使用这个默认值。
下面是<c:out>标签的具体用法:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title> out.jsp </title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <% pageContext.setAttribute("name", "longestory"); %> <c:out value="${name}"></c:out><br/> <c:out value="<script>alert('hello longestory.');</script>" escapeXml="false"></c:out><br/> <c:out value="${helloworld}" default="unknown"></c:out> </body> </html>
2.2. <c:set>标签
<c:set>标签用于设置范围变量的值或者JavaBean对象的属性。
<c:set>标签的语法格式如下:
<c:set var="变量名称" value="表达式" scope="JSP域范围"></c:set>
- var属性:将value属性计算表达式得到的结果,保存在该变量名称中。
- value属性:指定被计算的表达式。
- scope属性:var属性指定的变量所使用的范围(JSP域范围)。
- target属性:要设置属性的对象。必须是JavaBean对象(对应的属性有setter方法)或者java.util.Map对象。
- property属性:要设置的target对象的属性名称。
下面是<c:set>标签的具体用法:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>set.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <c:set var="name" value="longestory" scope="session"></c:set> <c:out value="name"></c:out> <br> <jsp:useBean id="user" class="app.java.bean.User" scope="session" /> <c:set target="${user }" property="age" value="18"></c:set> <c:out value="${user.age }"></c:out> </body> </html>
2.3. <c:remove>标签
<c:remove>标签用于移除指定范围的变量。
<c:remove>标签的语法格式如下:
<c:remove var="变量名称" scope="JSP域范围"/>
- var属性:要移除指定范围的变量名称。
- scope属性:var属性指定的变量的范围(JSP域),默认是page。
下面是<c:remove>标签的具体用法:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>remove.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <% pageContext.setAttribute("a", "pageContext"); request.setAttribute("a", "session"); session.setAttribute("a", "session"); application.setAttribute("a", "application"); %> <!-- 删除所有域中的name为a的数据 --> <c:remove var="a"/> <c:out value="${a }" default="none"/> </body> </html>
2.4. <c:if>标签
<c:if>标签用于实现Java语言中的if语句的功能。
<c:if>标签的语法格式如下:
<c:if test="条件" var="变量名称" scope="JSP域范围"></c:if>
- test属性:测试的条件,用于判断标签体是否应该被执行。
- var属性:将测试条件的结果值保存在该变量中,该变量的值类型为Boolean。
- scope属性:var属性指定变量的JSP域范围,默认值为page。
下面是<c:if>标签的具体用法:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>if.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <c:set var="name" value="longestory"/> <c:if test="${not empty name }" var="test" scope="session"> <c:out value="${name }"/> </c:if> <br> <c:if test="${test }"> <c:out value="helloworld"></c:out> </c:if> </body> </html>
2.5. <c:choose>标签
<c:choose>标签、<c:when>标签和<c:otherwise>标签一起实现互斥条件的执行,类似于Java语言的if/else if/else语句。
<c:choose>标签、<c:when>标签和<c:otherwise>标签的语法格式如下:
<c:choose> <c:when test="条件"></c:when> <c:otherwise></c:otherwise> </c:choose>
下面是<c:choose>标签、<c:when>标签和<c:otherwise>标签的具体用法:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>choose.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <c:set var="c" value="${param.score }"/> <c:choose> <c:when test="${c > 100 || c < 0 }">输入有误,请输入正确的成绩:0~100之间。</c:when> <c:when test="${c >= 90 }">A</c:when> <c:when test="${c >= 80 }">B</c:when> <c:when test="${c >= 70 }">C</c:when> <c:when test="${c >= 60 }">D</c:when> <c:otherwise>E</c:otherwise> </c:choose> </body> </html>
2.6. <c:url>标签
<c:url>标签使用正确的URL重写规则构造一个URL。
<c:url>标签的语法格式如下:
<c:url value="url路径" var="变量名称" scope="JSP域范围"></c:url>
- value属性:要处理的URL。
- var属性:将处理的URL保存在该变量中,该变量的类型为String。
- scope属性:var属性指定变量的JSP域范围,默认值为page。
下面是<c:url>标签的具体用法:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>url.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <a href="<c:url value='/servlet/AServlet'/>">链接1</a><br/> <c:url value="/servlet/AServlet" var="url" scope="request"> <c:param name="username" value="longestory" /> <c:param name="password" value="123" /> </c:url> <a href="${requestScope.url }">链接2</a> </body> </html>
2.7. <c:forEach>标签
<c:forEach>标签用于对包含了多个对象的集合进行迭代,重复执行它的标签体,或者重复迭代固定的次数。
<c:forEach>标签的语法格式如下:
<c:forEach var="变量名称" begin="起始位置" end="截至位置" step="步长" items="集合对象" varStatus="迭代状态"></c:forEach>
- var属性:用于迭代的变量名称。
- begin属性:指定迭代的起始位置。
- end属性:指定迭代的截至位置。
- step属性:迭代的步长,默认的步长是1。
- items属性:要迭代的集合对象。
- varStatus属性:要迭代的状态,可以获取迭代过程中的一些状态。
对于<c:forEach>标签的实际用法,大体分为两种:
- 循环变量,指定开始和结束位置(计数):
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>foreach.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <c:forEach var="i" begin="1" end="10"> ${i }<br/> </c:forEach> <br/> <c:set var="sum" value="0"/> <c:forEach var="i" begin="1" end="100"> <c:set var="sum" value="${sum + i }"/> </c:forEach> sum = ${sum } </body> </html>
- 循环遍历集合,其中包含数组、List或Map集合等:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>foreach.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <% String[] arr = {"one", "two", "three"}; pageContext.setAttribute("arr", arr); List<String> list = new ArrayList<String>(); list.add("a"); list.add("b"); list.add("c"); pageContext.setAttribute("abc", list); Map<String,String> person = new LinkedHashMap<String,String>(); person.put("name", "zhangSan"); person.put("age", "23"); person.put("sex", "male"); pageContext.setAttribute("p", person); %> <c:forEach items="${arr }" var="str"> ${str }<br/> </c:forEach> <br/> <c:forEach items="${abc }" var="str"> ${str }<br/> </c:forEach> <br/> <c:forEach items="${p }" var="entry"> ${entry.key } = ${entry.value }<br/> </c:forEach> </body> </html>
- 在迭代中通过varStatus属性获取迭代中的状态。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>foreach.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <c:forEach items="${arr }" var="str" varStatus="vs"> first: ${vs.first }, last: ${vs.last }, count: ${vs.count }, index: ${vs.index }, current: ${vs.current }<br/> </c:forEach> </body> </html>
3. 自定义标签
3.1. 为什么要自定义
JSP中设计自定义标签的目的就是为了实现HTML代码重用。自定义标签可以实现非常复杂的功能,但使用起来很简单。
在JavaEE规范中,是不建议在JSP页面直接编写Java代码的,而是通过自定义标签来调用对应的Java代码实现某些功能。因为企业的业务需求是多种多样的,常见的开源框架只能提供通用的Java代码功能。如果要实现既定业务逻辑功能,就可以通过自定义标签方式解决。
3.2. 自定义标签步骤
在JavaEE的API中提供了Tag接口,我们可以通过实现该接口,完成自定义标签的功能。Tag接口在API中提供了如下几种方法,实现该接口时需要重写这几种方法:
Method Summary |
|
int |
doEndTag() |
int |
doStartTag() |
getParent() |
|
void |
release() |
void |
setPageContext(PageContext pc) |
void |
setParent(Tag t) |
自定义标签的创建具体步骤如下:
- 创建一个Java类实现Tag接口,该类完成自定义标签的具体逻辑功能。
public class OneTag implements Tag { public void setPageContext(PageContext pc) { System.out.println("这是setPageContext()方法..."); } public void setParent(Tag t) { System.out.println("这是setParent()方法..."); } public Tag getParent() { System.out.println("这是getParent()方法..."); return null; } public int doStartTag() throws JspException { System.out.println("这是doStartTag()方法..."); return 0; } public int doEndTag() throws JspException { System.out.println("这是doEndTag()方法..."); return 0; } public void release() { System.out.println("这是release()方法..."); } }
- 在Web工程目录的WEB-INF目录中,创建tld自定义标签的描述文件。
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"> <!-- 定义自定义标签库的版本 --> <tlib-version>1.0</tlib-version> <!-- 定义自定义标签库的名称 --> <short-name>ls</short-name> <!-- 定义自定义标签库的URI --> <uri>http://www.mytag.com/tags/onetag</uri> <!-- 添加自定义标签 --> <tag> <!-- 定义自定义标签的名称 --> <name>onetag</name> <!-- 定义自定义标签对应的类的完整路径 --> <tag-class>app.java.tags.OneTag</tag-class> <!-- 定义自定义标签内允许包含哪些内容,备选项如下: * empty:表示当前自定义标签内,不能包含任何内容.(空标签) * jsp:表示当前自定义标签内,可以包含有关JSP页面的所有内容. * scriptless:表示当前自定义标签内,不能包含JSP脚本内容.(具体指JSP脚本<%%>) * tagdependent:表示当前自定义标签仅供服务器端使用,不能为客户端页面提供显示. --> <body-content>empty</body-content> </tag> </taglib>
- 在JSP页面中引入自定义标签库。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="ls" uri="/WEB-INF/mytags/onetag.tld" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>onetag.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <ls:onetag/> </body> </html>
3.3. 自定义标签继承链
虽然目前可以通过实现Tag接口,完成自定义标签的功能,但是很麻烦。下面来研究一下Tag接口的继承链关系,如下图所示:
通过查看Tag接口的继承链关系,可以发现如下内容:
- Tag接口具有一个父级接口,叫做JspTag接口。Tag接口是JSP1.2规范定义的接口,JspTag接口是JSP2.0规范定义的接口,通过实现Tag接口的自定义标签被称之为传统标签。目前更多使用是JSP2.0规范。
- Tag接口具有两个子接口IterationTag接口和BodyTag接口,这两个接口分别有TagSupport实现类和BodyTagSupport实现类。具体自定义标签时,实际上是通过继承这两个实现类完成的,而不是实现Tag接口或其两个子接口完成。
- JspTag接口具有另一个子接口SimpleTag接口。通过实现SimpleTag接口的自定义标签被称之为简单标签,但实际上只是实现过程变得更为简单而已。该接口具有一个实现类SimpleTagSupport类,实现自定义标签时是通过继承该类完成的。
3.4. 自定义简单标签
掌握了Tag接口的继承链关系后,在今后的实际开发中,更多是自定义简单标签。要想实现自定义简单标签,要么实现SimpleTag接口,要么继承SimpleTagSupport类。具体实现步骤如下:
- 创建一个Java类继承SimpleTagSupport类,重写doTag()方法。
public class TwoTag extends SimpleTagSupport { @Override /** * 当执行到自定义标签时,调用该方法 */ public void doTag() throws JspException, IOException { //1 获取pageContext对象 PageContext pageContext = (PageContext)this.getJspContext(); //2 通过pageContext对象获取out内置对象 JspWriter out = pageContext.getOut(); //3 通过out内置对象向页面输出内容 out.println("<h1>hello tags.</h1>"); } }
- 在Web工程目录的WEB-INF目录中,创建tld自定义标签的描述文件。
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"> <tlib-version>1.0</tlib-version> <short-name>ls</short-name> <uri>http://www.longestory.com/tags/onetag</uri> <tag> <name>twotag</name> <tag-class>app.java.tags.TwoTag</tag-class> <body-content>empty</body-content> </tag> </taglib>
- 在JSP页面中引入自定义标签库。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="ls" uri="/WEB-INF/mytags/mytags.tld" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>twotag.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <ls:twotag/> </body> </html>
3.5. 简单标签的生命周期
到目前为止,通过Tag接口和SimpleTagSupport类分别实现了传统自定义标签和简单自定义标签两种,下面跟深入地了解一下自定义标签的生命周期。
首先,先来讨论传统标签的生命周期(以上面的传统自定义标签为例)。
- 自定义传统标签类
- 该自定义标签类的构造函数只被调用一次,说明该自定义标签类是单实例的。
- 每次执行标签,会调用setPageContext()方法。该方法用于向自定义标签类传递pageContext内置对象(通过该对象可以获取其他八个内置对象)。
- setPageContext()方法被调用后,调用setParent()方法。该方法用于向当前自定义标签类传递一个父标签内容,例如<c:choose>标签内使用的<c:when>标签。
- 最后调用的是doStartTag()方法和doEndTag()方法,分别用于开始执行标签和结束执行标签,自定义标签具体实现的逻辑功能封装在这两个方法中。
- tomcat结束时调用release()方法。该方法用于释放创建自定义标签所用的资源。
下面,查看SimpleTag接口的API方法内容,如下表:
Method Summary |
|
void |
doTag() |
getParent() |
|
void |
setJspBody(JspFragment jspBody) |
void |
setJspContext(JspContext pc) |
void |
setParent(JspTag parent) |
根据SimpleTag接口提供的方法,来讨论简单标签的生命周期(以上面的简单自定义标签为例)。
- 向自定义简单标签类,增加构造函数、setParent()方法、setJspContext()方法和setJspBody()方法。
public class TwoTag extends SimpleTagSupport { public TwoTag() { System.out.println("这是TwoTag方法..."); } @Override public void setParent(JspTag parent) { System.out.println("这是setParent方法..."); } @Override public void setJspContext(JspContext pc) { System.out.println("这是setJspContext方法..."); } @Override public void setJspBody(JspFragment jspBody) { System.out.println("这是setJspBody方法..."); } @Override public void doTag() throws JspException, IOException { System.out.println("这是doTag方法..."); } }
-
- 每次执行自定义标签时,构造函数都被调用一次,说明自定义简单标签类是多实例的。
- 自定义标签被执行时,调用setJspContext()方法。该方法用于向自定义标签类传递pageContext内置对象(通过该对象可以获取其他八个内置对象)。
- 调用setJspContext()方法后,调用doTag()方法。该方法用于封装自定义标签具体实现的逻辑功能。
- setParent()方法:用于向当前自定义标签传递父标签内容,例如<c:choose>标签内使用的<c:when>标签。如果没有传递父标签,则为null值。
- setJspBody()方法:用于向当前自定义标签传递JSP内容。因为配置自定义标签时使用了EMPTY,所以没有被调用。
- 自定义简单标签并没有像传统标签一样的release()释放资源的方法。
3.6. 带标签体的简单标签
上述实现的自定义简单标签只是最基本的用法,简单标签在自定义时,还可以定义标签体内容。具体实现步骤如下:
- 创建一个Java类继承SimpleTagSupport类,重写doTag()方法。
public class ThreeTag extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { //1 获取pageContext对象 PageContext pageContext = (PageContext) this.getJspContext(); //2 通过pageContext对象获取out内置对象 JspWriter out = pageContext.getOut(); //3 创建输出流对象 StringWriter writer = new StringWriter(); //4 获取标签体内容,为JspFragment类型 JspFragment body = this.getJspBody(); /* * 5 通过invoke()方法将标签体输出到指定的writer中 * * 如果writer参数为null,将输出到默认的writer中.即getJspContext().getOut() */ body.invoke(writer); //6 打印标签体内容 out.println(writer.getBuffer().toString().toUpperCase()); } }
- 在Web工程目录的WEB-INF目录中,创建tld自定义标签的描述文件。
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"> <tlib-version>1.0</tlib-version> <short-name>ls</short-name> <uri>http://www.mytag.com/tags/onetag</uri> <tag> <name>threetag</name> <tag-class>app.java.tags.ThreeTag</tag-class> <!-- 定义自定义简单标签时,不能使用JSP选项. --> <body-content>tagdependent</body-content> </tag> </taglib>
- 在JSP页面中引入自定义标签库。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="ls" uri="/WEB-INF/mytags/mytags.tld" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>threetag.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <ls:threetag> this is mytag. </ls:threetag> </body> </html>
3.7. 带属性的简单标签
除了可以实现带标签体的自定义简单标签外,还可以实现带属性的自定义简单标签,具体实现步骤如下:
- 创建一个Java类继承SimpleTagSupport类,重写doTag()方法。
public class FourTag extends SimpleTagSupport { private Boolean test; public Boolean getTest() { return test; } public void setTest(Boolean test) { this.test = test; } @Override public void doTag() throws JspException, IOException { if(test){ this.getJspBody().invoke(null); } } }
- 在Web工程目录的WEB-INF目录中,创建tld自定义标签的描述文件。
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"> <tlib-version>1.0</tlib-version> <short-name>ls</short-name> <uri>http://www.mytag.com/tags/onetag</uri> <tag> <name>fourtag</name> <tag-class>app.java.tags.FourTag</tag-class> <body-content>scriptless</body-content> <!-- 为自定义简单标签配置一个属性 --> <attribute> <!-- 定义该属性名称 --> <name>test</name> <!-- 定义该属性是否必要 --> <required>true</required> <!-- 定义该属性是否允许EL表达式或JSTL标签等 --> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib>
- 在JSP页面中引入自定义标签库。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="ls" uri="/WEB-INF/mytags/mytags.tld" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>fourtag.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <ls:fourtag test="${param.flag }"> this is mytag. </ls:fourtag> </body> </html>
3.8. 其他情况的简单标签
除了自定义简单标签带有标签体和属性之外,简单标签还可以实现很多功能。例如在执行完自定义简单标签后,JSP页面后面的内容不再执行等功能。下面就来实现一下这个功能,具体实现步骤如下:
- 创建一个Java类继承SimpleTagSupport类,重写doTag()方法。
public class FiveTag extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { this.getJspContext().getOut().println("下面的内容都是不显示的."); throw new SkipPageException(); } }
- 在Web工程目录的WEB-INF目录中,创建tld自定义标签的描述文件。
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"> <tlib-version>1.0</tlib-version> <short-name>ls</short-name> <uri>http://www.mytag.com/tags/onetag</uri> <tag> <name>fivetag</name> <tag-class>app.java.tags.FiveTag</tag-class> <body-content>empty</body-content> </tag> </taglib>
- 在JSP页面中引入自定义标签库。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="ls" uri="/WEB-INF/mytags/mytags.tld" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>fivetag.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <ls:fivetag/> <p>人从来到这个世界上,就注定着最终要离开。 </body> </html>
3.9. 打包自定义标签库
自定义标签,无论是传统标签还是简单标签,都是为了将来开发JSP页面更加简单。而目前自定义的标签只能在当前Web工程中使用,其他Web工程并不能使用。为了可以让其他Web工程也可以使用自定义标签库内容,需要将自定义标签打包成JAR包。具体操作步骤如下:
- 创建一个Java工程,方便打包。
- 在当前Java工程的根目录中,右键创建一个目录为“META-INF”。
- 将tld自定义标签描述文件,拷贝到“META-INF”目录中。
- 将对应自定义标签类拷贝到当前Java工程的“src”目录中。
- 当将自定义标签类拷贝到“src”目录中后,会报错。原因是Java工程中没有JavaEE所需的JAR包。
- 需要为当前Java工程导入JavaEE所需的JAR包即可。
- 将当前Java工程导出为JAR包即可。
4. 国际化
4.1. 什么是国际化
如果一个系统或软件在某个国家或地区使用时,采用该国家或地区的语言、数字、货币和日期等习惯,将这种方式称之为资源本地化。开发系统或软件时,可以支持多个国家或地区的本地化应用,就可以叫做资源国际化。
所谓资源国际化,并不是简单提供几套不同语言的软件系统就能解决的,是一个很复杂的问题。而Java提供了资源绑定ResourcesBundle、地区Locale、时区TimeZone等支持资源国际化。资源国际化有两个常用的术语I18N与I10N。
- I18N即资源国际化,全称为Internationalization,因为首字母I与末字母N之间共18个字母,又称I18N。通俗地讲,资源国际化就是要让这个系统或软件使用国际环境,如语言、数字、货币和日期等。
- I10N为资源本地化,全称为Localization,因为首字母I与末字母N之间共10个字母,又称I10N。资源本地化就是要让这个系统或软件使用当地环境,如语言、数字、货币和日期等。
4.2. 如何国际化
让一个系统或软件实现本地化不难,但想要实现国际化还是有一些难度的。下面先来学习Java中提供的实现国际化功能的方式,具体内容如下:
-
在Web工程的“src”根目录下创建国际化资源文件。
- 国际化默认的资源文件命名方式为“资源文件名称.properties”。
name=default longestory
-
- 如果创建对应本地化资源文件的话,命名方式为“资源文件名称_语言代码_国家代码.properties”。下面分别为英文资源文件和中文资源文件,其中中文资源文件的编码为ISO-8859-1。
name=longestory
name=\u9F99\u54E5\u6709\u8BDD\u8BF4
- 创建一个Java文件,用于读取国际化资源文件,进行测试。
public class ResourceBundleTest { @Test // 与访问用户默认的语言和国家匹配 public void demo(){ // 通过ResourceBundle类的getBundle(默认国际化资源文件名称)方法读取国际化资源文件 ResourceBundle bundle = ResourceBundle.getBundle("info"); // 通过ResourceBundle对象的的getString(key)获取value String value = bundle.getString("name"); // 测试打印 System.out.println(value); } }
如果想要获取指定国家和语言的国际化资源文件,可以如下来完成。
- 创建一个Java文件,用于读取国际化资源文件,进行测试。
public class ResourceBundleTest { @Test public void demo2(){ // 通过ResourceBundle类的getBundle(默认国际化资源文件名称)方法读取国际化资源文件 ResourceBundle bundle = ResourceBundle.getBundle("info",Locale.US); // 通过ResourceBundle对象的的getString(key)获取value String value = bundle.getString("name"); // 测试打印 System.out.println(value); } }
4.3. 日期国际化
除了文本信息需要国际化以外,日期也需要国际化,可以使用Java提供的java.text.DateFormat类来完成。首先,来参考一下Java提供的API文档,需要用到DateFormat类的哪些方法:
方法摘要 |
|
static DateFormat |
getDateInstance(int style, Locale aLocale) |
static DateFormat |
getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale) |
static DateFormat |
getTimeInstance(int style, Locale aLocale) |
创建一个Java类,用于获取日期和时间等内容的国际化方式。
public class DateFormatTest { @Test public void demo(){ Date date = new Date(); /* * getDateInstance(int style,Locale local): * * 获得日期 formatter,该 formatter 具有给定语言环境的给定格式化风格. * * style参数:表示使用的日期格式化风格,常用的选项有FULL、LONG、MEDIUM、SHORT. * * local参数:指定使用的语言环境. */ DateFormat dateFormat1 = DateFormat.getDateInstance(DateFormat.FULL, Locale.CHINA); System.out.println("日期国际化:"+dateFormat1.format(date)); /* * getTimeInstance(int style,Locale local) * * 获得时间 formatter,该 formatter 具有给定语言环境的给定格式化风格. * * style参数:表示使用的时间格式化风格,常用的选项有FULL、LONG、MEDIUM、SHORT. * * local参数:指定使用的语言环境. */ DateFormat dateFormat2 = DateFormat.getTimeInstance(DateFormat.LONG, Locale.CHINA); System.out.println("时间国际化:"+dateFormat2.format(date)); /* * getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) * * 获得日期/时间 formatter,该 formatter 具有给定语言环境的给定格式化风格. * * dateStyle参数:表示使用的日期格式化风格,常用的选项有FULL、LONG、MEDIUM、SHORT. * * timeStyle参数:表示使用的时间格式化风格,常用的选项有FULL、LONG、MEDIUM、SHORT. * * local参数:指定使用的语言环境. */ DateFormat dateFormat3 = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.CHINA); System.out.println("日期和时间同时国际化:"+dateFormat3.format(date)); } }
4.4. 数字国际化
完成日期的国际化内容后,再来完成数字的国际化方式。Java提供的java.text.NumberFormat类来完成。首先,来参考一下Java提供的API文档,需要用到NumberFormat类的哪些方法:
方法摘要 |
|
static NumberFormat |
getCurrencyInstance(Locale inLocale) |
static NumberFormat |
getIntegerInstance(Locale inLocale) |
static NumberFormat |
getNumberInstance(Locale inLocale) |
static NumberFormat |
getPercentInstance(Locale inLocale) |
void |
setMaximumFractionDigits(int newValue) |
void |
setMaximumIntegerDigits(int newValue) |
void |
setMinimumFractionDigits(int newValue) |
void |
setMinimumIntegerDigits(int newValue) |
创建一个Java类,用于获取日期和时间等内容的国际化方式。
public class NumberFormatTest { @Test public void demo1(){ double i = 3.1415926; NumberFormat numberFormat1 = NumberFormat.getIntegerInstance(Locale.CHINA); System.out.println("指定语言环境的整数格式为:"+numberFormat1.format(i)); NumberFormat numberFormat2 = NumberFormat.getNumberInstance(Locale.CHINA); System.out.println("指定语言环境的通用数字格式为:"+numberFormat2.format(i)); NumberFormat numberFormat3 = NumberFormat.getCurrencyInstance(Locale.CHINA); System.out.println("指定语言环境的货币格式为:"+numberFormat3.format(i)); NumberFormat numberFormat4 = NumberFormat.getPercentInstance(Locale.CHINA); System.out.println("指定语言环境的百分比格式为:"+numberFormat4.format(i)); } }
4.5. 动态文本国际化
之前实现了文本信息的国际化,可以使用占位符实现固定文本实现动态变化。具体实现方式参考如下内容:
public class MessageFormatTest { @Test public void demo(){ /* * 作为错误信息的显示 * String msg1 = "用户名不能为空"; * String msg2 = "密码不能为空"; * * 所有不能为空提示信息完全一样,区别是字段名称 */ String pattern = "{0}不能为空"; // 通过MessageFormat 将信息替换到占位符中 String s1 = MessageFormat.format(pattern, "用户名"); String s2 = MessageFormat.format(pattern, "密码"); System.out.println(s1); System.out.println(s2); } }
当然,占位符不仅可以使用一个,多个占位符也是允许的,参考如下内容:
public class MessageFormatTest { @Test public void demo(){ String pattern = "{0}的长度必须在 {1}到{2}位数之间"; System.out.println(MessageFormat.format(pattern, "用户名", 3, 10)); } }
动态文本信息的国际化还具有很多高级用法,例如如下案例:
public class MessageFormatTest { @Test public void demo() { // 动态文本高级应用 String s = "At {0,time,short} on {0,date,medium}, a hurricance destroyed {1,number,integer} houses and caused {2,number,currency} of damage"; // 第一个参数 日期对象12:30 pm on jul 3,1998 Calendar calendar = Calendar.getInstance();// 日历类 // 所有日期月份从0开始 calendar.set(1998, 6, 3, 12, 30, 0); Date date = calendar.getTime(); // 第二个参数 99 int n = 99; // 第三个参数 $1000000 int m = 1000000; // 指定locale 是美国 MessageFormat messageFormat = new MessageFormat(s, Locale.US); System.out.println(messageFormat.format(new Object[] { date, n, m })); } }
4.6. 国际化案例
上述国际化内容都只是利用测试类完成国际化逻辑,下面利用国际化内容完成登录页面的国际化功能。具体的操作步骤如下:
- 在Web工程的“src”根目录下创建国际化资源文件。
- info.properties
info = default form
username = default username
password = default password
submit = default submit
- info_en_US.properties
info = form
username = username
password = password
submit = submit
- info_zh_CN.properties
info=\u767B\u9646\u9875\u9762
username=\u7528\u6237\u540D
password=\u5BC6\u7801
submit=\u767B\u9646
- 创建一个JSP页面用于显示登录信息,并实现国际化。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>login.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <% // 接收参数locale,该参数传递国际化环境,例如zh_CN. String localeinfo = request.getParameter("locale"); // 创建ResourceBundle对象 ResourceBundle bundle = null; // 判断国际化环境内容是否为空 if(localeinfo == null){ // 如果为空,使用默认的国际化资源文件内容. bundle = ResourceBundle.getBundle("info");// 使用默认 }else{ // 如果不为空,将国际化环境参数,拆分成语言和国家. String[] arr = localeinfo.split("_"); if(arr.length>1){ // 表示国际化环境参数包含语言和国家内容 bundle = ResourceBundle.getBundle("info",new Locale(arr[0],arr[1])); }else if(arr.length == 1){ // 表示国际化环境参数只包含语言内容 bundle = ResourceBundle.getBundle("info",new Locale(arr[0])); } } %> <h1><%=bundle.getString("info") %></h1> <form action="#" method="post"> <%=bundle.getString("username") %> <input type="text" name="username" /><br/> <%=bundle.getString("password") %> <input type="password" name="password" /><br/> <input type="submit" value="<%=bundle.getString("submit") %>" /> </form> </body> </html>
4.7. JSTL国际化标签
上述登录案例除了使用在JSP页面嵌入Java代码实现以外,还可以通过JSTL标签库的I18N标签库来实现。具体实现方式如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>login.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <!-- 设置指定国家locale --> <fmt:setLocale value="${param.locale}"/> <!-- 创建ResourceBundle --> <!-- 将ResourceBundle对象保存 page范围 bundle 属性中 --> <fmt:setBundle basename="info" var="bundle" scope="page"/> <!-- 显示form表单 --> <h1><fmt:message bundle="${bundle}" key="info" /> </h1> <form action="xxxx" method="post"> <fmt:message bundle="${bundle}" key="username" /> <input type="text" name="username" /><br/> <fmt:message bundle="${bundle}" key="password" /> <input type="password" name="password" /><br/> <input type="submit" value="<fmt:message bundle="${bundle}" key="submit" /> " /> </form> </body> </html>
JSTL标签库的I18N标签库除了以上标签外,也提供了日期和数字的格式化功能。具体内容参考如下内容:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>fmt.jsp</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> </head> <body> <% pageContext.setAttribute("date", new Date()); pageContext.setAttribute("num", "3"); %> <fmt:formatDate value="${date }" pattern="yyyy-MM-dd HH:mm:ss"/><br/> <fmt:formatNumber value="${num }" pattern="#.##" /><br/> <fmt:formatNumber value="${num }" pattern="0.00" /><br/> </body> </html>