一、 Struts 2 标签库概述
Struts 2 标签库大大简化了数据输出,也提供了大量标签来生成页面效果。
1.1 标签的优势
JSP 页面里大量嵌套 Java 脚本时,整个页面可读性下降,可维护性也下降。
从 JSP 规范 1.1 版以后,JSP 增加了自定义标签库的规范,通过自定义标签库,可以在简单的标签中封装复杂的功能,从而避免了 JSP 嵌套 Java 脚本
注意: 自定义标签库是一种表现层技术,这是其与 Servlet 最大的差别之处,虽然自定义标签库也需要提供一个 Java 类,但这个 Java 类不应该封装数据访问逻辑、控制器逻辑,它仅能封装数据标签逻辑,因为它是表现层技术。虽然 JSTL (JSP Standar Tag Library,JSP 标准标签库)也提供了 SQL 标签库,但不推荐使用。
开发自定义标签相对复杂,开发自定义标签需要如下步骤:
① 开放自定义标签处理类,标签处理类需要继承 TagSupport 类或其子类
② 建立一个 *.tld 文件,每个 tld 文件对应一个标签库,每个标签库对应多个标签
③ 在 web.xml 文件中增加自定义标签的定义
④ 先在 JSP 文件中使用 taglib 编辑指定导入标签库,然后才能使用自定义标签
JSP 规范制定了一个标准的标签库--- JSTL (JSP Standard Tag Library ),即 JSP 标准标签库。
自定义标签的优势:
● 标签使用更加简单,无需 Java 语言知识,即可开发 JSP ,通过简单的标签完成复杂了逻辑
● 避免了 JSP 页面中嵌套 JSP 脚本,因此开发 JSP 无需 Java 知识,有利于团队协作
● JSP 页面不再嵌套 JSP 脚本,可读性提高,有利于页面的后期维护、升级。
从 JSP 2.0 规范开始,JSP 引入了简化自定义标签的规范,开发自定义标签更加简单,步骤如下:
① 开发自定义标签处理类,统一继承 SimpleTagSupport 类,无需分别继承 TagSupport,或者 BodyTagSupport 类。
② 建立一个 *.tld 文件,每个 tld 文件对应一个标签库,每个标签库对应多个标签。 Web 应用自动加载 *.tld 文件,避免在 web.xml 文件中增加自定义标签定义
③ 先在 JSP 文件中使用 taglib 编辑指定导入标签库,然后才可以使用自定义标签
1.2 Struts 2 的标签分类
Struts 2 标签库的巨大改进之处:不依赖任何表现层技术,可以在各种表现层技术中使用,包括 JSP 、Velocity 、 FreeMarker 等模版技术中使用
Struts 2 标签分类: 大致分 3 类
● UI (User Interface 用户界面)标签: 主要用于生成 HTML 元素的标签
● 非 UI 标签: 主要用于数据访问、逻辑控制等的标签
● Ajax 标签: 用于 Ajax (Asynchronous JavaScript And XML )支持的标签
UI 标签,又分如下 2 类
● 表单标签: 用于生成 HTML 页面的 form 元素,以及普通表单元素的标签
● 非表单标签: 用于生成页面上的树、Tab 页等标签。
非 UI 标签,也分如下 2 类
● 流程控制标签: 主要包含用于实现分支、循环等流程控制的标签
● 数据访问标签: 只要包含用于输出 ValueStack 中的值,完成国际化等功能的标签
二、 Struts 2 标签入门
Struts 2 内建了大量标签。除此之外,还提供例如 Dojo 、YUI 插件等,它们包含的标签还提供 Ajax 功能
2.1 使用 Struts 2 标签的准备
打开 struts2-core-2.2.1.jar 文件,
在 META-INF 路径下找到 struts-tags.tld :
struts-tags.tld 片段:
<display-name>"Struts Tags"</display-name> <tlib-version>2.2</tlib-version> <!-- 标签库默认的短名 --> <short-name>s</short-name> <!-- 标签库默认的 URI --> <uri>/struts-tags</uri>
<uri> 元素是该标签库唯一的标识
在 JSP 中 使用 Struts 2 标签必须导入标签库 :
<%@taglib prefix="s" uri="/struts-tags"%>
2.2 Struts 2 的 OGNL 表达式语言
Struts 2 利用内建的 OGNL (Object Graph Navigation Language ) 表达式语言支持,大大加强了 Struts 2 的数据访问功能,XWork 在原有的 OGNL 的基础上,增加了对 ValueStack 支持
在传统的 OGNL 表达式求值中,系统会假设系统只有一个“根”对象,下面是标准 OGNL 表达式求值,如果系统的 Stack Context 中包含 2 个对象: foo 对象,它在 Context 中的名字为 foo ; bar 对象,它在 Context 中的名字是 bar 。并将 foo 对象设置成 Context 的根对象
//返回 foo.geetBlah() 方法的返回值
#foo.blah
//返回 bar.getBlah() 方法的返回值
#bar.blah
//因为 foo 是根对象,所以默认是取得 foo 对象的 blah 属性,
//即返回 foo.getBlah() 方法的返回值
blah
如有语法:
#bar.foo.blah
上面代码意味着返回 bar.getFoo().getBlah() 方法的返回值。
Struts 2 提供一个特殊的 OGNL PropertyAccessor (属性访问器),它可以自动搜寻 Stack Context 的所有实体(从上到下) ,直到找到与求值表达式匹配的属性。
例如 Stack Context 中包括 2 个根实例: Animal 和 Person ,这 2 个实例中包含 "name " 属性,而且 Animal 还有一个 "species " 属性,而 Person 有一个 "salary " 属性,其中 Animal 是栈顶元素,Person 在其后面。如下:
//返回 animal.getSpecies() 返回值
species
//返回 person.getSalary() 返回值
salary
//返回 animal.getName() 返回值,因为 Struts 2 先找到 Animal 实例
name
//如果要取得 Person 实例的 name 属性,必须如下
person.name
除此之外,还可以通过索引 来访问 Stack Context 中的对象:
//返回 animal.getName() 返回值,因为从第一个开始找,就会先找到 Animal 实例
[0].name
//返回 person.getName() 返回值,因为从第二个开始找,就会先找到 Person 实例
[1].name
上面使用索引的方式并不是直接取得指定元素,而是从指定索引开始向下搜索
重要: Struts 2 使用标准的 Context 来进行 OGNL 表达式语言求值,OGNL 的顶级对象是一个 Context ,这个 Context 对象就是一个 Map 类型实例,其根对象就是 ValueStack ,如果需要访问 ValueStack 里的属性,可用如下方法:
#取得 ValueStack 中的 bar 属性
${bar}
Struts 2 还提供了一些命名对象,这些命名对象与根无关,它们只是存在与 Stack Context 中。所以,访问这些对象时需要使用 # 前缀来指明
● parameters : 用于访问 HTTP 请求参数。如:#parameters['foo'] 或 #parameters.foo , 用于返回调用 HttpServletRequest 的 getParameter("foo") 方法的返回值
● request : 用于访问 HttpServletRequest 的属性,如:#request['foo'] 或 #request.foo ,用于返回调用 HttpServletRequest 的 getAttribute("foo") 方法的返回值
● session : 用于访问 HttpSession 的属性。如:#session['foo'] 或 #session.foo ,用于返回调用 HttpSession 的 getAttribute("foo") 方法的返回值
● application : 用于访问 ServletContext 的属性。如:#application['foo'] 或 #application.foo ,用于返回调用 ServletContext 的 getAttribute("foo") 方法的返回值
● attr : 该对象将依次搜索 PageContext 、HttpServletRequest 、HttpSession 、ServletContext 对象中的属性
注意: 当系统创建了 Action 实例后,该 Action 实例已经被保存到 ValueStack 中,所以无需书写 # 即可访问 Action 属性
注意: 可以在任意页面增加 <s:debug/> 标签,该标签生成一个链接,可以查看 ValueStack 和 Stack Context 里的数据。该标签用于辅助开发者进行调试
2.3 OGNL 中的集合操作
使用 OGNL 表达式可以之间创建集合对象。
List 创建:
{e1,e2,e3}
Map 创建:
#{key1:value1,key2:value2}
对于集合,OGNL 提供了 2 个元素符: in 和 not in
i n : 判断某个元素是否在指定集合中
not in : 判断某个元素是否不在指定集合中
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!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>
<s:if test="'foo' in {'foo','bar'}">包含 'foo'</s:if>
<s:else>不包含 'foo'</s:else>
<br/>
<s:if test="'foo' not in {'foo','bar'}">包含 'foo'</s:if>
<s:else>不包含 'foo'</s:else>
</body>
</html>
除此之外,OGNL 还允许通过某个规则取得集合的子集, 3 个操作符 :
● ? : 取出所有符合选择逻辑的元素
● ^ : 取出符合选择逻辑的第一个元素
● $ : 取出符合选择逻辑的最后一个元素
如下:
person.relatives.{? #this.gender == 'male'}
上面代码,.{} 运算符表明用于取出该集合的子集 ,在 {} 内使用 ? 表明取出所有符合选择逻辑的元素 ,而 #this 代表集合里元素 。
上面代码含义: 取出 person 的所有性别为 male 的 relatives (亲戚) 集合。
2.4 访问静态成员
OGNL 表达式还提供了一种访问静态成员 (包括调用静态方法 、访问静态 Field ) 的方式
Struts 2 默认关闭此功能 ,只允许通过 OGNL 访问静态 Field 。为了让 OGNL 访问 静态成员,要在 Struts 2 应用中把 struts.ognl.allowStaticMethodAccess 设置为 true
struts.xml 片段:
<constant name="struts.ognl.allowStaticMethodAccess" value="true" />
设置了以上常量,OGNL 可以通过如下语法访问静态成员:
@className@staticField
@className@staticMethod(val...)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>使用Struts 2标签访问静态成员和静态方法</title>
<meta name="website" content="http://www.crazyit.org" />
</head>
<body>
访问系统环境变量:
<s:property value="@java.lang.System@getenv('JAVA_HOME')" />
<br />
圆周率的值:
<s:property value="@java.lang.Math@PI" />
</body>
</html>
2.5 Lambda(γ) 表达式
OGNL 支持基本的 Lambda(γ) 表达式语法,可以让 OGNL 语言使用一些简单的函数
假设有如下 :
if n ==0 return 0; elseif n==1 return 1; else return fib(n-2) + fib(n-1);
给定 fib(0) = 0 , fib(1) = 1 ,如果希望根据上面数列规则求 fib(11) 的值。
可以使用 OGNL :
<s:property value="#fib=:[#this==0 ? 0 : #this==1 ? 1 : #fib(#this-2) + #fib(#this-1)],#fib(11)"/>
#fib=:[#this==0 ? 0 : #this==1 ? 1 : #fib(#this-2) + #fib(#this-1)] 表示定义一个简单函数,上面可输出 fib(11) 的值