Struts2 简介

Struts2 是一种流行的 Java模型 - 视图 - 控制器(MVC)框架。成功地结合了 WebWork和Struts1.x 两种 web 框架。Struts2是以WebWork为核心,采用拦截器机制对用户的请求进行处理。

从一个高水平角度看,Struts2 是一个MVC拉动的(或MVC2)框架,Struts2 的模型-视图-控制器模式是通过以下五个核心部分进行实现的:

  • 操作(Actions)
  • 拦截器(Interceptors)
  • 值栈(Value Stack)/OGNL
  • 结果(Result)/结果类型
  • 视图技术

而Struts2 与传统的MVC框架略有不同,因为它由Action扮演模型的角色,而不是控制器,虽然这样会有一些重叠。

Struts 2çæ¶æ

 

Struts2 配置文件

web.xml文件

这个文件为每个web应用程序提供接入点。在部署描述符(web.xml)中,Struts2 应用程序的接入点将会定义为一个过滤器。因此我们将在web.xml里定义一个FilterDispatcher类的接入点。

   <filter>
      <filter-name>struts2</filter-name>
      <filter-class>
         org.apache.struts2.dispatcher.FilterDispatcher
      </filter-class>
   </filter>

   <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>

 

 struts.xml文件

struts.xml中主要配置Struts项目的一些全局的属性,用户请求和响应Action之间的对应关系,以及配置Action中可能用到的参数,以及处理结果的返回页面。还包括各种拦截器的配置等。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <!-- 配置struts可以受理的请求的扩展名 -->
    <constant name="struts.action.extension" value="action,do,"></constant>
    <!--
        package:包,struts2使用package来组织模块
        name属性:必须,用于其他的包应用当前包
        extends:当前包继承哪个包,继承的,即可以继承其中的所有的配置,
                 通常情况下继承struts-default
        namespace:可选,如果它没有给出,则以“/”为默认值,若namespace有一个非默认值,
                   则要想调用这个包里的Action,就必须
                   把这个属性所定义的命名空间添加到有关的URI字符串里
    -->
    <package name="helloworld" extends="struts-default">
    <!--
            配置一个action:一个struts2的请求就是一个action
            name:对应一个struts2的请求的名字(或对一个servletPath,但去除/和扩展名),
                  不包含扩展名
            class:默认值为com.opensymphony.xwork2.ActionSupport
            method:默认值为execute
            result:结果,表示action方法执行后可能返回的一个结果,
                    代表action方法执行后,可能去的一个目的地,
                    所以一个action节点可能会有多个result子节点。
                    多个result子节点使用name来区分。
            name:标识一个result,和action方法的返回值对应,默认值为success
            type:表示结果的类型。默认值为dispatcher(转发到结果)。redirect(重定向),
                  但是重定向不能跳转到WEB-INF目录下。
        -->
    <!--
        ActionSupport是默认的Action类:若某个action节点没有配置class属性,则ActionSupport即为            
        待执行的Action类,而execute方法即为默认执行的action方法(该方法返回success字符串)
    --> 
    <action name="product-input">
        <result>/WEB-INF/pages/input.jsp</result> 
    </action>
        <!-- product-save请求调用Product类的save方法,方法返回值对应result的name-->
    <action name="product-save" class="com.struts2.helloworld.Product" method="test">
        <result name="details">/WEB-INF/pages/details.jsp</result>
    </action>
    </package>
</struts>

 

通配符匹配

<body>
    <a href="product-input.action">Product Input</a>
            
    <!-- 通配符匹配 -->
    <a href="UserAction-save?name=save">User Save</a>

    <a href="UserAction-update?name=update">User Update</a>

    <a href="UserAction-delete?name=delete">User Delete</a>

    <a href="UserAction-query?name=query">User Query</a>

</body>
    <!-- 利用通配符匹配 -->
    <action name="UserAciton-*" class="com.struts2.action.UserAciton" method="{1}">
        <result name="{1}-success">/success.jsp</result>
    </action>

 

struts提供了一种模块化struts.xml文件的方法,你可以将文件拆分为多个xml文件,并用以下方式导入它们。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
     <include file="my-struts1.xml"/>
     <include file="my-struts2.xml"/>
</struts>

 

struts-config.xml文件

struts-config.xml配置文件是Web Client中View和Model组件之间的链接,但在你99.99%的项目里你不必使用这些设置。 struts-config.xml配置文件包含以下主要元素:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">

<struts-config> <!-- 这是配置文件的根节点 -->

   <!-- 这是你将ActionForm子类映射到name的位置,你可以在struts-config.xml文件的其余部分,甚至 
        在JSP页面上,将这个name用作ActionForm的别名 -->
   <form-beans>
      <form-bean name="login" type="test.struts.LoginForm" />
   </form-beans>

   <!-- 此部分将你在webapp上的页面映射到name,你可以使用这个name来引用实际页面。这避免了对你网 
        页上的URL进行硬编码 -->
   <global-forwards>
   </global-forwards>

   <!-- 这是你声明表单处理程序的地方,也被称为操作映射(action mappings) -->
   <action-mappings>
      <action
         path="/login"
         type="test.struts.LoginAction" >

         <forward name="valid" path="/jsp/MainMenu.jsp" />
         <forward name="invalid" path="/jsp/LoginView.jsp" />
      </action>
   </action-mappings>

   <!-- 这部分是配置Struts的内部,在实际情况中很少使用 -->
   <controller 
      contentType="text/html;charset=UTF-8"
      debug="3"
      maxFileSize="1.618M"
      locale="true"
      nocache="true"/>

</struts-config>

 

struts.properties文件

这个配置文件提供了一种机制来改变框架的默认行为。实际上,struts.properties配置文件中包含的所有属性也可以在web.xml中配置使用init-param,以及在struts.xml配置文件中使用constant标签。

struts.properties文件中配置的值将覆盖default.properties中配置的默认值,这些值包含在struts2-core-x.y.z.jar分布中。有一些属性,你可以考虑改为使用struts.properties。

### When set to true, Struts will act much more friendly for developers
struts.devMode = true

### Enables reloading of internationalization files
struts.i18n.reload = true

### Enables reloading of XML configuration files
struts.configuration.xml.reload = true

### Sets the port that the server is run on
struts.url.http.port = 8080

 

Struts2 Actions动作

Actions是Struts2框架的核心,因为它们适用于任何MVC(Model View Controller)框架。 每个URL映射到特定的action,其提供处理来自用户的请求所需的处理逻辑。
但action还有另外两个重要的功能。 首先,action在将数据从请求传递到视图(无论是JSP还是其他类型的结果)方面起着重要作用。 第二,action必须协助框架确定哪个结果应该呈现在响应请求的视图中。

创建Action

Struts2中actions的唯一要求是必须有一个无参数方法返回String或Result对象,并且必须是POJO。如果没有指定no-argument方法,则默认是使用execute()方法。

ActionSupport类是默认的Action类,若某个action节点没有配置class属性,则ActionSupport类即为待执行的Action类,而类中的execute方法即为要默认执行的action方法。

在这个类中还默认封装了一些静态变量(同Action接口一样)

	* SUCCESS       -- 成功.
	* INPUT         -- 用于数据表单校验.如果校验失败,跳转INPUT视图.
	* LOGIN         -- 登录.
	* ERROR         -- 错误.
	* NONE          -- 页面不转向.

 这个类中的默认执行方法

public String input() throws Exception {
    return "input";
}

public String doDefault() throws Exception {
    return "success";
}

public String execute() throws Exception {
    return "success";
}

 

让我们来看看在Hello World示例中的action方法: 

public class UserAciton {
    
    public String test() {
        System.out.println("details...");
        return "details";
    }

    public String execute() throws Exception {
        return "success";
   }

    public String save() {
        System.out.println("save...");
        return "save-success";
    }
    
    public String update() {
        System.out.println("update...");
        return "update-success";
    }
    
    public String delete() {
        System.out.println("delete...");
        return "delete-success";
    }
    
    public String query() {
        System.out.println("query...");
        return "query-success";
    }
    
}

 

为了说明action方法控制视图的要点,让我们对execute方法进行以下更改,并扩展ActionSupport类如下:

public class HelloWorldAction extends ActionSupport{
   private String name;

   public String execute() throws Exception {
      if ("SECRET".equals(name))
      {
         return SUCCESS;
      }else{
         return ERROR;  
      }
   }
   
   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
}

 

在这个例子中,我们在execute方法中使用一些逻辑来查看name属性。如果属性等于字符串“SECRET”,我们返回SUCCESS作为结果,否则我们返回ERROR作为结果。因为我们已经扩展了ActionSupport,所以我们可以使用String常量、SUCCESS和ERROR。 现在,让我们修改struts.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
   "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
   "http://struts.apache.org/dtds/struts-2.0.dtd">
   <struts>
      <constant name="struts.devMode" value="true" />
      <package name="helloworld" extends="struts-default">
         <action name="hello" 
            class="cn.w3cschool.struts2.HelloWorldAction"
            method="execute">
            <result name="success">/HelloWorld.jsp</result>
            <result name="error">/AccessDenied.jsp</result>
         </action>
      </package>
</struts>

 

如果返回结果是SUCCESS将调用HelloWorld.jsp文件,如果action的结果是ERROR,即字符串常量为“error”,AccessDenied.jsp将被框架调用。

 

Struts2 拦截器

拦截器在概念上与servlet过滤器或JDK代理类相同。拦截器允许横切功能,把action以及框架分开实现。你可以使用拦截器实现以下操作:

  • 在调用action之前提供预处理逻辑。

  • 在调用action后提供后处理逻辑。

  • 捕获异常,以便可以执行备用处理。

Struts2框架中提供的许多功能都是使用拦截器实现的,包括异常处理,文件上传,生命周期回调和验证等。事实上,由于Struts2将其大部分功能基于拦截器,因此不太可能为每个action分配7个或8个拦截器。

Struts 2框架提供了一个良好的开箱即用的拦截器列表,这些拦截器预先配置好并可以使用。 下面列出了几个重要的拦截器:

序号拦截器和说明
1 alias

允许参数在请求之间使用不同的别名。

2 checkbox

通过为未检查的复选框添加参数值false,以辅助管理复选框。

3 conversionError

将字符串转换为参数类型的错误信息放置到action的错误字段中。

4 createSession

自动创建HTTP会话(如果尚不存在)。

5 debugging

为开发人员提供一些不同的调试屏幕。

6 execAndWait

当action在后台执行时,将用户发送到中间的等待页面。

7 exception

映射从action到结果抛出的异常,允许通过重定向自动处理异常。

8 fileUpload

便于文件上传。

9

i18n

在用户会话期间跟踪选定的区域。

10 logger

通过输出正在执行的action的名称提供简单的日志记录。

11 params

设置action上的请求参数。

12 prepare

这通常用于执行预处理工作,例如设置数据库连接。

13 profile

允许记录action的简单分析信息。

14 scope

在会话或应用程序范围内存储和检索action的状态。

15 ServletConfig

提供可访问各种基于servlet信息的action。

16 timer

以action执行时间的形式提供简单的分析信息。

17 token

检查action的有效性,以防止重复提交表单。

18 validation

提供action的验证支持。

如下图,我们首先使用timer拦截器,目的是测量执行action方法所需的时间。同时我们使用params拦截器,目的是将请求参数发送给action。 

<struts>
   <constant name="struts.devMode" value="true" />
   <package name="helloworld" extends="struts-default">
      <action name="hello" 
         class="cn.w3cschool.struts2.HelloWorldAction"
         method="execute">
         <interceptor-ref name="params"/>
         <interceptor-ref name="timer" />
         <result name="success">/HelloWorld.jsp</result>
      </action>
   </package>
</struts>

 

Struts2 结果类型

如前面所述,<results>标签在Struts2 MVC框架中扮演视图的角色。Action负责执行业务逻辑,下一步就是使用<results>标签显示视图。
通常有一些导航规则附加的结果。例如,如果action是进行验证用户,则有三种可能的结果:(a)成功登录(b)登录失败:用户名或密码不正确(c)帐户锁定。
在这种情况下,action将配置三个可能的结果字符串和三个不同的视图来渲染结果,这在我们前面的例子中已经看到过了。
但是,Struts2不绑定使用JSP作为视图技术。毕竟,MVC范例的目的是保持图层分离和高度可配置。例如,对于Web2.0客户端,你可能希望返回XML或JSON作为输出。在这种情况下,你可以为XML或JSON创建一个新的结果类型并实现这一点。
Struts提供了许多预定义的结果类型,我们已经看到的是默认的结果类型dispatcher,它用于分发到JSP页面。Struts允许你使用其他标记语言为视图技术呈现结果,较常选用的包括VelocityFreemakerXSLTTiles

dispatcher结果类型

dispatcher结果类型是默认的类型,如果未指定其他结果类型,则使用此类型。它用于转发到服务器上的servlet,JSP,HTML等页面。它使用RequestDispatcher.forward()方法。
如下面这个“简写”的版本,里面我们用一个JSP路径作为结果标签的主体。

①转发到一个JSP:

<result name="success">
   ${basePath}/HelloWorld.jsp  //struts.xml中可以使用EL表达式
</result>

 

我们还可以使用<result>元素中的<param name="location">标签来指定JSP文件

<result name="success" type="dispatcher">
   <param name="location">
      /HelloWorld.jsp
   </param >
</result>

 

②转发到一个Action:

<action name="testResult" class="com.struts2.action.TestResultAction">
    <!--参数之间必须使用&amp;。&amp;是&在xml中转义字符。需要在对应的Action类中定义参数的变量及其get和set方法。-->
    <result name="success" type="chain"><!-- 转发到一个Action并携带请求参数 -->
        <param name="actionName">testAction</param>
        <param name="namespace">/atguigu</param>
        <param name="errorcode">${errorcode}&amp;error=1</param> 
    </result>            
</action>

 

redirect结果类型

redirect结果类型调用标准的response.sendRedirect()方法,使得浏览器向给定的位置创建一个新请求。
我们可以在<result...>元素的主体中或作为<param name="location">的元素中给定位置。

①重定向到一个JSP

<action name="hello" 
   class="com.tutorialspoint.struts2.HelloWorldAction"
   method="execute">
   <result name="success" type="redirect">
       <param name="location">
         /NewWorld.jsp
      </param >
   </result>
</action>

 

②重定向到一个Action

<action name="testResult" class="com.struts2.action.TestResultAction">                                                            
    <result name="index" type="redirectAction">
        <!-- 下面两个参数和底下新建的package相关参数对应 -->
        <param name="actionName">testAction</param>    <!-- 对应要跳转到的Action -->
        <param name="namespace">/atguigu</param>  <!-- 指定目标Action所在的命名空间 -->
    </result>     
</package>
    
<package name="testPackage" namespace="/atguigu" extends="struts-default">
    <action name="testAction" class="com.struts2.action.TestAction">
        <result>/success.jsp</result>
    </action>
</package>

 

我们还可以通过redirect的响应类型便捷的实现redirectAction功能,但是转发不能通过这种写法实现

<result name="index" type="redirect">
    ${basePath}/atguigu/testAction.do
</result> 

 

Struts2 值栈/OGNL

Struts2使用OGNL将请求Action的参数封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性值。

每次访问action时候,都会创建action对象,在每个action对象里面都会有一个值栈对象。

在ValueStack对象的内部有两个逻辑部分:

①ObjectStack(对象栈):struts把Action和相关对象压入ObjectStack中。实际上是CompoundRoot类型,是一个使用ArrayList定义的栈。里边保存各种和当前Action实例相关的对象,是一个数据结构意义上的栈。

②ContextMap(Map栈):struts把各种各样的映射关系(一些Map类型的对象)压入ContextMap中,实际上是OgnlContext类型,是个Map,也是对ActionContext的一个引用。里边保存着各种Map:request、session、application、parameters,attr。


你可以在Action中获取值栈对象,如下所示:

ActionContext.getContext().getValueStack()

一旦你有一个值栈对象,你可以使用以下方法来操纵该对象:

序号值栈方法和说明
1 Object findValue(String expr)

通过在默认搜索顺序中对值栈评估所给定的表达式来查找值。

2 CompoundRoot getRoot()

获取将对象推入值栈的CompoundRoot。

3 Object peek()

获取值栈顶部的对象而不改变值栈。

4 Object pop()

获取值栈顶部的对象,并将其从值栈中删除。

5 void push(Object o)

将对象放在值栈的顶部。

6 void set(String key,Object o)

使用给定的key在值栈上设置一个对象,使其可通过findValue(key,...)检索。

7 void setDefaultType(Class defaultType)

设置在获取值时要转换的默认类型。

8 void setValue(String expr,Object value)

尝试使用由默认搜索顺序给定的表达式在值栈的bean上设置属性。

9 int size()

获取值栈中的对象数。

OGNL(Object-Graph Navigation Language,对象图导航语言)是一种强大的表达式语言,用于引用和操作值栈上的数据,还可用于数据传输和类型转换。
OGNL非常类似于JSP表达式语言。OGNL基于上下文中存有根对象或默认对象的理念,使用标记符号(即#号)来引用默认或根对象的属性。
如前面所述,OGNL是基于上下文的,而Struts构建了一个ActionContext映射以供OGNL使用。 ActionContext映射包含以下内容:

  • 应用程序 - 应用程序作用域变量

  • 会话 - 会话作用域变量

  • 根/值栈 - 所有的action变量都存储在这里

  • 请求 - 请求作用域变量

  • 参数 - 请求参数

  • 属性 - 存储在页面,请求,会话和应用程序作用域中的属性

访问对象栈中的属性

若想访问ObjectStack里的某个对象的属性,可以使用以下几种形式之一(OGNL写在value中): 

<s:property value="object.propertyName">
<s:property value="object.['propertyName']">
<s:property value="object.["propertyName"]">

ObjectStack里的对象可以通过一个从零开始的下标来引用。

ObjectStack里的栈顶对象可以用[0]来引用,它下面的那个对象可以用[1]引用。若希望返回栈顶对象的message属性值:[0].message或[0]['message']或[0]["message"]

若在指定的对象里没有找到指定的属性,则到指定对象的下一个对象里继续搜索,即[n]的含义是从第n个开始搜索,而不是只搜索第n个对象。若从栈顶对象开始搜索,则可以省略下标部分。

示例

创建用于访问值栈的action类,然后在details.JSP视图页面设置使用OGNL进行访问的几个key。

public class HelloWorldAction extends ActionSupport{
   private String name;
   
   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }
   
   public String execute() throws Exception {
      //获取值栈对象
      ValueStack stack = ActionContext.getContext().getValueStack();
      Map<String, Object> context = new HashMap<String, Object>();
      //把对象压入到值栈的栈顶
      context.put("key1", new String("This is key1")); 
      context.put("key2", new String("This is key2"));
      stack.push(context);
      System.out.println("Size of the valueStack: " + stack.size());
      return "success";
   }  
}

 

实际上,Struts 2在执行时会将action添加到值栈的顶部。所以,通常放置东西在值栈的方法是添加getters/setters的值到Action类,然后使用<s:property>标签访问值(property标签用来输出值栈中的一个属性值)。

创建index.jsp文件:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
   pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>Hello World</title>
</head>
<body>
   <h1>Hello World From Struts2</h1>
   <form action="hello">
      <label for="name">请输入用户名</label><br/>
      <input type="text" name="username"/>
      <input type="submit" value="Say Hello"/>
   </form>
</body>
</html>

 

创建视图details.jsp文件:

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %> <!-- 切记不要忘记引入这个 -->
<html>
<head>
<title>Hello World</title>
</head>
<body>
   Entered value : <s:property value="username"/>或者${username}<br/>
   Value of key 1 : <s:property value="key1" /><br/>
   Value of key 2 : <s:property value="key2" /> <br/>
</body>
</html>

 

现在,在index.jsp的文本框中输入任意单词,然后单击“Say Hello”按钮执行定义的action。可以在控制台看到以下输出:

Size of the valueStack: 3

这意味着它将显示你输入的任何值和我们放在值栈上的key1和key2的值。

页面最终跳转到details.jsp,显示效果如下:

this is key1
this is key2
fyj

注意:处于栈顶的分别是 key1 和 key2,其次才是我们提交的 username 值。

 

访问Map栈中的属性

 若想访问ContextMap里的某个对象的属性,可以使用以下几种形式之一: 

<s:property value="#object.propertyName">
<s:property value="#object.['propertyName']">
<s:property value="#object.["propertyName"]">

 

示例

创建Action类:

public class Product {
    private Integer productId;
    private String productName;
    private String productDesc;
    public Integer getProductId() {return productId;}
    public void setProductId(Integer productId) {this.productId = productId;}
    public String getProductName() {return productName;}
    public void setProductName(String productName) {this.productName = productName;}
    public String getProductDesc() {return productDesc;}
    public void setProductDesc(String productDesc) {this.productDesc = productDesc;}
    public String save(){
        System.out.println("save:"+this);
        //获取值栈对象
        ValueStack stack = ActionContext.getContext().getValueStack();
        Test test = new Test();
        test.setProductDesc("AABBCC");
        test.setProductName("ABCD");
        stack.push(test);
        sessionMap.put("product",this);
        requestMap.put("test",test);
        return "success";
    }
    private Map<String,Object> sessionMap;
    private Map<String,Object> requestMap;

}

 

在视图details.jsp访问Map栈中的属性值

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %><html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    ProductName:<s:property value="#session.product.productName"/>

    ProductName:<s:property value="#request.test.productName"/>

    <!-- 我们还可以调用对象栈的方法为一个属性赋值 -->
    <s:property value="setProductName('fyj')"/>
</body>
</html>

 

posted on 2019-04-17 20:48  FuYingju  阅读(90)  评论(0编辑  收藏  举报