Struts 2基础

 

2 Struts 2

此内容是《Java Web开发教程——入门与提高篇(JSP+Servlet)》一书附赠资料的一部分。

2.1概述

Struts现在分两个版本:Struts 1.XStruts 2.XStruts 1.X已经有很多年了,可以说非常流行,但是因为其他框架的快速发展以及自身存在的问题,Struts 2诞生了,Struts 2Struts 1的区别非常大,实际上Struts 2的核心思想是基于另外一个非常成功的Web框架WebWork。两者的区别如表20.1所示。下面主要针对Struts 2进行介绍。

2.1 Struts1Struts2的比较

Feature

Struts 1

Struts 2

Action

Struts 1中要求Action类继承抽象的基类。在Struts 1中一个普遍存在的问题就是面向抽象类编程,而不是面向接口编程。

Struts 2中的Action可以实现一个Action接口,同时可以实现其他的接口,这样可以使用户有选择性地使用其它自定义的服务。Struts 2提供了基础类ActionSupport,该类实现了一些通用的接口。Action接口不是必须的。任何具有execute方法的POJO对象都可以用作Struts 2Action对象。

线程模型

Struts 1Actions是单例的,因为只有一个类的实例来处理所有对这个Action的请求,所以必须是线程安全的。单例策略对Struts 1Action的能够完成的功能有很大限制,有些功能需要额外的努力才能完成。Action资源必须是线程安全的或者synchronized

Struts 2Action对象是为每个请求实例化的,因此没有线程安全的问题。(在实践中,Servlet容器会为每个请求生成多个throw-away对象,增加的对象不会对性能产生太大影响或者对垃圾回收产生影响)

Servlet依赖

Struts 1Action依赖Servlet API,因为当调用Actionexecute方法时需要传参数HttpServletRequestHttpServletResponse

Struts 2Action与容器不是紧密结合在一起的。多数情况下,servlet上下文被表示为Map对象,允许对Action进行独立的测试。如果需要,Struts 2Action仍然可以访问原始的requestresponse对象。 然而,其它框架元素可以减少或者消除对HttpServetRequest HttpServletResponse对象进行直接访问的必要。

可测试性

测试Struts 1 Action的一个主要障碍就是execute方法使用了Servlet API1个第三方扩展Struts TestCase,为Struts 1提供了一组模拟(mock)对象。

Struts 2Action可以通过实例化、设置属性和调用方法进行测试。依赖注入支持使测试更简单。

获取输入

Struts 1使用ActionForm对象来获取输入。像Action一样,所有的ActionForm必须继承一个基类。因为其它的JavaBean不能用作ActionForm,开发人员经常需要创建多余的类来获取输入。可以使用动态Form来替换传统的ActionForm类,但是开发人员同样可能需要重新描述已有的JavaBean

Struts 2使用Action的属性作为输入属性,不用创建第二个输入对象。输入属性可以是复杂的对象类型,还可以有自己的属性。可以在页面中通过taglib访问Action属性。Struts 2也支持ActionForm模式,以及POJO表单对象和POJO Action。复杂对象类型,包括业务或者域对象,都可以作为输入/输出对象。模型驱动的特性简化了标签库对POJO输入对象的引用。

表达式语言

Struts 1集成了JSTL,所以可以使用JSTLEL语言,EL提供了基本的对象结构遍历(object graph traversal),但是集合以及索引属性支持比较弱。

Struts 2可以使用JSTL,同时Struts还支持另外一种功能更强大、使用更灵活的表达式语言,这种语言是Object Graph Notation Language,简称 OGNL

值与视图的绑定

Struts 1使用了标准的JSP机制把对象与要访问的页面上下文绑定。

Struts 2使用了一种ValueStack技术,这样标签库不用把视图与要呈现的对象类型关联就可以访问值。ValueStack策略允许重用涉及多个类型的视图,这些类型可能有相同的属性名,但是属性类型不同。

类型转换

Struts 1ActionForm属性通常都是字符串类型。Struts 1 使用Commons-Beanutils进行类型转换。转换器是针对每个类的,而不能为每个实例配置。

Struts 2使用OGNL进行类型转换,框架包含了常用对象类型和基本数据类型的转换器。

验证

Struts 1支持手动验证,通过ActionFormvalidate方法或者通过继承通用的验证器来完成。对于同一个类可以有不同的验证上下文环境,但是不能链接到对子类型的验证。

Struts 2支持通过验证方法进行手工验证和XWork验证框架。Xwork验证框架支持对子属性的链接验证,使用为属性类型定义的验证规则和上下文。

Action执行的控制

Struts 1支持为每个模块提供独立的请求处理器(生命周期),但是同一个模块中的所有Action具有相同的生命周期。

Struts 2通过拦截器栈支持为每个Action创建不同的生命周期。必要的时候,可以使用不同的Actio创建和使用自定义栈。

注:来自Struts的官方网站:http://struts.apache.org/2.0.11.2/docs/comparing-struts-1-and-2.html

Strust 2结构图如图2.1(原图来自Strust 2文档)所示:

2.1 Struts2结构图

在处理一个请求的时候,主要使用3个类:ActionInterceptorResult

处理流程:

u  请求到达服务器之后,首先经过一系列过滤器,有的是可选的,最主要的过滤器是FilterDispatcher。所有的请求都会提交给它处理,该过滤器是在web.xml中配置的。配置代码如下:

   

        struts2

        org.apache.struts2.dispatcher.FilterDispatcher

   

 

   

        struts2

        /*

   

u  FilterDispatcher过滤器接收到请求之后调用ActionMapper查看是否需要调用ActionActionMapper提供了HttpRequestAction调用请求之间的映射关系,可以决定当前请求是否需要调用Action。如果ActionMapper返回的信息表明需要调用ActionFilterDispatcher过滤器把控制前交给ActionProxy

u  ActionProxy调用配置文件管理器ConfigurationManager,该管理器从struts.xml配置文件中获取配置信息,获取的信息主要包括当前请求对应哪个Action(对用户的请求进行处理),对应哪些Result(决定了如何对用户响应),有时候还涉及拦截器。然后根据这些信息创建ActionInvocation对象,该对象负责具体的调用过程。struts.xml是用户需要提供的最主要的配置文件。下面是一个struts.xml配置文件的部分内容。

   

 

       

            /pages/Logon.jsp

            Welcome

            MainMenu

            ChangePassword

       

 

       

            Welcome

       

 

   

u  ActionInvocation对象按照顺序执行当前请求所对应的拦截器,拦截器能够对请求进行预处理,例如验证、文件上传等,并能够对响应内容进行再处理。通常拦截器是由系统提供的,如果需要,编程人员只需要进行配置即可。在调用Action的方法之前,会调用拦截器的预处理方法;

u  ActionInvocation对象调用拦截器的预处理方法之后会调用Actionexecute方法,Action中的代码主要由编程人员根据功能进行编写的,通常从数据库检索信息或者向数据库存储信息。Action的方法返回一个字符串。下面是一个简单的Action例子。

package simple;

import java.util.Map;

import javax.servlet.http.HttpSession;

 

import com.opensymphony.webwork.ServletActionContext;

import com.opensymphony.xwork.ActionSupport;

 

public class LogoutAction extends ActionSupport {

 

    public String execute() throws Exception {

     Map session = ActionContext.getContext().getSession();

     session.remove("logined");

     session.remove("context");

        return SUCCESS;

    }

 

}

u  ActionInvocation对象根据Action方法的返回结果以及struts配置文件生成Result对象。Result对象选择一个模板文件来响应用户,模板文件可以是JSPFreeMarkerVelocity

u  容器加载并执行模板文件,使用在Action中获取的信息对模版中的变量进行赋值,也可能从资源文件或者其他内部对象中获取信息。最终向浏览器呈现的是HTMLPDF或者其他内容。

u  模板文件执行的结果会经过拦截器进行再处理,最后通过过滤器返回给客户端。

在该结构图中,既包含了Struts框架提供的基础接口,也包括了用户要编写的文件。其中,ActionMapperActionProxyConfigurationManagerActionInvocationResult是框架提供的核心类。过滤器和拦截器是框架提供的,用户可以根据需要进行配置,当然也可以编写自己的过滤器和拦截器。用户需要编写的文件是struts.xmlAction和模板文件,这些也是用户在使用Struts 2框架时需要做的工作。

2.2 开发人员的主要任务

框架为开发人员提供了大量的辅助类,用户在使用框架开发的时候只需要编写很少文件。在使用Struts 2开发的时候,首先应该把环境搭建起来,然后使用Struts 2提供的标签开发界面,然后编写Action类,最后进行配置。

环境搭建

在进行具体的开发之前,需要先搭建环境。包括如下过程:

u  创建Web工程;

u  加载Struts 2的核心类库,核心类库包括commons-logging-1.0.4.jarfreemarker-2.3.8.jarognl-2.6.11.jarstruts2-core-2.0.11.2.jarxwork-2.0.5.jar,把这些类库放到Web工程的WEB-INF/lib下面;

u  配置web.xml,主要配置Struts中心控制器FilterDispatcher,下面是1个例子。

 

    Struts Blank

 

   

        struts2

        org.apache.struts2.dispatcher.FilterDispatcher

   

 

   

        struts2

        /*

   

 

   

        index.html

   

 

u  创建struts.xml配置文件,与类文件放在一起,空白的struts文件如下所示。在使用Struts 2进行开发所有的配置基本上都在这个文件中完成。也可以根据需要创建多个配置文件,然后在这个配置文件中使用进行包含。

    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

    "http://struts.apache.org/dtds/struts-2.0.dtd">

 

 

   

   

 

   

 

   

 

环境搭建完之后,在具体开发过程中主要完成3个方面的工作:

u  制作模板文件,可以使用JSPFreeMarker或者Velocity等;

u  编写Action,基本上每个动作对应1Action

u  配置,主要在struts.xml中进行配置。

下面分别介绍。

制作模板文件

模版文件的主要作用是接收用户输入的信息,并向用户展示信息。Struts提供了多个标签库来简化页面的代码量,使用标签之后页面也更容易维护。下面是一段标签:

 

 

 

 

 

 

 

 

            οnclick="form.οnsubmit=null"/>

Struts 2中提供了两类通用标签和3类界面标签:

u  控制标签

u  数据标签

u  Form标签

u  Non-Form用户接口标签

u  Ajax标签

下面对这些类型的标签进行介绍。

控制标签及其用法如表2.2所示。

2.2 控制标签

   

     

   

   

     

   

 

标签名

描述

例子

if

Java中的if基本相同

   

Will Not Be Executed

   

Will Be Executed

   

Will Not Be Executed

else if

Java中的else if基本相同

else

Java中的else基本相同

append

按照顺序把多个迭代器的元素组合到一个迭代器中,保持原来的顺序不变。

    

    

    

    

generator

根据val属性的给定的值生成迭代器对象。

 

    

 

iterator

对迭代器或者集合进行遍历,类似于Java中的for-each循环。

 

merge

把多个迭代器的元素合并到一个迭代器中,合并后的顺序为1.12.13.11.21.3…1.1表示第1个迭代器的第1个元素。

    

    

    

    

sort

List进行排序。

subset

获取集合的子集。

数据标签及其用法如表2.3所示。

2.3 数据标签

标签名

描述

例子

a

生成HTML

value="/images/delete.gif"/>" border="none"/>

action

JSP页面中直接调用Action

bean

实例化JavaBean对象

 

  The value of foot is :

date

创建Date对象

debug

 

 

i18n

得到ResourceBundle对象。

include

包含1JSP或者Servlet的输出。

  

param

为其他标签提供参数

参考上面的例子

property

获取属性值

参考bean标签的例子

push

把值保存起来使用

   

   

set

把某个值保存到某个作用范围的变量中。

Hello, . How are you?

text

呈现i18n的文本消息

   

url

用于生成URL

   

Form标签及其用法如表2.4所示。

2.4 Form标签

标签名

描述

例子

checkbox

生成复选框

value="aBoolean" fieldValue="true"/>

checkboxlist

生成多个复选框

combobox

输入框与下拉框的组合。

    label="My Favourite Fruit"

    name="myFavouriteFruit"

    list="{'apple','banana','grape','pear'}"

    headerKey="-1"

    headerValue="--- Please Select ---"

    emptyOption="true"

    value="banana" />

doubleselect

生成联动菜单

list="{'fruit','other'}" doubleName="dishes"

doubleList="top == 'fruit' ? {'apple', 'orange'} : {'monkey', 'chicken'}" />

head

生成HTMLhead部分。

  My page

 

file

生成文件输入框

 

form

生成form表单

hidden

生成隐藏域

label

生成标签

optiontrans

-ferselect

生成两个列表框,可以通过中间的按钮把左边的选项移动到右边,也可以把右边的选项移动到左边。

     label="Favourite Cartoons Characters"

     name="leftSideCartoonCharacters"

     list="{'Popeye', 'He-Man', 'Spiderman'}"

     doubleName="rightSideCartoonCharacters"

     doubleList="{'Superman', 'Mickey Mouse', 'Donald Duck'}"

 />

optgroup

select中提供选项

           name="mySelection"

           value="%{'POPEYE'}"

           list="%{#{'SUPERMAN':'Superman', 'SPIDERMAN':'spiderman'}}">

  

                list="%{#{'SOUTH_PARK':'South Park'}}" />

  

                list="%{#{'POKEMON':'pokemon','DIGIMON':'digimon',

'SAILORMOON':'Sailormoon'}}" />

select

生成下拉框

password

密码输入框

size="10" maxlength="15" />

radio

单选按钮

reset

重值按钮

submit

提交按钮

textarea

生成文本域

textfield

生成输入框

 

token

阻止表单重复提交

 

updownselect

创建元素能够上下移动的列表框

list="#{'england':'England', 'america':'America', 'germany':'Germany'}"

name="prioritisedFavouriteCountries"

headerKey="-1"

headerValue="--- Please Order Them Accordingly ---"

emptyOption="true" />

non-form UI标签及其用法如表2.5所示。

2.5 non-form标签

标签名

描述

例子

actionerror

呈现错误信息

actionmessage

呈现提示信息

component

创建自定义组件

div

生成HTML

 

fielderror

输出关于输入元素的错误信息

  

        field1

        field2

  

   

      ....

  

Ajax标签包括aautocompleterbinddatetimepickerdivheadsubmittabbedpaneltextareatreetreenode等。具体用法参考Struts 2帮助文档。

编写Action

针对每个功能可以编写1Action,也可以多个功能共享1ActionAction完成的主要功能包括:

u  获取用户的输入信息,这个获取的过程是由框架完成的,但是用户需要在Action中定义与用户输入表单元素名字相同的成员变量,关键是要提供对成员变量赋值的set方法,这样框架在获取用户输入信息之后会调用set方法把值赋给Action的成员变量。

u  根据用户的请求信息,调用完成业务逻辑的JavaBean。如果希望要把某些执行结果传递给模板文件(JSPFreeMarkerVelocity等),需要在Action中定义成员变量来表示这些结果,最关键的是要定义get方法,这样在执行模版文件的时候会通过get方法来获取这些信息。

u  根据执行的结果,返回1个字符串,这个字符串决定了使用什么模板对用户进行响应。

下面是1个简单的例子。

public class LoginAction extends ActionSupport {

 

    private String userId;

    private String passwd;

    // userIdpasswd操作的settergetter方法

    public String execute() throws Exception {

        if ("admin".equals(userId) && "password".equals(passwd)) {

            Map session = ActionContext.getContext().getSession();

            session.put("logined","true");

            session.put("context", new Date());

            return SUCCESS;

        }

        return ERROR;

    }

}

注意:并不是必须继承ActionSupport,主要提供execute方法即可。

配置

通过配置文件Struts.xmlWeb应用的流程进行管理,包括Action映射和Result处理,前者把请求与Action关联起来,后者把Action执行的结果与响应界面关联起来。下面是一段配置。下面是一个简单的例子。

   

 

       

            /pages/Logon.jsp

            Welcome

            MainMenu

            ChangePassword

       

 

       

            Welcome

       

 

   

Struts 2中完成的主要配置如表2.6所示。

2.6 Struts 2的主要配置信息

配置元素

例子

JavaBean

name="myfactory" class="com.company.myapp.MyObjectFactory" />

常量

   ...

命名空间

包含

拦截器

 

 class="com.company.security.SecurityInterceptor"/>

 

   

   

 

引用拦截器

class="org.apache.struts2.example.counter.SimpleCounter">

    ...

   

全局Result

    /Error.jsp

    /Error.jsp

Action

Result

异常配置

Action中使用:

全局:

Struts 2提供了大量的拦截器,用户可以根据需要调用。

Struts 2的配置文件struts.xmlDTD定义如下。

 

 

    name CDATA #REQUIRED

    extends CDATA #IMPLIED

    namespace CDATA #IMPLIED

    abstract CDATA #IMPLIED

    externalReferenceResolver NMTOKEN #IMPLIED

 

 

    name CDATA #REQUIRED

    class CDATA #REQUIRED

    default (true|false) "false"

 

 

    name CDATA #REQUIRED

    class CDATA #REQUIRED

 

    name CDATA #REQUIRED

 

    name CDATA #REQUIRED

 

    name CDATA #REQUIRED

 

    name CDATA #REQUIRED

 

    class CDATA #REQUIRED

 

 

 

    name CDATA #REQUIRED

    class CDATA #IMPLIED

    method CDATA #IMPLIED

    converter CDATA #IMPLIED

 

    name CDATA #REQUIRED

 

    name CDATA #IMPLIED

    type CDATA #IMPLIED

 

    name CDATA #IMPLIED

    exception CDATA #REQUIRED

    result CDATA #REQUIRED

 

    file CDATA #REQUIRED

 

    type CDATA #IMPLIED

    name CDATA #IMPLIED

    class CDATA #REQUIRED

    scope CDATA #IMPLIED

    static CDATA #IMPLIED

    optional CDATA #IMPLIED

 

    name CDATA #REQUIRED

    value CDATA #REQUIRED   

2.3实例

功能:登录。

涉及的文件有:

l  Login.jsp,用于输入登录信息;

l  welcome.jsp,登录之后的欢迎界面;

l  loginCheck.jsp,判断用户是否登录;

l  LoginAction.java,完成登录业务处理,正常情况下会调用其他业务逻辑JavaBean来完成;

l  LogoutAction.java,完成退出业务处理;

l  struts.xml,应用的配置文件。

下面分别介绍。

Login.jsp

源文件:Login.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

 pageEncoding="ISO-8859-1"%>

Insert title here

User id

Password

 

/pages/welcome.jsp 

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

    pageEncoding="ISO-8859-1"%>

<%@ taglib prefix="ww" uri="/webwork" %>

Welcome

 

Welcome, you have logined.

The attribute of 'context' in session is




Logout


Logout2

 

/WEB-INF/inc/loginCheck.jsp

<%@ taglib="/webwork" prefix="ww" %>


 

simple.LoginAction.java

package simple;

import java.util.Date;import java.util.Map;

 

import javax.servlet.http.HttpSession;

 

import com.opensymphony.webwork.ServletActionContext;

import com.opensymphony.xwork.ActionSupport;

 

public class LoginAction extends ActionSupport {

 

    private String userId;

    private String passwd;

 

    public String execute() throws Exception {

        if ("admin".equals(userId) && "password".equals(passwd)) {

//            HttpSession session = ServletActionContext.getRequest().getSession();

//            session.setAttribute("logined","true");

//            session.setAttribute("context", new Date());

// Better is using ActionContext

  Map session = ActionContext.getContext().getSession();

session.put("logined","true");

            session.put("context", new Date());

            return SUCCESS;

        }

        return ERROR;

    }

 

    public String logout() throws Exception {

//        HttpSession session = ServletActionContext.getRequest().getSession();

//        session.removeAttribute("logined");

//        session.removeAttribute("context");

 Map session = ActionContext.getContext().getSession();

 session.remove("logined");

        session.remove("context");

        return SUCCESS;

    }

 

    public String getPasswd() {

        return passwd;

    }

 

    public void setPasswd(String passwd) {

        this.passwd = passwd;

    }

 

    public String getUserId() {

        return userId;

    }

 

    public void setUserId(String userId) {

        this.userId = userId;

    }

}

 simple.LogoutAction.java

package simple;

import java.util.Map;

import javax.servlet.http.HttpSession;

 

import com.opensymphony.webwork.ServletActionContext;

import com.opensymphony.xwork.ActionSupport;

 

public class LogoutAction extends ActionSupport {

 

    public String execute() throws Exception {

     Map session = ActionContext.getContext().getSession();

session.remove("logined");

session.remove("context");

        return SUCCESS;

    }

 

} 

 /WEB-INF/classes/xwork.xml

 

   

 

   

       

       

            /pages/welcome.jsp

            /login.jsp

       

 

       

            /login.jsp

       

 

       

            /login.jsp

       

   

你可能感兴趣的:(204,Java,Web开发教程——入门与提高篇)