Tomcat与JavaWeb 8.2 自定义标签的创建和使用(一)访问标签属性
1. 创建和使用message标签(访问标签属性)
在JSP文件中会包含许多静态文本,例如以下hello0.jsp中的“helloapp”"Nice to meet you:"都是静态文本:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>helloapp</title>
</head>
<body>
<b>Nice to meet you: ${param.username}</b>
</body>
</html>
下面创建一个能够替换JSP文件中的所有静态文本的message标签,它放在mytaglib标签库中,hello.jsp与hello0.jsp能生成同样的网页,区别在于hello.jsp使用了 message标签。在hello.jsp文件中使用message标签的步骤如下。
1) 在JSP文件中引入这个标签库:
<%@ taglib prefix="mm" uri="/mytaglib" %>
2) 用<mm:message>标签替换文件中的所有静态文本,以下是hello.jsp的源代码:<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="mm" uri="/mytaglib" %>
<html>
<head>
<title><mm:message key="hello.title"/></title>
</head>
<body>
<b><mm:message key="hello.hi"/>:${param.username}</b>
</body>
</html>
当客户访问hello.jsp时,message标签的处理类会根据key属性的值从一个特定的资源文件中找到与key匹配的字符串。假定与"hello.hi"字符串匹配的字符串为"Nice to meet you",那么message标签的处理类就将这个字符串输出到网页上。
对开发人员来说,采用message标签可以使JSP网页变得简洁并且易于维护。如果许多JSP网页的静态文本内容发生需求变更,那么无需修改这些JSP文件的代码,而只需要修改一个集中存放了所有网页的静态文本的资源文件即可。
采用message标签,还可以方便地实现同一个JSP文件支持多种语言版本。message标签可以根据客户选择的语言来输出响应的静态文本。使用不同语言的客户访问hello.jsp文件,message标签的处理过程如图所示:
1.1 创建message标签的处理类MessageTag
下面将创建message标签的处理类MessageTag,以及相关的资源文件和Servlet类。
1) 创建包含JSP网页静态文本的资源文件
首先创建包含JSP网页静态文本的资源文件,这些文本以key/value的形式存放。在messageresource.properties资源文件中存放英文静态文本,在messageresource_ch.properties资源文件中存放中文静态文本,这两个文件都要存放在WEB_INF目录下:
messageresource.properties
hello.title = helloapp
hello.hi = Nice to meet you
login.title = helloapp
login.user = User Name
login.password = Password
login.submit = Submit
messageresource_ch.properties
hello.title = helloapp的hello页面
hello.hi = 你好
login.title = helloapp的登录页面
login.user = 用户名
login.password = 密码
login.submit = 提交
以上资源文件中包含了login.jsp和hello.jsp的静态文本,为了便于管理和区分不同JSP文件的文本,约定login.jsp中的静态文本的key以login开头,而hello.jsp中的静态文本以hello开头。
2) 在Web应用启动时加载静态文本
尽管加载静态文本的任务可以直接由标签处理类来完成,但是把初始化的操作安排在Web应用启动时完成,更符合Web编程的规范。在本例中,将用LoadServlet类的init()方法来完成加载静态文本的任务。init()方法分别从messageresource.properties文件和messageresource_ch.properties文件中读取英文、中文静态文本,然后把它们加载到各自的Properties对象中,最后再把两个Properties对象作为属性保存到Web应用范围内。下面是其源代码:
LoadServlet.java
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Properties;
public class LoadServlet extends HttpServlet {
@Override
public void init() throws ServletException {
Properties ps = new Properties();
Properties ps_ch = new Properties();
try {
ServletContext context = getServletContext();
InputStream in = context.getResourceAsStream("/WEB_INF/messageresource.properties");
ps.load(in);
InputStream in_ch = context.getResourceAsStream("/WEB_INF/messageresource_ch.properties");
ps_ch.load(in);
in.close();
in_ch.close();
//在Web应用范围内存放包含静态文本的Properties对象
context.setAttribute("ps",ps);
context.setAttribute("ps_ch",ps_ch);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
init();
PrintWriter out = resp.getWriter();
out.println("The resource file is loaded.");
}
}
为了保证在Web应用启动时就加载LoadServlet,应该在web.xml中配置这个Servlet时设置为<load-on-setup>子元素:<web-app>
<servlet>
<servlet-name>LoadServlet</servlet-name>
<servlet-class>LoadServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>LoadServlet</servlet-name>
<url-pattern>/load</url-pattern>
</servlet-mapping>
</web-app>
3) 创建MessageTag类
MessageTag.java
package mypack;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.TagSupport;
import java.util.Properties;
public class MessageTag extends TagSupport {
private String key = null;
public String getKey() {
return this.key;
}
public void setKey(String key) {
this.key = key;
}
@Override
public int doEndTag() throws JspException {
try {
Properties ps = (Properties) pageContext.getServletContext().getAttribute("ps");
Properties ps_ch = (Properties) pageContext.getServletContext().getAttribute("ps_ch");
HttpSession session = pageContext.getSession();
String language = (String) session.getAttribute("language");
String message = null;
if (language != null && language.equals("Chinese")) {
message = (String) ps_ch.get(key);
} else {
message = (String) ps.get(key);
}
pageContext.getOut().print(message);
} catch (Exception e) {
throw new JspTagException(e);
}
return EVAL_PAGE;
}
}
MessageTag类包含一个成员变量key,它与<message>标签的属性key对应。在MessageTag中定义了getKey()和setKey()方法。LoadServlet的init()方法吧Properties对象保存在Web应用范围内。在MessageTag的doEndTag()方法中,首先从Web应用范围内读取包含静态文本的Properties对象,接着从会话范围内读取当前客户所使用的语言,然后根据使用的语言来选择相应的Properties对象,从中读取key对应的静态文本。如果用户选择的是中文语言,还要进行字符编码转换处理。Properties对象在通过InputStream流装载文件时,按照ISO-8859-1编码来读取数据,因此应该把葱Properties对象中读取的静态文本转换为中文编码GB2312.
1.2 创建标签库描述文件(TLD)
TLD文件采用XML文件格式,对标签库及库中的标签做了描述。TLD文件中的元素可以分为3类:<taglib>,标签库元素;<tag>,标签元素;<attribute>,标签属性元素。
1) 标签库元素<taglib>
标签库元素用来设定标签库的相关信息,它的子元素包括:
- tlib-version:指定标签库的版本
- jsp-version:指定JSP的版本
- short-name:指定标签库默认的前缀名(prefix)
- uri:sh标签库的唯一访问标识符
- info:设定标签库的说明信息。
2) 标签元素<tag>
标签元素用来定义一个标签,它的子元素包括:
- name:设定标签的名字
- tag-class:设定标签的处理类
- body-content:设定标签主体的类型
- info:设定标签的说明信息
以上<tag>元素的<body-content>子元素用于设定标签主体的类型,可选值包括:
- empty:标签主体为空。
- scriptless:标签主体不为空,并且包含JSP的EL表达式和动作元素,但不能包含JSP的脚本元素。所谓动作元素是指<jsp:include>和<jsp:forward>等以"jsp"为前缀的JSP内置标签。所谓脚本元素是指以"<%!"、"<%="、"<%!"这三种以"<%"开头的JSP标记。
- jsp:标签主体不为空,并且包含JSP代码,在JSP代码中可包含EL表达式、动作元素和脚本元素。<body-content>子元素的scriptless可选值与jsp可选值的区别在于前者不能包含JSP的脚本元素。
- tagdependant:标签主体不为空,并且标签主体内容由标签处理类来解析和处理。标签主体内的所有代码都会原封不动地传给标签处理类,而不是把标签主体的执行结果传给标签处理类。加入用户定义了一个<sql:query>标签,它的<body-content>元素的值为tagdependant。以下JSP代码使用了该标签:
<sql:query>
select * from MemberDB where ID<10
</sql:query>
这段代码中的标签主体为一个SQL语句,它将由<sql:query>标签的处理类来处理,该类负责执行这个SQL语句。
3) 标签属性元素<attribute>
标签属性元素用来描述标签的属性,该元素的子元素包括:
- name:属性名称
- required:属性是否是必须的,默认为false
- rtexprvalue:属性值是否可以为基于"<%"和"%>"形式的Java表达式或EL表达式
对于<rtexprvalue>子元素,如果取值为true,表示标签的属性可以为普通的字符串,或者是Java表达式和EL表达式;如果是false,那么标签的属性只能为普通的字符串。例如对于以下mytag标签,它的num属性就是一个Java表达式,因此当TLD文件中定义mytag标签的num属性时,应该把<rtexprvalue>子元素设置为true。
<% int num = 1;%>
<prefix:mytag num="<%= num %>"/>
下面将创建TLD文件,命名为mytaglib.tld。在这个文件中定义了uri为"/mytaglib"的标签库,本章创建的所有标签都位于这个标签库中。下例程是mytaglib.tld文件的源代码,它定义了一个名为message的标签,这个标签有一个名为"key"的属性。本章后文还会介绍向mytaglib.tld文件中加入其他范例标签的定义代码。
WEB_INF/mytaglib.tld
<?xml version="1.0" encoding="ISO-8859-1"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<tlib-version>1.0</tlib-version>
<short-name>mytaglib</short-name>
<uri>/mytaglib</uri>
<tag>
<description>produce message by key</description>
<name>message</name>
<tag-class>mypack.MessageTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>key</name>
<required>true</required>
</attribute>
</tag>
</taglib>
由于<message>标签的主体为空,因此<body-content>子元素的值为"empty"。当在JSP文件中使用<message>标签时,必须为标签设置key属性,因此<attribute>元素的<required>子元素的值为"true".
1.3 在Web应用中使用标签
如果在Web应用中用到了特定标签库中的自定义标签,则应该在web.xml中加入<taglib>元素,它用于声明所引用的标签库:
<jsp-config>
<taglib>
<taglib-uri>/mytaglib</taglib-uri>
<taglib-location>/WEB-INF/mytaglib.tld</taglib-location>
</taglib>
</jsp-config>
在JSP文件中需要通过<%@ taglib>指令来声明对标签库的引用,例如:<%@ taglib uri="/mytaglib" prefix="mm" %>
以上指令中的uri属性用来指定标签库的标识符,它和web.xml中的<taglib-uri>元素的值保持一致。prefix属性表示在JSP文件中引用这个标签库中的标签的前缀,例如如下代码表示引用mytaglib标签库中的<message>标签:<title><mm:message key="hello.title" /></title>
除了按照本章的方式在web.xml文件中声明引入的标签库,还可以直接使用第三方提供的符合特定规范的标签库的JAR打包文件下面在Web应用中加入3个负责产生网页的文件:index.html、login.jsp和hello.jsp,它们之间的链接关系为:index.html -> login.jsp -> hello.jsp,这三个文件组成了一个小小的支持中文和英文两个版本的Web应用。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>helloapp</title>
</head>
<body>
<p>Welcome to helloapp!</p>
<a href="login.jsp?language=English">English Version</a>
<a href="login.jsp?language=Chinese">中文版本</a>
</body>
</html>
login.jsp<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="mm" uri="/mytaglib" %>
<html>
<%
String language = request.getParameter("language");
if (language==null) language="English";
session.setAttribute("language",language);
%>
<head>
<title><mm:message key="login.title"/></title>
</head>
<body>
<br>
<form name="loginForm" method="post" action="hello.jsp">
<table>
<tr>
<td><div align="right"><mm:message key="login.user"/>:</div> </td>
<td><input type="text" name="username"> </td>
</tr>
<tr>
<td><div align="right"><mm:message key="login.password"/>:</div> </td>
<td><input type="password" name="username"> </td>
</tr>
<tr>
<td></td>
<td><input type="submit" name="Submit" value=<mm:message key="login.submit"/> > </td>
</tr>
</table>
</form>
</body>
</html>
hello.jsp<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="mm" uri="/mytaglib" %>
<html>
<head>
<title><mm:message key="hello.title"/></title>
</head>
<body>
<b><mm:message key="hello.hi"/>:${param.username}</b>
</body>
</html>
OK,需要的文件都已经写好,下面运行起来应用,进入index.html效果图:
分别点击中英文版本,会出现对应语言的表单。(如果点击中文版本显示Server Internal Error,可能是IDEA编码设置的问题,在IDEA的File ——Settings——Editor——File Encodings,把Global和Project Encoding设为UTF-8,把下方Default encoding for properties files设置为ISO-8859-1,并勾选旁边的Transparent native-to-ascii conversion)
上一篇: 38.Java算术运算符
下一篇: ANSI和UNICODE字符串处理函数