一、初识Struts2
Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。许多框架在大家一开始学习的时候都觉得比较繁琐和多此一举,但大体都有相同的目的,那就是增强可扩展性。Struts2的核心其实就是通过改配置文件的方式将请求和视图(结果)分开。
1.1 开发环境搭建
首先下载Struts2,地址http://struts.apache.org/,我这里下载的版本是2.5.10.1,解压之后有如下4个文件夹
所需的基本jar包有以下9个。struts2-core是开发的核心类库,struts2的UI标签的模板使用freemarker编写,OGNL是对象图导航语言,通过它来读写对象属性。
1.2 Struts2配置文件
①web.xml文件
主要完成对StrutsPrepareAndExecuteFilter的配置,它的实质是一个过滤器,负责初始化整个Struts框架并且处理所有的请求。在2.5以及2.1.3之前的版本filter-class会不同,请自行查询官方文档,filter-name和url-pattern是默认写法,不建议修改。
<filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
②struts.xml文件
Struts2的核心配置文件就是struts.xml文件,在这个配置文件里面我们可以根据需要再包括其它一些配置文件。在通常的应用开发中,我们每个人来写不同的模块,每个人单独配置一个struts.xml文件,最后合并,这样也利于管理和维护。
struts.xml中包含全局属性、用户请求和相应Action之间的对应关系、Action可能用到的参数和返回结果以及各种拦截器的配置,具体将在以下几节中慢慢介绍。
struts.xml可以从解压过后的示例程序里复制,拷贝到工程的src目录下,注释或删除掉struts标签中的内容,来填写我们需要的配置。
③struts.properties(default.properties)
default.properties文件在struts2-core.jar中的org.apache.struts2包下,里面保存着许多Struts是的默认属性,如编码格式、是否启用开发模式等等。当要修改某些属性时,建议在struts2的xml配置文档中进行更改,格式如下面一行代码,而不建议自己新建一个struts.properties文件。
<constant name="" value=""></constant>
④struts-default.xml
此文件是struts2框架默认加载的配置文件,它定义了struts2一些核心bean和拦截器,它会自动包含到struts.xml文件中(实质是通过<package extends="struts-default">),并为我们提供了一些标准的配置。我们可以在struts2-core.jar中找到这个文件。
二、struts.xml配置
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <constant name="struts.ognl.allowStaticMethodAccess" value="true"/> <package name="user" namespace="/user" extends="struts-default"> <action name="user" class="com.dhcc.struts2.action.UserAction"> <result>/user_add_success.jsp</result> </action> </package> </struts>
2.1 配置文件的优先级
在struts2中一些配置(比如常量)可以同时在struts-default.xml(只读性),strtus-plguin.xml(只读性),struts.xml,struts.properties和web.xml文件中配置,它们的优先级逐步升高,即是说后面的配置会覆盖掉前面相同的配置。
以struts.i18n.encoding=UTF-8的配置为例进行说明:
在struts.xml配置形式如下:
<constant name="struts.i18n.encoding" value="gbk"></constant>
在struts.properties的配置形式如下:struts.i18n.encoding=UTF-8
在web.xml中配置如下:
<filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class> <init-param> <param-name>struts.i18n.encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter>
2.2 package配置
属性名 |
是否必须 |
说明 |
Name |
是 |
Package的唯一标识,不允许同名 |
Extends |
否 |
指定要继承的包 |
Namespace |
否 |
指定名称空间 |
Abstract |
否 |
声明包为抽象否 |
package元素的namespace属性及action的name属性,它们共同定义了action所映射到的实质文件。
namespace默认值“”,即不配置namespace属性,如果action不能进行完整路径匹配,则会来此namespace下进行匹配。namespace也可以配置成namespace="/"。它代表配置为项目的根。总结action的名称探索顺序:完全对应、逐步追溯到上级目录查找、"/"下查找、默认namespace下查找。
namespace引发的链接问题:当我们为action配置了namespace时,访问此action的形式总会是如下形式:.../webappname/xxx/yyy/ActionName.action,而当此action成功执行跳转到某个jsp页面时,如想在此jsp页面写链接,一定要写绝对路径,因为相对路径是相对.../webappname/xxx/yyy/,而如果以后我们修改了action的namespace时,相对路径又要变,所以链接不能写成相对路径。 可以在建立一个jsp文件时,加上如下内容:
<% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>
我们写绝对路径可以参此内容。还可以参<head>下的<base href="<%=basePath%>"> 来完成绝对路径的书写。
三、Action
3.1 新建一个Action
第一步,新建一个Class,继承ActionSupport ,ActionSupport实现了execute()方法。
package com.struts2.test; import com.opensymphony.xwork2.ActionSupport; public class UserAction extends ActionSupport { public String add() { return "success"; } public String del() { return "success"; } public String update() { return "success"; } public String query() { return "success"; } }
第二步,配置此Action,在struts.xml中加入如下内容:
<package name="user" extends="struts-default" namespace="/user"> <action name="addUser" class="com.asm.UserAction" method="add"> <result name="success">/user/addUser.jsp</result> </action> <action name="delUser" class="com.asm.UserAction" method="del"> <result name="success">/user/delUser.jsp</result> </action> <action name="updateUser" class="com.asm.UserAction" method="update"> <result name="success">/user/updateUser.jsp</result> </action> <action name="queryUser" class="com.asm.UserAction" method="query"> <result name="success">/user/queryUser.jsp</result> </action> </package>
上面的method方法的值来源于CRUDAction中方法的名字,这样当我们访问上面的每一个Action时,它实质是和method指定的方法关联上。如果没有为action指定class,默认就是ActionSupport类,如果没有为action指定method属性,则默认执行execute方法,如果没有指定result的name属性,默认值为success。
第三步,编写相应的jsp页面,在此略去crud文件夹下的四个跳转jsp页面(addSuccess.jsp等),重点是crud.jsp页面。内容如下:
<html> <% String path=request.getContextPath(); %> <body> <a href="<%=path %>/user/addUser.action">添加数据</a><br> <a href="<%=path %>/user/delUser.action">删除数据</a><br> <a href="<%=path %>/user/queryUser.action">查询数据</a><br> <a href="<%=path %>/user/updateUser.action">修改数据</a><br> </body> </html>
最后发布测试。
3.2 动态调用DMI
不使用method实现统一,我们在struts.xml中增加如下内容:
<action name="op" class="com.struts2.test.UserAction"> <result name="add">/user/addUser.jsp</result> <result name="del">/user/delUser.jsp</result> <result name="query">/user/queryUser.jsp</result> <result name="update">/user/updateUser.jsp</result> </action>
然后再在crud.jsp中定义如下链接:
<a href="<%=path %>/user/op!add.action">添加数据</a><br> <a href="<%=path %>/user/op!del.action">删除数据</a><br> <a href="<%=path %>/user/op!query.action">查询数据</a><br> <a href="<%=path %>/user/op!update.action">修改数据</a><br>
注意查看上面的链接地址,它们都是针对op这个action,然后再加地上“!+UserAction中相应的方法名”,最后再写上.action即可以访问到相应result的name指定的jsp。大家会发现跟上面不同的是,result的name不再都是SUCCESS,这样才能区分开要访问的页面,但千万不要忘记在UserAction中相应的方法也要返回add/del/query/update,而不是SUCCESS。如果不想使用动态方法调用,我们可以通过常量来关闭,即在struts.xml中增加如下配置:
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
3.3 通配符
为了使用通配符,只需要改写配置文件即可。将3.1节中的配置文件改为如下内容可达到相同的效果:
<package name="user" extends="struts-default" namespace="/user"> <action name="*User" class="com.asm.UserAction" method="{1}"> <result name="success">/crud/{1}User.jsp</result> </package>
当有.../addUser.action请求时,如果不能在当前应用中找到完全相同的addUser名字的Action时,通配符配置这时就起作用了。
其实如果我们有良好的编程命名习惯,所有的Action我们都只需要进行一次配置。举例:规定所有的Action类都用XXXAction来命名,类中所有的CRUD方法都用add/del/update/query。Jsp页面也用add/del/update/query_XXX.jsp这样的形式。即配置文件可以写成如下形式:
<action name="*_*" class="com.struts2.test.{2}Action" method="{1}"> <result name="success">.../{1}_{2}.jsp</result> </action>
name中第一个*代表CRUD操作的名字,第二个*代表类的名字。所以访问链接地址举例如:.../del_User.action将访问到UserAction类的del方法,成功后跳到del_User.jsp页面。说明{0}是代表name中所有的*组合。
3.4 接收参数
①Action属性接收参数
UserAction中建两个属性name和age,并且要生成相应的get/set方法。
public class UserAction extends ActionSupport { private String name; private int age;public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } }
在传参的jsp页面,有一个表单
<form action="<%=request.getContextPath()%>/addUser.action" method="get"> 名字:<input type="text" name="name"><br> 年龄:<input type="text" name="age"><br> <input type="submit" value="login"> </form>
这样name和age就能接收到传入的值。需要注意的是,传参参照的action中的方法名,而非属性名。
②DomainModel接收参数
UserAction中有一个域模型private User user,注意不要自己new对象,User类中有name和age属性和对应的get/set方法。UserAction中要生成User对象对应的get/set方法。
访问http://.../user/user!add?user.name=a&user.age=8 即可对user赋值,相当于调用了user的set方法。
public class UserAction extends ActionSupport { private User user;public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
- 如果传入的参数个数和域模型的属性个数不同,可以用DTO(Data Transfer Object)。比如传入的参数还有一个isAdmin,那么我们建一个UserDTO,包含name,age和isAdmin三个属性,用UserDTO去接收参数,然后用UserDTO再生成相应的User
③ModelDriven接收参数
public class UserAction extends ActionSupport implements ModelDriven<User>{ //需要实现ModelDriven接口 private User user = new User(); //ModelDriven需要自己new @Override public User getModel() { return user; } }
3.5 访问Scope对象(request、session、application,HttpServletRequest、HttpSession、ServletContext)
①与Servlet解耦合的非IOC方式
public class LoginAction extends ActionSupport { ActionContext context; Map request; Map session; Map application; public String execute() throws Exception { context=ActionContext.getContext(); request=(Map) context.get("request"); session=context.getSession(); application=context.getApplication(); request.put("req", "requst属性"); session.put("ses", "sesion属性"); application.put("app", "application属性"); return SUCCESS; } }
②与Servlet解耦合的IOC方式
public class Login2Action extends ActionSupport implements RequestAware,SessionAware,ApplicationAware { //实现XxxAware接口,重写setXxx()方法 Map request; Map session; Map application; public String execute() throws Exception { request.put("req", "requst属性"); session.put("ses", "sesion属性"); application.put("app", "application属性"); return SUCCESS; } public void setRequest(Map<String, Object> request) { this.request=request; } public void setSession(Map<String, Object> session) { this.session=session; } public void setApplication(Map<String, Object> application) { this.application=application; } }
③与Servlet耦合的非IOC方式
public class Login3Action extends ActionSupport { //获取的纯粹的Scope对象,它与容器相关 HttpServletRequest request; HttpSession session; ServletContext application; public String execute() throws Exception { request = ServletActionContext.getRequest(); session = request.getSession(); application = ServletActionContext.getServletContext(); request.setAttribute("req", "requst属性"); session.setAttribute("ses", "sesion属性"); application.setAttribute("app", "application属性"); return SUCCESS; } }
④与Servlet耦合的IOC方式
public class Login4Action extends ActionSupport implements ServletRequestAware,ServletContextAware{ //实现XxxAware接口,重写SetXxx()方法 ActionContext context; HttpServletRequest request; HttpSession session; ServletContext application; public String execute() throws Exception { context=ActionContext.getContext(); session=request.getSession(); request.setAttribute("req", "requst属性"); session.setAttribute("ses", "sesion属性"); application.setAttribute("app", "application属性"); return SUCCESS; } public void setServletRequest(HttpServletRequest request) { System.out.println("测试:"+request); this.request=request; } public void setServletContext(ServletContext application) { System.out.println("测试:"+application); this.application=application; } }
之后可以在jsp中使用EL表达式${requestScope.req}或通过request.getAttribute这样的方式获取对象值
3.6 default-action-ref 配置统一访问
当访问没有找到对应的action时,默认就会调用default-action-ref指定的action。在struts.xml文件的package中增加如下内容:
<default-action-ref name="error"></default-action-ref> <action name="error"> <result>/other/error.jsp</result> </action>
上面一段内容就是说当我们访问的action不能被找到时便指向名为error的action中去,接着我们在下面配置了这个error Action。但是要注意,一个package内只配置一个<default-action-ref>,如果配置多个,就无法预测结果了。此时我们只要输入.../myStruts2/luanFangWen.action这样的形式,它就会去访问这个默认的<default-action-ref>,通常我们会为它配置一个错误页面,以提示用户访问的页面不存在。 在web开发中,我们还可以把这个默认的action访问配置成主页,这样当用户访问一些不存在的action时,总会跳到主页上去。
通过此配置,只要是访问一个不存在的action便会转向到.../other目录下的error.jsp页面。但是如果访问是其它的不存在资源则仍是报tomcat所标识的404错误,我们可以在web.xml中作如下配置:
<error-page> <error-code>404</error-code> <location>/other/404error.jsp</location> </error-page>
四、Result配置
4.1 type类型
在前面的许多案例中我们所用到的Action基本都继承自ActionSupport这个类,而在这个类中我们定义了五个字段:SUCCESS,NONE,ERROR,INPUT,LOGING。我们可以直接返回这些字段值,这些字段值实质是被定义成:String SUCCESS=”success”这样的形式,所以我们只要在Result元素中用它们的小写即可。
<result name="success" type="dispatcher"> <param name="location">/default.jsp</param> </result>
如果我们都采用默认的形式,最终可以简写成:<result>/default.jsp</result>
Type类型值 |
作用说明 |
chain |
用来处理Action链 |
dispatcher |
用来转向页面,通常处理JSP |
redirect |
重定向到一个URL |
redirectAction |
重定向到一个Action |
plainText |
显示源文件内容,如文件源码 |
freemarker |
处理FreeMarker模板 |
httpheader |
控制特殊http行为的结果类型 |
stream
|
向浏览器发送InputSream对象,通常用来处理文件下载,还可用于返回AJAX数据。 |
velocity |
处理Velocity模板 |
xslt |
处理XML/XLST模板 |
json |
序列化action为json |
当一个Action处理后要返回的Result是另一个Action,就需要使用chain和redirectAction。以chain为例,它的param有4个值可配,actionName(默认)、namespace、method和skipActions。namesapace的默认值当前namespace,可以省略不写。method用于指定转向到一个目标action所调用的方法,默认是调用下一个action的execute方法,所以也可以省略。SkipActions是一个可选的属性,一般不用。
<package name="public" extends="struts-default"> <!-- Chain creatAccount to login, using the default parameter --> <action name="createAccount" class="..."> <result type="chain">login</result> </action> <action name="login" class="..."> <!-- Chain to another namespace --> <result type="chain"> <param name="actionName">dashboard</param> <param name="namespace">/secure</param> </result> </action> </package> <package name="secure" extends="struts-default" namespace="/secure"> <action name="dashboard" class="..."> <result>dashboard.jsp</result> </action> </package>
注意:如果result中指定type类型为redirect,要想传递参数可以在result指向的jsp页面中附加参数即可,我们可以在test2 action的result中写成:
<result name="success" type="redirect">/test2Suc.jsp?username=${username}</result>
随后在test2Suc.jsp页面中引用时会出现三个问题:
1.EL表达式引用失效,(EL表达式应该使用${param.username}形式)。我们也可以使用<%=request.getParameter("username")%>获取参数值。
2.由于在前面的TestAction中设定的值为中文,而附加到这里的uri请求的参数后面时会出现乱码问题。(可以使用URI编码再解码解决此问题)
3.值栈取值失效:因为每一次request共享同一个值栈,所以服务器端的forward跳转也是能共享同一值栈得。但是当redirect重定向到test2Suc.jsp页面,这时其实就是重发的一次request,所以前一个action保存的值栈内容全部失效。这也就是为什么我们要附加参数的原因。
4.2 global-results(全局result)
如果我们所有的action均有可能跳到相同的页面,则不防使用全局result。为了方便引用我们专门建立一个package来存放公共的result。在会用到个全局的跳转时,只需要继承自这个公共的package即可。建立公共包,代码如下:
<package name="pubResult" extends="struts-default" abstract="true"> <global-results> <result name="error">/error.jsp</result> </global-results> </package>
由于它下面没的action配置,所以我们可以像默认的struts-default包一样,声明abstract=true,这样声明表示此packgage下不会有action,它一般是用来让别的package继承。随后再在要用到全局result中引用这个公共的package。代码如下:
<package name="testGlobal" extends="pubResult" > <action name="error" class="com.struts2.ErrorAction"></action> <action name="error2" class="com.struts2.Error2Action"></action> </package>
这样操作相当于把全局的result加到了此package下的所有action中去。
4.3 动态result(了解)
action中的变量r,设置了get/set方法,在struts.xml中使用${r}根据不同的值对应到不同的result。
public class DynaAction extends ActionSupport { private String username; private String nextAction; public String execute() throws Exception { if (username.equals("admin")) { nextAction = "admin"; } else if (username.equals("user")) { nextAction = "user"; } else { nextAction = ERROR; } return SUCCESS; } ...省略get/set方法 }
<package name="dynaTest" extends="pubResult"> <action name="dyna" class="com.asm.DynaAction"> <result name="success" type="chain">${nextAction}</result> </action> <action name="admin" > <result>/admin.jsp</result> </action> <action name="user"> <result>/user.jsp</result> </action> </package>
当把参数传递到DynaAction中去时,如果传递的值为admin,我们便设定了nextAction的值admin,在配置文件中我们通过${nextAction}(用在struts配置文件中的ognl,其实nextAction的值是存在值栈中,我们通过${}这样的形式取出,在此只作了解,后面的章节详细介绍)来获取值便为admin,随后再继续把请求传递到下一个Action中去(此时也即admin.action),为了方便我们设定了两个ForwardAction:admin.action和user.action。这样便可以跳到指定的jsp页面。 原理:dyna.action执行后会继续把请求传递给下一个Action,而下一个Action的到底是哪一个Action,是通过DynaAction中动态指定的,比如这里是根据传递的username的值指定。
五、ValueStack(值栈)
1.ValueStack是一个接口,在struts2中使用OGNL(Object-Graph Navigation Language)表达式实际上是使用实现了ValueStack接口的类OgnlValueStack.它是ValueStack的默认实现类.
2.ValueStack贯穿整个action的生命周期,每一个action实例都拥有一个ValueStack对象,其中保存了当前action对象和其他相关对象.
3.struts2把ValueStack对象保存在名为:struts.valueStack的request域中.即ValueStack作用域为request.当action创建的时候,ValueStack就创建了,action被销毁的时候,ValueStack就销毁了
4.ValueStack中的数据分两部分存放:root(对象栈,也称Object Stack,CompoundRoot)和context(Map栈,OgnlContext),Struts将把动作和相关对象压入Object Stack,而把各种各样的映射关系压入Context Map。
- 其中的root对象是CompoundRoot,CompoundRoot继承了ArrayList,提供了额外的方法:push(),和pop()方法,用来对root对象中所包含的数据进行存取。正是由于这两个方法,CompoundRoot变成了一个栈结构。struts2中,一个请求在最终到达Action的方法之前,Action对象本身会被压入ValueStack(实际上就是放到ValueStack的CompoundRoot中),所以action对象是CompoundRoot中的一个元素。
- 其中的context对象是OgnlContext,它实现了map接口,在valuestack的默认实现类中,即OgnlValueStack类中,调用ongl中的方法:Ognl.createDefaultContext(..)给context赋值,查看源代码可知,此方法返回的是一个OgnlContext对象。
5.获取valueStack的三种方法:
ValueStack v1 = ActionContext.getContext().getValueStack(); ValueStack v2 = ServletActionContext.getValueStack(ServletActionContext.getRequest()); ValueStack v3 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
6.ValueStack内存结构图
在jsp中使用struts的<s:debug/>标签可以在页面上生成一个超链接,通过该链接可以查看ValueStack和Stack Context 中的所有值信息,如下图(Stack Context下面没有截全,大家自己动手试一下看一看):
7.常用的存储数据的方法
⑴向对象栈中存数据,即ValueStack中的root(CompoundRoot)对象
* 先得到root,再把数据压入到root中,这中方式是放入到栈底.
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.getRoot().add(new Person());
* 先得到root,利用add(index,Object)把一个对象压入到root中指定位置.
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.getRoot().add(0,new Person());//这里0,表示压入栈顶.
* 存放一个map到对象栈中
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.set("msg","dude"); //先封装成Map,在把Map放入到对象栈中,且放入栈顶.
* 利用valueStack的push方法把一个对象直接压入栈顶
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.push(new Person());
⑵从值栈中获取数据
* 通过request,session等向map中存储数据
ActionContext.getContext().getValueStack().peek();
ActionContext.getContext().getValueStack().getRoot().get(0);
⑶向map栈中存数据,即ValueStack中的context(OgnlContext)
* 通过request,session等向map中存储数据
ServletActionContext.getRequest().setAttribute("username","joey");
ServletActionContext.getSession().put("pwd","123");
* 直接向map栈中放数据
ActionContext.getContext().put("msg","how you doing");
8.利用OGNL表达式取ValueStack中的数据
(1)<s:property />:取栈顶的元素。取Stack Context中的值时,应加#,如<s:property value=“#parameter.t” />
(2)<s:iterator />:取栈顶的元素。此标签的value属性值无论来自对象栈还是Map栈,都可以不加#前缀(<s:select/>标签也适用),因为此标签在迭代的时候,总是把当前正在迭代的元素放入到栈顶。value可以不写,默认是迭代栈顶元素。
六、OGNL(Object Graphic Navigatinon Language)对象图导航语言
OGNL是Struts2中使用的一种表达式语言,它可以用于JSP的标签库中,以便能够方便的访问各种对象的属性;它用于界面将参数传递到Action(并进行类型转换)中,还可以用于struts2的配置文件中。
下面用一个例子来谈ognl的定义,在这个例子中,我们的LoginAction中有一个User对象,而在User对象中又有一个Address对象,这些对象之间依靠这种类的字段进行关联,或者说是依靠字段属性进行导航,
(1)值栈中Action的普通属性:<s:property value="username" />
(2)值栈中Action的普通方法:<s:property value="方法()" />
(2)值栈中对象的普通属性:<s:property value="user.age" /> //如果自己不new,user.xxx只有传入值才会构造user,需要参数为空的构造函数
<s:property value="user.address.city" />
(3)值栈中对象的普通方法:<s:property value="user.方法()" />
<s:property value="user.name.length()" />
(4)静态方法: <s:property value="@全类名@方法()" /> //在某些版本中,struts.ognl.allowStaticMethodAccess的默认值为false,我们只需在struts.xml中增加如下内容:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
(5)静态属性: <s:property value="@全类名@属性" />
(6)默认类Math的访问:<s:property value="@java.lang.Math@min(1,2)"/><br>
<s:property value="@@min(1,2)"/><br>
<s:property value="@@PI"/> //因为是默认的类,所以可以省略类名
(7)普通类的构造方法:<s:property value="new com.dhcc.vo.Student('jack','20','85.5')"/><br> //只new出对象,显示的时候其实是调用对象的toString方法
<s:property value="new com.dhcc.vo.Student('jack','20','85.5').name"/>
(8)集合对象:
访问list:<s:property value="users"/> //[user1,user2,user3]
访问list中某个元素:<s:property value="users[0]"/>
访问list中某个属性的集合:<s:property value="users.{age}"/>
访问list中某个属性的集合的特定值:<s:property value="users[0].age"/> | <s:property value="users.{age}[0]"/>
访问set:<s:property value="dogs"/>
访问set中某个元素:set无序,不能取下标
访问map:<s:property value="dogMap"/> //{dog1:d1,dog2:d2}
访问map中某个元素:<s:property value="dogMap.dog1"/> 或者 <s:property value="dogMap['dog1']"/>
访问map中所有键:<s:property value="dogMap.keys"/><br>
访问map中所有值:<s:property value="dogMap.values"/><br>
访问容器的大小:<s:property value="users.size()"/><br> //不加()也可以
(9)投影:?#是指取出符合条件的所有Student对象,而^#是指取出符合条件的第一个对象,$#是指取出符合条件的最后一个对象。
选择年龄等于20的user信息:<s:property value="users.{?#this.age==20}"/><br>
选择年龄大于20的user名字信息:<s:property value="users.{?#this.age>20}.{name}"/><br>
选择年龄大于20的第一个user名字信息:<s:property value="studentList.{^#this.grade>60}.{name}"/>
<s:property value="users.{?#this.age>20}.{name}[0]"/><br>
选择年龄大于20的最后一个user名字信息:<s:property value="studentList.{&#this.grade>60}.{name}"/><br>
(10)N语法top语法
N语法[0]:<s:property value="[0]"/><br> //[com.dhcc.struts2.action.UserAction@6a35f7e3, com.opensymphony.xwork2.DefaultTextProvider@6239dff6]
N语法[1]:<s:property value="[1]"/><br> //[com.opensymphony.xwork2.DefaultTextProvider@6239dff6]
N语法[0].top:<s:property value="[0].top"/><br> //com.dhcc.struts2.action.UserAction@6a35f7e3
N语法[1].top:<s:property value="[1].top"/><br> //com.opensymphony.xwork2.DefaultTextProvider@6239dff6
N语法top:<s:property value="top"/><br> //com.dhcc.struts2.action.UserAction@6a35f7e3
N语法取值:<s:property value="[0].user.username"/><br>
N语法取值:<s:property value="top.user.username"/><br>
说明:规定栈顶的对象为[0],而我们只使用[0]的意思是从值栈中第一个对象取,一直取至栈底。N的意思是从值栈中的第N个对象开始,取到栈底为止。如果要想访问某个对象,需要使用[N].top,纯top可以简洁地取出值栈中的栈顶对象。当我们通过chain链访问时,值栈中可能有两个以上的Action对象。
(11)获取Stack Context中的信息
除了可以从值栈中获取信息,还可以从Stack Context中获取信息,只是要加上#,因为这些对象都是存在一般的Context Map中,而不是存在值栈中。
名称 |
作用 |
例子 |
parameters |
包含当前HTTP请求参数的Map |
#parameters.id[0]作用相当于request.getParameter("id") |
request |
包含当前HttpServletRequest的属性(attribute)的Map |
#request.userName相当于request.getAttribute("userName") |
session |
包含当前HttpSession的属性(attribute)的Map |
#session.userName相当于session.getAttribute("userName") |
application |
包含当前应用的ServletContext的属性(attribute)的Map |
#application.userName相当于application.getAttribute("userName") |
Attr |
用于按request > session > application顺序访问其属性 |
#application.userName相当于application.getAttribute("userName") |
(12)OGNL总结
OGNL有一个上下文的概念,这个上下文件实质就是一个Map结构,它实现了java.utils.Map接口,在struts2中上下文的实现为ActionContext,下面是上下文的结构示意图:
当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action。然后把action存放进ValueStack,所以action的实例变量可以接受OGNL访问。
访问上下文中的对象需要使用#号标注命名空间,如#application、#session。另外OGNL会设定一个根对象,在struts2中根对象就是ValueStack值栈对象,如果要访问根对象中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。在struts2中,根对象的实现类为OgnlValueStack,该对象不是我们想象的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型的变量,就是使用这个List变量来存放一组对象。在root变量(List类型)中处于第一位的对象叫栈顶对象,通常我们在Ognl表达式里直接写上属性的名称即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下寻找。 注意:struts2中 ,OGNL表达式需要配合struts的标签才可以使用。
七、struts标签
7.1通用标签
添加struts标签库:<%@ taglib uri="/struts-tags" prefix="s" %>
①<s:property>
default:可选属性,如果需要输出的属性值为null,则显示属性指定的值
escapeHtml:可选属性,指定是否格式化html代码。类似的还有escapeCsv和escapeXml
value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值
<s:property value="username" default="管理员" /><br/> <s:property value="''<hr/>>" escapeHtml="false" />
②<s:set> 此标签主要用于设置一些属性值
scope:指定变量被设置的范围,该属性可以接受application、session、request、page或action。如果没有设置该属性,则默认放置在OGNL Context中,我们可以通过#号来引用。
value:赋给变量的值,如果没有设置该属性,则将ValueStack栈顶的值赋给变量。
var:属性的引用名称,id/name均过时。此名称用于引用push到ValueStack的值。
<s:set var="UserName" value="user.name"/> Hello, <s:property value="#UserName"/> <s:set var="janesName">Jane Doe</s:set> <s:property value="#janesName"/>