文章目录
Struts2 标签的官方在线说明文档: https://struts.apache.org/tag-developers/tag-reference.html
Struts2 标签是干什么的
Struts2 自己定义的标记库,专门用于访问 ValueStack,查看 ValueStack 中的内容。
使用 Struts 标签之前,在 JSP 中需要添加相关的指令来导入标签库:
<%@ taglib uri="/struts-tags" prefix="s" %>
Struts2 标签可以使用在哪里
Struts2 标签都是结合 OGNL 表达式一起使用的。
Struts2 标签只能用于 JSP 文件中,不可以在 XML 和 HTML 文件中使用。
Struts2 标签中不能使用 EL 表达式。
关于标签的属性
除了 Struts2 标记库自定义的属性,其它所有 html 的属性都可以使用,比如 name、value 等,但是有 2 个属性名字做了改动:
1.class 属性 在 Struts2 中为 cssClass
2.style 属性 在 Struts2 中位 cssStyle
常用的标签
s:property
该标签表示访问 ValueStack 获取指定属性的值并输出到页面中。
<s:property value="name" escape="true" default=""/>
value: 该属性的值是 OGNL 表达式,表示从 OGNL 表达式中取出数据
escape: 表示 value 中从“name”取出的数据是否是转义的,默认是转义的。
default: 如果 OGNL 表达式中取不到值,就显示 default 的值
该标签的底层是执行如下的代码:
String name = Ognl.getValue("name", context, root).toString();
out.print(name);
OGNL 表达式中以 #
开头,表示访问 ValueStack
中的 context
对象,获取 name
属性值,并输出到页面中。
<s:property value="#name"/>
如果省略 value 属性,则默认将 root 栈顶的第一个对象取出,直接输出到页面中,参考如下代码:
<%@ page import="java.util.*" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>
<s:property/>
</h2>
</body>
</html>
访问 JSP 文件得到如下的结果:
value 属性值中还可以进行字符串拼接:
<s:property value="name + 'java'" />
先从 ValueStack 对象中获取 name 的属性值,然后和字符串“java”拼接,最后输出到页面中。
value 属性值中可以调用方法:
<s:property value="arry[1].toUpperCase()" />
先从 ValueStack 对象中获取 arry 的属性值,其实是一个字符串数组,接着获取下标索引为 1 的元素,接着调用该元素对象的 toUpperCase() 方法,最后将结果值输出到页面中。
调用方法
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>property标签</title>
</head>
<body>
<h2>
Struts2 Tag - 非UI标签
</h2>
<%--到ValueStack中的root区查找含有方法format的对象,找到后调用对象的format方法。其中的参数date,会被解析成去调用
对象的getDate方法,将方法的返回值作为参数传入方法format中--%>
<s:property value="format(date,'yyyy-MM-dd E')" />
</body>
</html>
s:iterator
<s:iterator value="projects" var="pro">
“projects” 是 ognl 表达式,底层是执行下面的代码来获取到对应的属性值:
List projectList = (List)Ognl.getValue("projects",root);// root是CompoundRoot对象,其实就是一个集合对象,数据的存取遵循“栈”规则
OGNL 引擎从 root 的栈顶开始查找含有 projects 属性的对象,找到后就不会继续往下查找,然后调对象的 getProjects()
方法获取到对应的属性值,其实就是一个集合对象。
接着开始遍历集合 projects,循环取出集合中的元素。将取出的元素(Project 对象)压入 root 栈顶,同时以 pro 为 key 值存放在 context 对象中(这是 ValueStack 的另一个属性,是 Map 结构),那么要访问 context 中的这个元素,ognl 表达式的格式为 #pro
。不仅如此,还会以 pro 为属性名将取出的元素绑定到 PageContex 对象中。
当取出集合中的第二个元素时会将之前压入 root 栈顶的对象先弹出再压入 root 的栈顶,同时又以 pro 为 key 值将第二个元素存入 context 中,那么之前的就会被覆盖掉。
这些数据状态可以通过 <s:debug/>
标签来记录获知!!
我们一起来分析下面的标签语句:
<s:iterator status="stus" value="projects" var="pro">
<s:property value="#stus.count"/>
<s:property value="name"/>
<s:property value="#pro.name"/>
${pro.name}
${name}
</s:iterator>
属性 status 用来指定一个状态对象,该对象有以下的属性:
index:被遍历元素的下标
count:统计遍历的次数
first:是否为第一个元素
even(偶数):判断count是否偶数
odd(奇数):判断count是否奇数
modulus(n)方法: count 除以 n 后取余
其中的 status 属性就是用来指定状态对象的,其值就是 ognl 表达式,可以找到存储在 context 对象中的状态对象,所以访问状态对象的相关属性,需要加上 #
,例如:#stus.count
。
其中的 value 属性用来指定被遍历的集合对象,其值就是 ognl 表达式,通过该表达式可以获取到被遍历的集合对象,实际是到 ValueStack 对象中的 root 对象中查找。
其中的 var="pro"
,表示迭代遍历的时候会以 pro 为绑定名将遍历出来的对象绑定到 PageContext 对象和 ValueStack 的 context 对象中。
其中的 <s:property>
标签的 value 属性值是 #stus.count
,表示到 ValueStack 对象中的 context 对象查找属性 stus,找到后再获取 count 属性值,标签 <s:property/>
就是将最终获取到值输出到页面中。
其中的 <s:property>
标签的 value 属性值是 name
,表示到 ValueStack 对象中的 root 对象查找属性 name,找到后获取 name 的值并输出到页面中。遍历处理的 Project 对象都会压入 root 栈顶,所以可以到 root 对象中获取 Project 对象的 name 属性值。
其中的 <s:property>
标签的 value 属性值是 #pro.name
,表示到 ValueStack 对象中的 context 对象查找属性 pro,找到后再获取 name 属性值,标签 <s:property/>
就是将最终获取到值输出到页面中。
其中的 ${pro.name}
el 表达式是去 PageContext 对象中找到绑定名为 pro 的对象,找到后获取该对象的 name 属性值,并输出到页面中。
其中的 ${name}
el 表达式是取 ValueStack 的 root 对象中查找 name 属性,找到后将属性值输出到页面中。这个特性是因为 Struts2 对 el 表达式进行了改进才得以实现的。
<s:iterator/>
标签对应于 jstl 的 <c:forEach/>
标签。
<s:iterator/>
标签的应用示例代码如下:
<s:iterator value="projects" var="project">
<tr>
<td>${id}</td>
<td>${name}</td>
<td>${startDate}</td>
<td>${endDate}</td>
</tr>
</s:iterator>
Action 对象有属性 projects,这个属性值是一个集合对象,从集合遍历出来的 project 对象会压入 ValueStack 的 root 对象中,所以可以通过 el 表达式直接获取 project 对象的属性值。
上述的 el 表达式也可以替换成 <s:property/>
标签,如下所示:
<s:iterator value="projects" var="project">
<tr>
<td><s:property value="id"/></td>
<td><s:property value="name"/></td>
<td><s:property value="startDate"/></td>
<td><s:property value="endDate"/></td>
</tr>
</s:iterator>
<s:property/>
标签就是去访问 ValueStack 对象,获取指定属性的值,并输出到页面中。
<c:forEach/>
标签的应用示例代码如下:
<c:forEach items="${projects}" var="project">
<tr>
<td>${project.id}</td>
<td>${project.name}</td>
<td>${project.startDate}</td>
<td>${project.endDate}</td>
</tr>
</c:forEach>
遍历Integer类型的List
<label>爱好:</label>
<s:iterator value="hobbies" status="st">
<%-- hobbies是一个Integer类型的List对象,st代表的是一个状态对象,其中的属性index
存储着遍历到元素的下标索引值,状态对象存储在ValueStack的context对象中,所以获取状态
对象需要在属性名称前面加上#--%>
<s:property value="hobbies[#st.index]"/>,
</s:iterator>
s:select
<s:select list="list" label="选择城市" name="city" value="01" listKey="id" listValue="name" headerKey="-1" headerValue="请选择..." theme="simple">
</s:select>
这个 Struts2 标签很有意思,最后生成1个下拉选择框,是根据 list 这个集合迭代生成各个 Option 对象,这个 Option 对象的 value 属性值等于集合取出来的对象的 id 属性值,Option 对象的 text 属性值等于集合取出来的对象的 name 属性值,并且这个下拉选择框的头 1 个Option 对象的 value 属性值等于 headerKey 的值,text 属性值等于 headerValue 的值。
属性 list 指定遍历的集合对象
属性 value 指定元素 select 默认提交的值
属性 label 指定显示的文本
属性 name 指定参数的名称
属性 listKey 对应每一个 option 提交的值,即作用于 option 的属性 value
属性 listValue 对应每一个 option 的显示文本,即作用于 option 的属性 text
属性 headerKey 提示头对应的提交值,即作用于第一个 option 的属性 value
属性 headerValue 提示头的文字,即作用于第一个 option 的属性 text
属性 theme 指定下拉选择框的显示样式,如果其值为 simple,则属性 label 的作用会失效
s:if
这个标签对应于 jstl 的 <c:choose>
标签。
Struts2 的 <s:if/>
标签的使用示例:
<s:if test="page > 1">
<a href="projectlist.action?page=${page-1}">上一页</a>
</s:if>
<s:else>
上一页
</s:else>
属性 test 的值是 ognl 表达式,表达式 page > 1
会先获取 page 的值,然后和 1 进行比较,成立则返回 true,test="true"
则会将 <s:if/>
标签体的内容输出到页面中。如果 test="false"
则会将 <s:else/>
标签体中的内容输出到页面中。
对应的 jstl 的 <c:choose>
标签的示例代码如下:
<c:choose>
<c:when test="${page gt 1}">
<a href="projectlist.action?page=${page-1}">上一页</a>
</c:when>
<c:otherwise>
上一页
</c:otherwise>
</c:choose>
s:form
<s:form action="login" method="post">
</s:form>
属性 action 的值会被完整显示,即【虚拟目录+命名空间+action名称】,并且名称后面会自动加后缀 .action
。特别注意,JSP 只有通过控制器转发,action 的值才会自动完整显示,如果你只是放在项目根目录下,浏览器可以直接访问,那么 action 的值你填写的是什么就显示什么。
最终会输出如下的 HTML 标签:
<form id="login" name="login" action="/sp/login.action" method="post">
<table class="wwFormTable">
</table>
</form>
在 <form/>
内会包含一个 <table/>
,所以这个标签都是结合 <s:textfield>
、<s:submit>
等标签一起使用的。
自动填写表单项的值
<s:form>
标签可以自动将 Action 对象的属性填写到页面对应的表单项中,不过密码是不被自动填写的。
s:textfield
<s:textfield label="账户名称" name="user.accountName"/>
该标签都是结合标签 <s:form/>
一起使用的,该标签会自动从 ValueStack 的 root 对象中获取 user.accountName 的值,然后赋值给 <input/>
的属性 value。
最终会输出如下的 HTML 标签:
<tr>
<td class="tdLabel">
<label for="user_accountName" class="label">
账户名称:
</label>
</td>
<td class="tdInput">
<input type="text" name="user.accountName" value="admin" id="user_accountName"/>
</td>
</tr>
s:include
在当前页面中包含页面 select-language.jsp
<s:include value="select-language.jsp"/>
s:submit
Struts2 的 submit 标签如果只定义了属性 key:
<s:submit key="submit"/>
会输出下面的标签内容:
<tr>
<td colspan="2">
<div class="formButton">
<input type="submit" id="login_submit" name="submit"/>
</div>
</td>
</tr>
<s:submit/>
的属性 key 的值只决定了生成的 HTML 标签的属性 name 的取值;属性 type 的值由 Struts2 标签名称决定;没有属性 value 默认显示“提交”。
<s:submit/>
的属性 key 与生成的 HTML 标签的属性 name 对应。
注:生成的 HTML 标签的属性 id 的值由表单 <form/>
的属性 action 的值连接上标签自身属性 name 的值。
如果只定义了属性 name:
<s:submit name="submit"/>
则会输出下面的HTML 标签:
<tr>
<td colspan="2">
<div class="formButton">
<input type="submit" value="Submit" id="login_submit" name="submit"/>
</div>
</td>
</tr>
<s:submit/>
的属性 name 的值决定了生成的 HTML 标签的属性 value、name 的取值。
如果只定义了 value 属性:
<s:submit value="提交"/>
则会输出下面的 HTML 标签:
<tr>
<td colspan="2">
<div class="formButton">
<input type="submit" value="提交" id="login_0"/>
</div>
</td>
</tr>
<s:submit/>
的属性 value 的值只决定了生成的 HTML 标签的属性 value 的取值。
总结:
<s:submit/>
标签不适合设置属性 name 的值,也不适合设置属性 key 的值,因为提交按钮不应该有属性 name,否则提交表单数据时,会将提交按钮的 name 作为请求参数传递给服务端,这样会出现问题。如下所示:
另外属性 key 和属性 name 也不建议使用中文。
s:checkbox
<s:form action="" method="post" theme="xhtml">
<s:iterator value="hobbies">
<s:checkbox label="%{label}" name="hobbies" fieldValue="%{value}"/>
</s:iterator>
<s:submit/>
</s:form>
上面的hobbies 是一个 List 对象,里面存储着 Hobby 对象,Hobby 对象有两个属性分别是tabel和value。
%{label}
表示从ValueStack的属性root中获取属性label的值
%{value}
表示从ValueStack的属性root中获取属性value的值
如果List 对象改成 Map 对象,应该这样获取键值对的值:
<s:iterator value="productOptions">
<s:checkbox label="%{key}" name="product" fieldValue="%{value}" />
</s:iterator>
productOptions 是一个 Map 对象
%{key}
表示获取Map的key
%{value}
表示获取对应的value
猜想:Struts2会将Map<String,Integer>
对象转换成Set<Entry<String,Integer>>
对象,再遍历Set对象,将内含的Entry对象置入到ValueStack的root对象中,再分别取出key值和value值,再赋值给checkbox对应的属性,从而逐一输出对应的checkbox元素。
s:radio
<s:form action="basic-information" method="post" theme="xhtml">
<s:radio list="hobbies" label="爱好" name="hobby"
listValue="label" listKey="value" />
<s:submit/>
</s:form>
list 属性: 指定需要遍历的集合
listValue 属性: 作用于每一个选项的提示(One 、Two、Three)
listKey 属性: 作用于每一个要提交的值(1、2、3)
listValue=“label” 表示到ValueStack中的root对象获取属性label的值
listKey=“value” 表示到ValueStack中的root对象获取属性value的值
上述的标签代码最后会生成如下的标准html代码:
<form id="basic-information" name="basic-information" action="/st/basic-information.action" method="post">
<table class="wwFormTable">
<tr>
<td class="tdLabel"><label for="basic-information_hobby" class="label">爱好::</label></td>
<td
class="tdInput"
><input type="radio" name="hobby" id="basic-information_hobby1" value="1"/><label
for="basic-information_hobby1">one</label>
<input type="radio" name="hobby" id="basic-information_hobby2" value="2"/><label
for="basic-information_hobby2">two</label>
<input type="radio" name="hobby" id="basic-information_hobby3" value="3"/><label
for="basic-information_hobby3">three</label>
</td>
</tr>
<tr>
<td colspan="2">
<div class="formButton"><input type="submit" value="Submit" id="basic-information_0"/>
</div>
</td>
</tr>
</table>
</form>
页面显示效果如下:
s:action
<s:action name="Two_2" executeResult="true" var="two"/>
<s:action/>
用来访问指定的 Action 组件。
name 指定 Action 的名称,也就是 struts.xml 中标签 Action 的属性 name 的值。
executeResult 为 true 则表示会执行结果组件,如果结果组件是一个页面,那么该页面就会嵌入到标签 <s:action/>
所在的页面中;值为 false 则不会执行结果组件。
有时候,我们需要在页面显示出 Action 取出的数据,所以设置属性 executeResult=“true”,
有些时候,我们会在页面调用 Action,但是不需要数据显示出来,设置为 false 即可。
var 指定 key 的名称,上述示例中 var=“two”,表示将 Action 对象作为 value,two 作为 key 存储到 ValueStack 对象中的属性 context 中,context 是 Map 的实例对象。
如果我们写了 var="xxx"属性,就会把 Action 放入 ValueStack 中,如果不写就不会放,默认不放。
因为 Action 对象被存储到 context 中,所以想要获取 Action 对象的属性的值,假设 TwoAction 有个名为 two 的属性,获取该属性的值可以这样写:
<s:property value="#two.two" />
示例代码
struts.xml的配置内容如下:
<action name="*_*" class="priv.lwx.struts2.tag.web.{1}Action">
<result name="success">
/WEB-INF/jsp/{2}.jsp
</result>
</action>
<!-- http://localhost:8080/st/3.action -->
<action name="3">
<result name="success">
WEB-INF/jsp/3.jsp
</result>
</action>
3.jsp 的源码如下:
<%@page pageEncoding="utf-8" %>
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title></title>
</head>
<body>
<h2>
Three
</h2>
<s:action name="Two_2" executeResult="true" />
</body>
</html>
2.jsp 的源码如下:
<%@page pageEncoding="utf-8" %>
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
<head><title></title>
</head>
<body>
<h1>
two
</h1>
<h2>
<s:property value="two"/>
</h2>
</body>
</html>
访问 http://localhost:8080/st/3.action,可以得到下图所示的页面内容:
上图的结果就是因为在 3.jsp 中访问了 TwoAction,而 TwoAction 执行完后转发到 2.jsp,因此最后的结果就是 2.jsp 嵌入到 3.jsp 中。
s:date
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>date标签</title>
</head>
<body>
<h2>
Struts2 Tag - 非UI标签
</h2>
<%--到ValueStack中的root区查找含有属性date的对象,找到后调用对象的getDate方法,获取属性date的值。
接着会去调用某个方法,并将获取到的属性date的值和格式字符串"yyyy-MM-dd E"作为参数传入,这个方法会将
日期按指定的格式拼接成一个字符串,并返回该字符串。接着将该字符串作为参数调用out.print(),从而将指定的
日期对象按指定的格式输出到页面中--%>
<s:date name="date" format="yyyy-MM-dd E"/>
</body>
</html>
s:url
<s:url action="Two_2" var="url" includeParams="get/none/all" value=""/>
action:会自动生成一个资源的请求路径(/st/Two_2.action)
var:则指定属性名,将地址存储到 ValueStack 的属性 context 中
includeParams:值可取 get/none/all。
值 all 表示自动将请求本页面的参数,加于本 url 之后
值 none 表示没有此功能(默认)
值 get 表示仅 get 请求本页面的参数,加于本 url 之后
value:如何访问的不是 Action,可以使用属性 value 指定请求地址。例如:
<s:url value="http://www.baidu.com" var="url2"/>
<a href="${url2}">click me</a>
示例代码
<%@page pageEncoding="utf-8" %>
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>URL标签的演示</title>
</head>
<body>
<h2>
<%--属性action会自动生成一个资源的请求路径,而属性var则指定属性名,将地址存储到ValueStack的
属性context中--%>
<s:url action="Two_2" var="url"/>
<%--EL表达式可以访问ValueStack的属性context,获取指定属性的值--%>
<a href="${url}">click me</a>
<s:debug/>
</h2>
</body>
</html>
s:set
<s:set var="" value="" scope="" />
从 ValueStack 中取出数据,放入指定的范围中。
scope:指定 4 个范围之一(pageContext、request、session、application)
value:指定 Ognl 表达式
var:指定属性名
示例代码
<%@page pageEncoding="utf-8" %>
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>URL标签的演示</title>
</head>
<body>
<h2>
<%--属性action会自动生成一个资源的请求路径,而属性var则指定属性名,将地址存储到ValueStack的
属性context中--%>
<s:url action="Two_2" var="url"/>
<%--EL表达式可以访问ValueStack的属性context,获取指定属性的值--%>
<a href="${url}">click me</a>
<hr/>
<%--从ValueStack中获取属性url的值,并以属性名url1将其存储到pageContext中--%>
<s:set var="url1" value="#url" scope="pageContext"/>
<a href="${url1}">click me</a>
<%-- <s:debug/>--%>
</h2>
</body>
</html>