ErBing

往事已经定格,未来还要继续。

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

 

一、初识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是开发的核心类库,struts2UI标签的模板使用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;
    }
}
依赖注入与控制反转DI/IOC

  之后可以在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>
example

  注意:如果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方法    
}
action示例
<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>
struts.xml示例

  当把参数传递到DynaAction中去时,如果传递的值为admin,我们便设定了nextAction的值admin,在配置文件中我们通过${nextAction}(用在struts配置文件中的ognl,其实nextAction的值是存在值栈中,我们通过${}这样的形式取出,在此只作了解,后面的章节详细介绍)来获取值便为admin,随后再继续把请求传递到下一个Action中去(此时也即admin.action),为了方便我们设定了两个ForwardActionadmin.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:指定变量被设置的范围,该属性可以接受applicationsessionrequestpageaction。如果没有设置该属性,则默认放置在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"/>

 

posted on 2017-06-30 13:54  ErBing  阅读(814)  评论(0编辑  收藏  举报