struts2学习总结

来源:互联网 发布:mysql简版 编辑:程序博客网 时间:2024/06/06 20:03

第一节:model1与model2的区别和联系

JSPModel1和Model2是两种JSP建立应用程序的方式。

(1).Model1的体系结构图


1).首先web浏览器对JSP页面发出request请求,取得页面参数。

2.)通过JavaBean对业务逻辑进行操作。

3.)从而对应用数据进行操作,并返回数据。

4.)通过Response响应,渲染JSP页面.

优点:架构简单,比较适合小型项目开发。基本上两层代码就能搞定,JSP+持久层。

缺点JSP页面有控制页面显示的代码又有与后台业务逻辑交互的Java代码,职责不单一,页面负担很重,影响页面运行速度;而且不方便后期的维护。

 

(2)Model2的体系结构图:MVC

 

Model2跟Model1不同的地方是:

1.Model1是JSP负责业务逻辑+页面显示,Model2采用servlet和JSP协作,JSP负责页面显示,servlet负责业务逻辑,将页面和业务解耦和,让整个前台的开发和维护更加灵活,代码复用性高,便于维护。

具体职责:

JavaBean(Model),模型,主要职责 1.业务逻辑 2.保存数据的状态

JSP(View),视图,主要职责:1.页面显示

Servlet(Controller),控制器,主要职责:1.获取表单数据 2.调用业务逻辑 3.渲染页面

优点:职责清晰,适合大型项目

缺点:分层过多,不适合小型项目的开发

总结

  Model2是Model1的进化版,或者也可以说拓展版。两者应用的场合不同,一大项目一小项目。就跟公司一样,如果公司想要扩充规模,就必须职责单一,这样才不会导致人员责任不明确,效率低下,资源浪费。

 

第二节:框架

1.框架:framework。框架就是一个模板,半成品,使用框架必须遵守框架的规则。

好处:提高开发效率,并不会提高执行效率。

典型的MVC框架:

    Structs2:由structs1+webwork整合而来。是apatch下的一个项目,开源免费。基于包来管理。

2.MVC框架完成的事情:

(1).Servlet做哪些事情:处理用户提交的数据,调用业务方法,处理业务结果,控制试图显示,将用户请求映射到一个java类。

(2). MVC框架完成的事情:将用户请求映射到一个java类,获取用户提交的数据,渲染数据(将数据封装到前台显示(request)),控制试图跳转。

struts2执行原理(执行流程)

 (2012-12-06 21:35:48)

转载



一个请求在Struts2框架中的处理大概分为以下几个步骤:

1 客户端发送请求;
2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action。FilterDispatcher的功能如下:

        (1)执行Actions
        (2)清除ActionContext
        (3)维护静态内容
        (4)清除request生命周期内的XWorkinterceptors

4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 ActionProxy创建一个ActionInvocation (调用)的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper


拦截器与过滤器:

 

1、拦截器是基于java反射机制的,而过滤器是基于函数回调的。2、过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有请求起作用。
4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。



在上述过程中所有的对象(ActionResultsInterceptors,等)都是通过ObjectFactory来创建的。

Struts2的目标很简单--使Web开发变得更加容易。为了达成这一目标,Struts2中提供了很多新特性,比如智能的默认设置、annotation的使用以及"惯例重于配置"原则的应用,而这一切都大大减少了XML配置。Struts2中的Action都是POJO,这一方面增强了Action本身的可测试性,另一方面也减小了框架内部的耦合度,而HTML表单中的输入项都被转换成了恰当的类型以供action使用。开发人员还可以通过拦截器(可以自定义拦截器或者使用Struts2提供的拦截器)来对请求进行预处理和后处理,这样一来,处理请求就变得更加模块化,从而进一步减小耦合度。模块化是一个通用的主题--可以通过插件机制来对框架进行扩展;开发人员可以使用自定义的实现来替换掉框架的关键类,从而获得框架本身所不具备的功能;可以用标签来渲染多种主题(包括自定义的主题);Action执行完毕以后,可以有多种结果类型--包括渲染JSP页面,VelocityFreemarker模板,但并不仅限于这些。

 

Struts2的优缺点:

优点:

(1)实现了MVC模式,层次结构清晰,使程序员只需关注业务逻辑的实现。

(2)丰富的标签库,大大提高了开发的效率。Struts的标记库(Taglib)

(3) Struts2提供丰富的拦截器实现。

(4) 页面导航,通过配置文件,就可以掌握整个系统各个部分之间的关系,对于后期的维护有着莫大的好处

(5) 异常处理机制(exception),只需在配置文件中配置异常的映射,即可对异常做相应的处理。
(6) Struts2的可扩展性高。Struts2的核心jar包中由一个struts-default.xml文件,在该文件中设置了一些默认的bean,resultType类型,默认拦截器栈等,所有这些默认设置,用户都可以利用配置文件更改,可以更改为自己开发的bean,resulttype等。因此用户开发了插件的话只要很简单的配置就可以很容易的和Struts2框架融合,这实现了框架对插件的可插拔的特性。

(7) 面向切面编程的思想在Strut2中也有了很好的体现。最重要的体现就是拦截器的使用,拦截器就是一个一个的小功能单位,用户可以将这些拦截器合并成一个大的拦截器,这个合成的拦截器就像单独的拦截器一样,只要将它配置到一个、Action中就可以。

(8)支持国际化i18N,数据库链接池管理。

缺点

1. 转到展示层时,需要配置forward,如果有十个展示层的jsp,需要配置十次struts,而且还不包括有时候目录、文件变更,需要重新修改forward,注意,每次修改配置之后,要求重新部署整个项目,而tomcate这样的服务器,还必须重新启动服务器

2.Struts 的Action必需是thread-safe方式,它仅仅允许一个实例去处理所有的请求。所以action用到的所有的资源都必需统一同步,这个就引起了线程安全的问题。

3. 测试不方便.Struts的每个Action都同Web层耦合在一起,这样它的测试依赖于Web容器,单元测试也很难实现。不过有一个Junit的扩展工具Struts TestCase可以实现它的单元测试。

4. 类型的转换.Struts的FormBean把所有的数据都作为String类型,它可以使用工具Commons-Beanutils进行类型转化。但它的转化都是在Class级别,而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。

5. 对Servlet的依赖性过强.Struts处理Action时必需要依赖ServletRequest和ServletResponse,所有它摆脱不了Servlet容器。

6.前端表达式语言方面.Struts集成了JSTL,所以它主要使用JSTL的表达式语言来获取数据。可是JSTL的表达式语言在Collection和索引属性方面处理显得很弱。

7.对Action执行的控制困难. Struts创建一个Action,如果想控制它的执行顺序将会非常困难。甚至你要重新去写Servlet来实现你的这个功能需求。

8.对Action执行前和后的处理. Struts处理Action的时候是基于class的hierarchies,很难在action处理前和后进行操作。

9.对事件支持不够. 在struts中,实际是一个表单Form对应一个Action类(或DispatchAction),换一句话说:在Struts中实际是一个表单只能 对应一个事件,struts这种事件方式称为applicationevent,application event和component event相比是一种粗粒度的事件

 

Struts源码解析跟踪:http://qq421606162.iteye.com/blog/2009428

 

第三节:struts线程安全问题

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 

线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

 

Struts2线程安全吗?

每次请求都会重新创建新的action对象,所以线程安全。由于action对象是struts2,反射生成的,所以要求Action类要有一个公共的无参构造方法。

 

第四节:struts2xml配置

4.1.1常量配置:

1).包配置-------Struts2框架中核心组件就是Action、拦截器等,Struts2框架使用包来管理Action和拦截器等。每个包就是多个Action、多个拦截器、多个拦截器引用的集合。

在struts.xml文件中package元素用于定义包配置,每个package元素定义了一个包配置。它的常用属性有:

(1)name:必填属性,用来指定包的名字。

(2) extends:可选属性,用来指定该包继承其他包。继承其它包,可以继承其它包中的Action定义、拦截器定义等。

(3) namespace:可选属性,用来指定该包的命名空间。

<!DOCTYPE struts PUBLIC

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

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

<struts>

    <!-- struts2action必须放在一个指定的包空间下定义 -->

    <package name="default" extends="struts-default">

    <!-- 定义处理请求URLlogin.actionAction -->

        <action name="login" class="org.qiujy.web.struts.action.LoginAction">

        <!-- 定义处理结果字符串和资源之间的映射关系 -->

            <result name="success">/success.jsp</result>

            <result name="error">/error.jsp</result>

        </action>

    </package>

</struts>

4.1.2.    命名空间配置:

考虑到同一个Web应用中需要同名的ActionStruts2以命名空间的方式来管理Action,同一个命名空间不能有同名的Action

Struts2通过为包指定namespace属性来为包下面的所有Action指定共同的命名空间。

把上示例的配置改为如下形式:

<!DOCTYPE struts PUBLIC

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

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

<struts>

    <!-- struts2action必须放在一个指定的包空间下定义 -->

    <package name="qiujy" extends="struts-default">

    <!-- 定义处理请求URLlogin.actionAction -->

        <action name="login" class="org.qiujy.web.struts2.action.LoginAction">

        <!-- 定义处理结果字符串和资源之间的映射关系 -->

            <result name="success">/success.jsp</result>

            <result name="error">/error.jsp</result>

        </action>

    </package>

   

    <package name="my" extends="struts-default" namespace="/manage">

    <!-- 定义处理请求URLlogin.actionAction -->

        <action name="backLogin" class="org.qiujy.web.struts2.action.LoginAction">

        <!-- 定义处理结果字符串和资源之间的映射关系 -->

            <result name="success">/success.jsp</result>

            <result name="error">/error.jsp</result>

        </action>

    </package></struts>

如上配置了两个包:defaultmy,配置my包时指定了该包的命名空间为/manage

对于包default:没有指定namespace属性。如果某个包没有指定namespace属性,即该包使用默认的命名空间,默认的命名空间总是""

对于包my:指定了命名空间/manage,则该包下所有的Action处理的URL应该是命名空间/Action。如上名为backLoginAction,它处理的URL为:

http://localhost:8080/userlogin_struts2/manage/backLogin.action

Struts2的命名空间的作用等同于struts1里模块的作用。

4.1.3.    包含配置:

Struts2中可以将一个配置文件分解成多个配置文件,那么我们必须在struts.xml中包含其他配置文件。

<struts>

    <include file="struts-default.xml"/>

    <include file="struts-user.xml"/>

    <include file="struts-book.xml"/>

    <include file="struts-shoppingCart.xml"/>

   

    ......

   </struts>

4.1.4.    拦截器配置:

见后面章节介绍。

4.1.5.    常量配置:

Struts2框架有两个核心配置文件,其中struts.xml文件主要负责管理应用中的Action映射,Action处理结果和物理资源之间的映射关系。除此之外,Struts2框架还包含了一个struts.properties文件,该文件主义了Struts2框架的大量常量属性。但通常推荐也是在struts.xml文件中来配置这些常量属性。

如:后面会讲到Struts2的国际化,它的资源文件位置就用常量属性来指定:

<struts>

    ......

    <constant name="struts.custom.i18n.resources" value="messages"/>

</struts>

表示指定了资源文件的放置在classes目录下,基本名是messages,则在classes目录下您就应该放置类似messages_zh_CN.propertiesmessage_en.properties名的文件。

 

4.5.   Struts2Action

4.5.1.    实现Action类:

Struts2Action是核心内容,它包含了对用户请求的处理逻辑,我们也称Action为业务控制器。

Struts2中的Action采用了低侵入式的设计,Struts2不要求Action类继承任何的Struts2的基类或实现Struts2接口。(但是,我们为了方便实现Action,大多数情况下都会继承com.opensymphony.xwork2.ActionSupport类,并重写此类里的public String execute() throws Exception方法。因为此类中实现了很多的实用接口,提供了很多默认方法,这些默认方法包括获取国际化信息的方法、数据校验的方法、默认的处理用户请求的方法等,这样可以大大的简化Action的开发。)

Struts2中通常直接使用Action来封装HTTP请求参数,因此,Action类里还应该包含与请求参数对应的属性,并且为属性提供对应的gettersetter方法。(当然,Action类中还可以封装处理结果,把处理结果信息当作一属性,提供对应的gettersetter方法)

修改第一部分的用户登录示例:把Action改成如下:

package org.qiujy.web.struts2.action;

import com.opensymphony.xwork2.ActionSupport;

/**

 *@authorqiujy

 *@version1.0

 */

publicclass LoginAction extends ActionSupport{

    private String userName;

    private String password;

   

    private String msg//结果信息属性

   

    /**

     *@returnthemsg

     */

    public String getMsg() {

        returnmsg;

    }

    /**

     *@parammsgthemsgtoset

     */

    publicvoid setMsg(String msg) {

        this.msg = msg;

    }

    /**

     *@returntheuserName

     */

    public String getUserName() {

        returnuserName;

    }

    /**

     *@paramuserNametheuserNametoset

     */

    publicvoid setUserName(String userName) {

        this.userName = userName;

    }

    /**

     *@returnthepassword

     */

    public String getPassword() {

        returnpassword;

    }

    /**

     *@parampasswordthepasswordtoset

     */

    publicvoid setPassword(String password) {

        this.password = password;

    }

    /**

     *处理用户请求的excute()方法

     *@return结果导航字符串

     *@throwsException

     */

    public String execute() throws Exception{

       if("test".equals(this.userName) &&

"test".equals(this.password)){

           msg = "登录成功,欢迎" + this.userName;

           returnthis.SUCCESS;

       }else{

           msg = "登录失败,用户名或密码错";

           returnthis.ERROR;

       }

    }

}

success.jsperror.jsp页面中添加 ${msg} EL表达式来显示结果信息。则最终效果跟以前一样。

4.5.2.    Action访问Servlet API:(解耦两种,耦合两种)

Struts2中的Action并没有和任何Servlet API耦合,这样框架更具灵活性,更易测试。

但是,对于web应用的控制器而言,不访问Servlet API几乎是不可能的,例如跟踪HTTP Session状态等。Struts2框架提供了一种更轻松的方式来访问Servlet APIStruts2中提供了一个ActionContext(当前Action的上下文对象),通过这个类可以访问Servlet API。下面是该类中提供的几个常用方法:

ActionContextmap结构的容器。ActionContextAction的上下文,存放Action过程中数据信息。ActionContext存放Action数据,ActionInvocationrequest的数据,session的数据,local的数据,conversion errors等。每次请求时会为当前线程创建一个新的ActionContxt。而ActionContext采用了ThreadLocal(存放多线程内部的局部变量容量,静态的,线程安全)的方式来存放ActionContext,所以ActionContext是线程安全的。可以通过ActionContext.getContext获取,由于ActionContext是线程安全的,并且是通过静态方法获取的,所以在本线程的非Action类中也可以直接访问,注意:ActionContext是基于请求创建的,所以在非请求的线程中是不能直接使用ActionContext对象的,如:filterinit()方法。

ActionContext包含六大对象

ActionContext

Application

Session

Request

Parameters

Attribute(page->request-àsession->application)

ValueStack()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Ognl ValueStack值栈是ActionContext中的一个对象,本身是栈结构,存放的数据时Action对象。

结论:使用ognl表达式去访问action的属性时可以直接访问。访问ActionContext中的属性时需要加“#”。(需要在jsp页面中导入标签库:<%@ taglib prefix="s” uri=”/struts-tag”%>,访问格式:用户名:<s:propertyvalue=#session.user/>)。

l publicstatic ActionContext getContext():获得当前ActionActionContext实例。

l publicObject get(Object key):此方法类似于调用HttpServletRequestgetAttribute(String name)方法。

l publicvoid put(Object key, Object value):此方法类似于调用HttpServletRequestsetAttribute(String name, Object o)

l publicMap getParameters():获取所有的请求参数。类似于调用HttpServletRequest对象的getParameterMap()方法。

l publicMap getSession():返回一个Map对象,该Map对象模拟了HttpSession实例。

l publicvoid setSession(Map session)直接传入一个Map实例,将该Map实例里的key-value对转换成session的属性名-属性值对。

l publicMap getApplication():返回一个Map对象,该对象模拟了该应用的ServletContext实例。

l publicvoid setApplication(Map application):直接传入一个Map实例,将该Map实例里的key-value对转换成application的属性名-属性值对。

修改以上用户登录验证示例的Action类中的execute方法:

public String execute() throws Exception{

        if("test".equals(this.userName) && "test".equals(this.password)){

            msg = "登录成功,欢迎" + this.userName;

            //获取ActionContext实例,通过它来访问Servlet API

            ActionContext context = ActionContext.getContext();

            //session中是否已经存放了用户名,如果存放了:说明已经登录了;

//否则说明是第一次登录成功

            if(null != context.getSession().get("uName")){

                msg = this.userName + ":你已经登录过了!!!";

            }else{

                context.getSession().put("uName"this.userName);

            }

           

            returnthis.SUCCESS;

        }else{

            msg = "登录失败,用户名或密码错";

            returnthis.ERROR;

        }

    }

       Struts2中通过ActionContext来访问Servlet API,让Action彻底从Servlet API 中分离出来,最大的好处就是可以脱离Web容器测试Action。

  另外,Struts2中还提供了一个ServletActionContext类,Action只要继承自该类,就可以直接访问Servlet API。具体方法参看struts2API文档。

4.5.3.    一个Action内包含多个请求处理方法的处理

Struts1提供了DispatchAction,从而允许一个Action内包含多个请求处理方法。Struts2也提供了类似的功能。处理方式主要有以下三种方式:

4.5.3.1.    动态方法调用:提示,这种调用方式会带来安全隐患!

DMIDynamic MethodInvocation动态方法调用。

动态方法调用是在action的名字中使用感叹号(!)来标识要调用的方法名,其语法格式为 actionName!methodName.action

动态方法调用是指:表单元素的action不直接等于某个Action的名字,而是以如下形式来指定对应的动作名:

<form method="post" action="userOpt!login.action">

则用户的请求将提交到名为”userOpt”Action实例,Action实例将调用名为”login”方法来处理请求。同时login方法的签名也是跟execute()一样,即为public String login() throws Exception

注意:要使用动态方法调用,必须设置Struts2允许动态方法调用,通过设置struts.enable.DynamicMethodInvocation常量来完成,该常量属性的默认值是true

4.5.3.1.1.      示例:

修改用户登录验证示例,多增加一个注册用户功能。

1.       修改Action类:

package org.qiujy.web.struts2.action;

 

import com.opensymphony.xwork2.ActionContext;

import com.opensymphony.xwork2.ActionSupport;

 

/**

 *@authorqiujy

 *@version1.0

 */

publicclass LoginAction extends ActionSupport{

    private String userName;

    private String password;

    private String msg//结果信息属性

    /**

     *@returnthemsg

     */

    public String getMsg() {

        returnmsg;

    }

    /**

     *@parammsgthemsgtoset

     */

    publicvoid setMsg(String msg) {

        this.msg = msg;

    }

    /**

     *@returntheuserName

     */

    public String getUserName() {

        returnuserName;

    }

    /**

     *@paramuserNametheuserNametoset

     */

    publicvoid setUserName(String userName) {

        this.userName = userName;

    }

    /**

     *@returnthepassword

     */

    public String getPassword() {

        returnpassword;

    }

    /**

     *@parampasswordthepasswordtoset

     */

    publicvoid setPassword(String password) {

        this.password = password;

    }

   

    /**

     *处理用户请求的login()方法

     *@return结果导航字符串

     *@throwsException

     */

    public String login() throws Exception{

        if("test".equals(this.userName) && "test".equals(this.password)){

            msg = "登录成功,欢迎" + this.userName;

            //获取ActionContext实例,通过它来访问Servlet API

            ActionContext context = ActionContext.getContext();

            //session中是否已经存放了用户名,如果存放了:说明已经登录了;

//否则说明是第一次登录成功

            if(null != context.getSession().get("uName")){

                msg = this.userName + ":你已经登录过了!!!";

            }else{

                context.getSession().put("uName"this.userName);

            }

           

            returnthis.SUCCESS;

        }else{

            msg = "登录失败,用户名或密码错";

            returnthis.ERROR;

        }

    }

   

    public String regist() throws Exception{

        //将用户名,密码添加到数据库中

        //...

        msg = "注册成功。";

        returnthis.SUCCESS;

   }

}

2.       struts.xml文件:没有什么变化,跟以前一样配置

<!DOCTYPE struts PUBLIC

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

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

<struts>

    <package name="my" extends="struts-default" namespace="/manage">

    <!-- 定义处理请求URLlogin.actionAction -->

        <action name="userOpt" class="org.qiujy.web.struts2.action.LoginAction">

        <!-- 定义处理结果字符串和资源之间的映射关系 -->

            <result name="success">/success.jsp</result>

            <result name="error">/error.jsp</result>

        </action>

    </package>

</struts>

3.       页面:

index.jsp

<%@ page language="java" pageEncoding="UTF-8"%>

<html>

 <head>

    <title>用户登录页面</title>

 </head>

 

 <body>

  <h2>用户入口</h2>

  <hr>

    <form action="manage/userOpt!login.action" method="post">

    <table border="1">

         <tr>

             <td>用户名:</td>

             <td><input type="text" name="userName"/></td>

         </tr>

         <tr>

             <td>密码:</td>

             <td><input type="password" name="password"/></td>

         </tr>

         <tr>

             <td colspan="2">

                 <input type="submit" value=确定 "/>

             </td>

         </tr>

    </table>

    </form>

 </body>

</html>

regist.jsp

<%@ page language="java" pageEncoding="UTF-8"%>

<html>

 <head>

    <title>用户注册页面</title>

 </head>

 

 <body>

  <h2>用户注册</h2>

  <hr>

    <form action="manage/userOpt!regist.action" method="post">

    <table border="1">

         <tr>

             <td>用户名:</td>

             <td><input type="text" name="userName"/></td>

         </tr>

         <tr>

             <td>密码:</td>

             <td><input type="password" name="password"/></td>

         </tr>

         <tr>

             <td colspan="2">

                 <input type="submit" value=注册 "/>

             </td>

         </tr>

    </table>

    </form>

 </body>

</html>

4.       运行结果:

4.5.3.2.    Action配置method属性:

Action类中的每一个处理方法都定义成一个逻辑Action方法。

<!DOCTYPE struts PUBLIC

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

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

<struts>

    <package name="my" extends="struts-default" namespace="/manage">

        <action name="userLogin" class="org.qiujy.web.struts2.action.LoginAction" method="login">

            <result name="success">/success.jsp</result>

            <result name="error">/error.jsp</result>

        </action>

       

        <action name="userRegist" class="org.qiujy.web.struts2.action.LoginAction"method="regist">

            <result name="success">/success.jsp</result>

            <result name="error">/error.jsp</result>

        </action>

    </package>

</struts>

如上,把LoginAction中的loginregist方法都配置成逻辑Action。要调用login方法,则相应的把index.jsp中表单元素的action设置为"manage/userLogin.action";要调用regist方法,把regist.jsp中表单元素的action设置为"manage/userRegist.action"

4.5.3.3.    使用通配符映射(wildcard mappings)方式:(*

struts.xml文件中配置<action…>元素时,它的nameclassmethod属性都可支持通配符,这种通配符的方式是另一种形式的动态方法调用。

当我们使用通配符定义Actionname属性时,相当于用一个元素action定义了多个逻辑Action

<action name="user_*"

class="org.qiujy.web.struts2.action.UserAction" method="{1}">

            <result name="success">/success.jsp</result>

            <result name="error">/error.jsp</result>

        </action>

如上,<action name=”user_*”>定义一系列请求URLuser_*.action模式的逻辑Action。同时method属性值为一个表达式{1},表示它的值是name属性值中第一个*的值。例如:用户请求URLuser_login.action时,将调用到UserAction类的login方法;用户请求URLuser_regist.action时,将调用到UserAction类的regist方法。

 

4.6.    配置处理结果

Struts2Action处理完用户请求后,将返回一个普通字符串,整个普通字符串就是一个视图名。Struts2通过配置逻辑视图名和物理视图资源之间的映射关系,一旦系统收到Action返回的某个逻辑视图名,系统就会把对应的物理视图资源呈现给浏览者。

4.6.1.    配置处理结果:

Struts2Action处理用户请求结束后,返回一个普通字符串-逻辑视图名,必须在struts.xml文件中完成逻辑视图和物理视图资源的映射,才可让系统转到实际的视图资源。

Struts2通过在struts.xml文件中使用<result…/>元素来配置结果。Struts2提供了两种结果。

局部结果:将<result …/>作为<action …>元素的子元素配置。

全局结果:将<result …/>作为<global-results…>元素的子元素配置。

package元素中配置<global-results>子元素:

<global-results>

 <result name="error">/Error.jsp</result>

 <result name="invalid.token">/Error.jsp</result>

 <result name="login" type="redirect-action">Logon!input</result>

</global-results>

4.6.2.    处理结果类型:

Struts2提供了对不同种类返回结果的支持,常见的有JSPFreeMarkerVelocity等。

Struts2支持的不同类型的返回结果为:

名字

说明

chain

用来处理Action

dispatcher

用来转向页面,通常处理JSP,这是默认的结果类型

freeMarker

处理FreeMarker模板

httpHeader

用来控制特殊的Http行为

redirect

重定向到一个URL

redirect-action

重定向到一个Action

stream

向浏览器发送InputSream对象,通常用来处理文件下载

velocity

处理Velocity模板

xslt

处理XML/XLST模板

plaintext

显示原始文件内容,例如文件源代码

tiles

结合Tile使用

另外第三方的Result类型还包括JasperReportsPlugin,专门用来处理JasperReport类型的报表输出;JfreechartPluginJSF Plugin

4.6.3.    动态返回结果

有些时候,只有当Action执行完毕的时候我们才知道要返回哪个结果,这个时候我们可以在Action内部定义一个属性,这个属性用来存储Action执行完毕之后的result值,例如:

private String nextAction;

public String getNextAction() {

    return nextAction;

}

strutx.xml配置文件中,我们可以使用${nextAction}来引用到Action中的属性,通过${nextAction}表示的内容来动态的返回结果,例如:

<action name="fragment" class="FragmentAction">

 <result name="next" type="redirect-action">${nextAction}</result>

</action>

 

上述Actionexecute方法返回next的时候,还需要根据nextAction的属性来判断具体定位到哪个Action

 

4.7属性驱动和模型驱动

不管属性驱动还是模型驱动,Struts2框架都是通过拦截器负责提取请求参数,并将请求数据封装到相应的Action实例的属性或专门的模型的属性。

4.7.1.    属性驱动:

属性驱动就是属性(property)作为贯穿MVC流程的信息携带者。简单的说,就是使用Action实例来封装请求参数和处理结果信息。前面我们做的示例都属于属性驱动模式。

4.7.2.    模型驱动:

模型驱动就是使用单独的javaBean作为贯穿整个MVC流程的信息携带者。也就是说,使用单独的VO(值对象)来封装请求参数和处理结果信息。

示例:继续修改用户登录验证:

1.       新增一用户域模型对象:User.java

package org.qiujy.domain;

publicclass User {

    private String userName;

    private String password;

    /**

     *@returntheuserName

     */

    public String getUserName() {

        returnuserName;

    }

    /**

     *@paramuserNametheuserNametoset

     */

    publicvoid setUserName(String userName) {

        this.userName = userName;

    }

    /**

     *@returnthepassword

     */

    public String getPassword() {

        returnpassword;

    }

    /**

     *@parampasswordthepasswordtoset

     */

    publicvoid setPassword(String password) {

        this.password = password;

    }

}

 

2.       业务控制器:UserAction.java

package org.qiujy.web.struts2.action;

import org.qiujy.domain.User;

import com.opensymphony.xwork2.ActionContext;

import com.opensymphony.xwork2.ActionSupport;

publicclass UserAction extends ActionSupport{

    //定义用于封装请求参数的模型对象

    private User user = new User();

    private String msg//结果信息属性

    /**

     *@returntheuser

     */

    public User getUser() {

        returnuser;

    }

    /**

     *@paramusertheusertoset

     */

    publicvoid setUser(User user) {

        this.user = user;

    }

    /**

     *@returnthemsg

     */

    public String getMsg() {

        returnmsg;

    }

    /**

     *@parammsgthemsgtoset

     */

    publicvoid setMsg(String msg) {

        this.msg = msg;

    }

    /**

     *处理用户请求的login()方法

     *@return结果导航字符串

     *@throwsException

     */

    public String login() throws Exception{

        String userName = user.getUserName();

        String password = user.getPassword();

       

        if("test".equals(userName) && "test".equals(password)){

            msg = "登录成功,欢迎" + userName;

            //获取ActionContext实例,通过它来访问Servlet API

            ActionContext context = ActionContext.getContext();

            //session中是否已经存放了用户名,如果存放了:说明已经登录了;否则说明是第一次登录成功

            if(null != context.getSession().get("uName")){

                msg = userName + ":你已经登录过了!!!";

            }else{

                context.getSession().put("uName", userName);

            }

            returnthis.SUCCESS;

        }else{

            msg = "登录失败,用户名或密码错";

            returnthis.ERROR;

        }

    }

    public String regist() throws Exception{

        //将用户名,密码添加到数据库中

        //...

        msg = "注册成功。";

        returnthis.SUCCESS;

    }

}

 

3.       配置文件:struts.xml

<!DOCTYPE struts PUBLIC

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

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

<struts>

    <package name="my" extends="struts-default" namespace="/manage">

        <action name="userOpt" class="org.qiujy.web.struts2.action.UserAction">

            <result name="success">/success.jsp</result>

            <result name="error">/error.jsp</result>

        </action>

    </package>

</struts>

 

4.       页面:

index.jsp

<%@ page language="java" pageEncoding="UTF-8"%>

<html>

 <head>

    <title>用户登录页面</title>

 </head>

 <body>

  <h2>用户入口</h2>

  <hr>

    <form action="manage/userOpt!login.action" method="post">

    <table border="1">

         <tr>

             <td>用户名:</td>

             <td><input type="text" name="user.userName"/></td>

         </tr>

         <tr>

             <td>密码:</td>

         <td><input type="password" name="user.password"/></td>

         </tr>

         <tr>

             <td colspan="2">

                 <input type="submit" value=确定 "/>

             </td>

         </tr>

    </table>

    </form>

 </body>

</html>

其它页面略。

5.       运行效果:同以前一样。

6.       源代码:

 

Struts2的异常处理机制:

任何成熟的MVC框架都应该提供成就的异常处理机制。Strut2也不例外。Struts2提供了一种声明式的异常处理方式。Struts2也是通过配置的拦截器来实现异常处理机制的。

Struts2的异常处理机制通过在struts.xml文件中配置<exception-mapping…>元素完成的,配置该元素时,需要指定两个属性:

exception:此属性指定该异常映射所设置的异常类型。

result:此属性指定Action出现该异常时,系统转入result属性所指向的结果。

    异常映射也分为两种:

l   局部异常映射:<exception-mapping…>元素作为<action…>元素的子元素配置。

l  全局异常映射:<exception-mapping…>元素作为<global-exception-mappings>元素的子元素配置。

    输出异常信息:

使用Struts2的标签来输出异常信息:

l <s:propertyvalue="exception.message"/>输出异常对象本身。

l <s:propertyvalue="exceptionStack"/>输出异常堆栈信息。

    示例:

还是修改用户登录示例:

1)      UserAciton.java中的regist方法改成:

public String regist() throws Exception{

        //将用户名,密码添加到数据库中

        //...

        //msg = "注册成功。";

        if(true){

           throw new java.sql.SQLException("没有数据库驱动程序");

       }

       

        return this.SUCCESS;

    }

 

2)      修改struts.xml文件:

<!DOCTYPE struts PUBLIC

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

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

<struts>

    <package name="my" extends="struts-default" namespace="/manage">

        <!-- 定义全局处理结果 -->

        <global-results>

        <!-- 逻辑名为sql的结果,映射到/exception.jsp页面 -->

        <result name="sql">/exception.jsp</result>

        </global-results>

       

        <global-exception-mappings>

        <!-- Action抛出SQLException异常时,转入名为sql的结果 -->

        <exception-mapping exception="java.sql.SQLException" result="sql"/>

        </global-exception-mappings>

       

        <action name="userOpt" class="org.qiujy.web.struts2.action.UserAction">

            <result name="success">/success.jsp</result>

            <result name="error">/error.jsp</result>

        </action>

    </package>

</struts>

 

3)      新增一页面:exception.jsp

<%@ page language="java" pageEncoding="utf-8"%>

<%@ taglib uri="/struts-tags" prefix="s" %>

<html>

 <head>

    <title>异常信息</title>

 </head>

 

 <body>

 <h2>

 出现异常啦

 </h2>

 <hr/>

   <h3 style="color:red">

   <!-- 获得异常对象 -->

    <s:property value="exception.message"/>

    </h3>

    <br/>

    <!-- 异常堆栈信息 -->

    <s:property value="exceptionStack"/>

</html>

 

4)      运行regist.jsp进行调试:

 

第五节:拦截器的配置和应用

1. 理解拦截器

5.1.0. 什么是拦截器:

拦截器,在AOPAspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。

Webwork的中文文档的解释为——拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。

谈到拦截器,还有一个词大家应该知道——拦截器链(Interceptor Chain,在Struts 2中称为拦截器栈InterceptorStack)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

5.1.1. 拦截器的实现原理:

大部分时候,拦截器方法都是通过代理的方式来调用的。Struts 2的拦截器实现相对简单。当请求到达Struts 2ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器。如下图:

 

5.1.2.拦截器的配置

Struts 2已经为您提供丰富多样的,功能齐全的拦截器实现。大家可以至struts2jar包内的struts-default.xml查看关于默认的拦截器与拦截器链的配置。

Struts2XWork)提供的拦截器的功能说明:

 

拦截器

名字

说明

Alias Interceptor

alias

在不同请求之间将请求参数在不同名字件转换,请求内容不变

Chaining Interceptor

chain

让前一个Action的属性可以被后一个Action访问,现在和chain类型的result<result type=”chain”>)结合使用。

Checkbox

Interceptor

checkbox

添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox

Cookies

Interceptor

cookies

使用配置的name,value来是指cookies

Conversion Error

Interceptor

conversionError

将错误从ActionContext中添加到Action的属性字段中。

Create Session Interceptor

createSession

自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。

Debugging

Interceptor

debugging

提供不同的调试用的页面来展现内部的数据状况。

Execute and Wait Interceptor

execAndWait

在后台执行Action,同时将用户带到一个中间的等待页面。

Exception Interceptor

exception

将异常定位到一个画面

File Upload Interceptor

fileUpload

提供文件上传功能

I18n Interceptor

i18n

记录用户选择的locale

Logger Interceptor

logger

输出Action的名字

Message Store Interceptor

store

存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。

Model Driven Interceptor

model-driven

如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。

Scoped Model Driven

scoped-model-driven

如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用ActionsetModel方法将其放入Action内部。

Parameters Interceptor

params

将请求中的参数设置到Action中去。

Prepare Interceptor

prepare

如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法。

Scope Interceptor

scope

Action状态存入sessionapplication的简单方法。

Servlet Config Interceptor

servletConfig

提供访问HttpServletRequestHttpServletResponse的方法,以Map的方式访问。

Static Parameters Interceptor

staticParams

struts.xml文件中将<action>中的<param>中的内容设置到对应的Action中。

Roles Interceptor

roles

确定用户是否具有JAAS指定的Role,否则不予执行。

Timer Interceptor

timer

输出Action执行的时间

Token Interceptor

token

通过Token来避免双击

Token Session Interceptor

tokenSession

Token Interceptor一样,不过双击的时候把请求的数据存储在Session

Validation Interceptor

validation

使用action-validation.xml文件中定义的内容校验提交的数据。

Workflow Interceptor

workflow

调用Actionvalidate方法,一旦有错误返回,重新定位到INPUT画面

Parameter Filter Interceptor

N/A

从参数列表中删除不必要的参数

Profiling Interceptor

profiling

通过参数激活profile

 

struts.xml文件中定义拦截器,拦截器栈:

<package name="my" extends="struts-default" namespace="/manage">

<interceptors>

<!-- 定义拦截器 -->

<interceptor name="拦截器名" class="拦截器实现类"/>

<!-- 定义拦截器栈 -->

<interceptor-stack name="拦截器栈名">

<interceptor-ref name="拦截器一"/>

<interceptor-ref name="拦截器二"/>

</interceptor-stack>

</interceptors>

......

</package>

3.使用拦截器

一旦定义了拦截器和拦截器栈后,就可以使用这个拦截器或拦截器栈来拦截Action了。拦截器的拦截行为将会在Actionexceute方法执行之前被执行。

<action name="userOpt" class="org.qiujy.web.struts2.action.UserAction">

<result name="success">/success.jsp</result>

<result name="error">/error.jsp</result>

<!-- 使用拦截器,一般配置在result之后, -->

<!-- 引用系统默认的拦截器 -->

<interceptor-ref name="defaultStack"/>

<interceptor-ref name="拦截器名或拦截器栈名"/>

</action>

此处需要注意的是,如果为Action指定了一个拦截器,则系统默认的拦截器栈将会失去作用。为了继续使用默认拦截器,所以上面配置文件中手动引入了默认拦截器。

4.自定义拦截器

作为“框架(framework)”,可扩展性是不可或缺的。虽然,Struts2为我们提供如此丰富的拦截器实现,但是这并不意味我们失去创建自定义拦截器的能力,恰恰相反,在Struts2自定义拦截器是相当容易的一件事。

5.3. 实现拦截器类:

所有的Struts 2的拦截器都直接或间接实现接口com.opensymphony.xwork2.interceptor.Interceptor。该接口提供了三个方法:

1) void init(); 在该拦截器被初始化之后,在该拦截器执行拦截之前,系统回调该方法。对于每个拦截器而言,此方法只执行一次。

2) void destroy();该方法跟init()方法对应。在拦截器实例被销毁之前,系统将回调该方法。

3) Stringintercept(ActionInvocation invocation) throws Exception; 该方法是用户需要实现的拦截动作。该方法会返回一个字符串作为逻辑视图。

除此之外,继承类com.opensymphony.xwork2.interceptor.AbstractInterceptor是更简单的一种实现拦截器类的方式,因为此类提供了init()destroy()方法的空实现,这样我们只需要实现intercept方法。

5.4. 使用自定义拦截器:

两个步骤:

通过<interceptor…>元素来定义拦截器。

通过<interceptor-ref…>元素来使用拦截器。

5..5自定义拦截器示例

5.6. 问题描述:

使用自定义拦截器来完成用户权限的控制:当浏览者需要请求执行某个操作时,应用需要先检查浏览者是否登录,以及是否有足够的权限来执行该操作。

5.7. 实现权限控制拦截器类:

AuthorizationInterceptor.Java

package org.qiujy.common;

 

import java.util.Map;

 

import com.opensymphony.xwork2.Action;

import com.opensymphony.xwork2.ActionInvocation;

import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

 

/**

* 权限检查拦截器

*

@author qiujy

@version 1.0

*/

public class AuthorizationInterceptor extends AbstractInterceptor {

 

/*

* 拦截Action处理的拦截方法

*

*/

public String intercept(ActionInvocation invocation) throws Exception {

 

Map session = invocation.getInvocationContext().getSession();

String userName = (String) session.get("userName");

 

if (null != userName && userName.equals("test")) {

System.out.println("拦截器:合法用户登录---");

return invocation.invoke();

else {

System.out.println("拦截器:用户未登录---");

return Action.LOGIN;

}

}

}

 

5.6. 配置权限控制拦截器:

struts.xml

<!DOCTYPE struts PUBLIC

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

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

<struts>

<package name="my" extends="struts-default">

 

<interceptors>

<!-- 定义权限控制拦截器 -->

<interceptor name="authority" class="org.qiujy.common.AuthorizationInterceptor"/>

</interceptors>

 

<!-- 定义全局处理结果 -->

<global-results>

<!-- 逻辑名为login的结果,映射到/login.jsp页面 -->

<result name="login">/login.jsp</result>

</global-results>

 

<action name="listall" class="org.qiujy.web.struts2.action.UserAction" method="listAllUser">

<result name="success">/listall.jsp</result>

<!-- 使用拦截器 -->

<interceptor-ref name="defaultStack"/>

<interceptor-ref name="authority"/>

</action>

 

<action name="userOpt" class="org.qiujy.web.struts2.action.UserAction">

<result name="success">/success.jsp</result>

</action>

</package>

</struts>

其它页面见源代码。

5.7. 运行调试:

在浏览器地址栏直接输入http://localhost:8080/AuthorityInterceptorDemo/listall.action 来访问,此动作配置了权限拦截器,所有被转到登录页面。

 

登录后:

 

再访问http://localhost:8080/AuthorityInterceptorDemo/listall.action 这个链接:

 

如果为了简化struts.xml文件的配置,避免在每个Action重复配置该拦截器,可以将拦截器配置成了一个默认拦截器栈。如下:

<!DOCTYPE struts PUBLIC

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

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

<struts>

<package name="my" extends="struts-default">

 

<interceptors>

<!-- 定义权限控制拦截器 -->

<interceptor name="authority"

class="org.qiujy.common.AuthorizationInterceptor" />

<!-- 定义一个包含权限控制的拦截器栈 -->

<interceptor-stack name="mydefault">

<interceptor-ref name="defaultStack" />

<interceptor-ref name="authority" />

</interceptor-stack>

</interceptors>

 

<!-- 定义默认拦截器 -->

<default-interceptor-ref name="mydefault" />

 

<!-- 定义全局处理结果 -->

<global-results>

<!-- 逻辑名为login的结果,映射到/login.jsp页面 -->

<result name="login">/login.jsp</result>

</global-results>

 

<action name="listall"

class="org.qiujy.web.struts2.action.UserAction"

method="listAllUser">

<result name="success">/listall.jsp</result>

</action>

</package>

 

<package name="font" extends="struts-default">

<action name="userOpt" class="org.qiujy.web.struts2.action.UserAction">

<result name="success">/success.jsp</result>

</action>

</package>

</struts>

一旦在某个包下定义了默认拦截器栈,在该包下的所有action都会使用此拦截器栈。对于那些不想使用些拦截器栈的action,则应该将它放置在其它的包下。

注:拦截器与过滤器的区别:

过滤器(filter),是在Java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者struts的action前统一设置字符集,或者去除掉一些非法字符

拦截器(interceptor),是在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。

拦截器与过滤器的区别 :

1、拦截器是基于Java的反射机制的,而过滤器是基于函数回调。

2、拦截器不依赖与servlet容器,过滤器依赖与servlet容器。

3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。

4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。

5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次

6、执行顺序 :过滤前 -拦截前 - Action处理 - 拦截后 - 过滤后。

过滤是一个横向的过程,首先把客户端提交的内容进行过滤(例如未登录用户不能访问内部页面的处理);过滤通过后,拦截器将检查用户提交数据的验证,做一些前期的数据处理,接着把处理后的数据发给对应的Action;Action处理完成返回后,拦截器还可以做其他过程(还没想到要做啥),再向上返回到过滤器的后续操作。

一个Filter可负责拦截多个请求或响应:一个请求或响应也可被多个请求拦截。

创建一个Filter只需两个步骤:

(1)创建Filter处理类:

(2)在web.xml文件中配置Filter 。

创建Filter必须实现javax.servlet.Filter 接口,在该接口中定义了三个方法。

• voidinit(FilterConfig config):用于完成Filter 的初始化。

• void destroy():用于Filter 销毁前,完成某些资源的回收。

• voiddoFilter(ServletRequest request, ServletResponse response,FilterChain chain):实现过滤功能,该方法就是对每个请求及响应增加的额外处理。 

过滤器Filter也具有生命周期:init()->doFilter()->destroy(),由部署文件中的filter元素驱动。在servlet2.4中,过滤器同样可以用于请求分派器,但须在web.xml中声明,<dispatcher>INCLUDE或FORWARD或REQUEST或ERROR</dispatcher>该元素位于filter-mapping中。

注:struts2中所体现出的设计模式有:

1.Command Pattern

基本定义:Command(Request)封装成对象,把发出命令(Invoker)的责任和执行命令(Receiver)的责任分割开,委派给不同的对象。

责任划分有什么好处?

责任约单一,内聚性越高,可重用的可能性越大,试想下,如果服务员不仅要点菜,还要去做菜,会是什么情景。

为什么把InvokerReceiver解耦好处多?

类之间的耦合越低,可扩展的可能性越高。解耦后,更换一个服务员并不会影响厨师的工作

那么把Request封装成对象具体是什么意思呢?

在遥控器RemoteControl例子中,比如我有一个开灯Request,那么就应该对应有一个LightOnCommand对象,关灯就应该有LightOffCommand

Web应用中,我有一个add userRequest,那么就应有一个AddUserCommand去处理请求,或者用strutsname convention就是AddUserAction.

那么在Struts中又是怎么运用Command Pattern的呢?

Client : FilterDispatcherServlet

Invoker:ActionInvocation

Command:Action (not a must in Struts2)

ConcreteCommandAddUserAction

Receiver:AddUserServiceImpl (not a must, depends on how many logic you want to put intoconcreteCommand)

Struts,你什么时候调用的setCommand()?

用户定义了ActionMappingstruts.xml里,在web Container启动时,ConfigurationManager就装载了struts.xml,当Request过来时,Struts Framework会根据当前的URL去找ConfigurationManager所对应的concreteCommand/Action,然后这个Action会被setActionInvocation

Command Interface是必须的吗?

struts1中,有一个Interface,所有的Action都必须是它的实现。但是在Struts2中,Action可以是任意的POJO,因为在Runtime的时候,具体的Action是什么,该调用它的什么方法,都可以通过配置文件(MetaDataJava Reflection来实现。这种新方式的好处是POJO Action没有了对框架的依赖,测试将会更加容易。缺点是因为没有interface的约束,调用Action的什么方法完全取决于默认值(比如execute)或是配置文件中的配置。若设置不妥,只有在Runtime的时候才能发现错误。

Receiver 是必须得吗?

不是,取决于你Action的厚度,如果你想让Action很轻的话,那么通常你会在Action中使用UserService.addUser()去做事情,此时的UserService就是Receiver。把Action设计的厚点,直接把addUserlogic放在Action中也是可以的。

Struts2中运用了command的思想,但并没有严格的按照其经典模型实现,而是做了些变通,这些变通乍看起来可能是有点违背设计原则,比如说取消了Action Interface,这不是反模式吗,反面向接口的编程吗,但仔细想想,这里我们真的需要这个接口吗?通过配置文件+Reflection,我们同样可以做到在Runtime的时候给ActionInvocation注入不同的Action的目的。而接口却增加了用户实现对框架的依赖,降低了程序的可测性,所以这样的变通其实是有积极意义的,虽然我们损失了一点点接口作为契约所带来的好处。

2. Interceptor Pattern

于其说这是模式,不如说这是AOPPipeline思想的结合,只不过Struts2中的实现非常的精巧,我不得不说这应该作为一个模式来推广。

AOP所谓的AOP就是preProcess and postProcess, 系统应用中,许多地方是需要面向切面的,比如logauthenticationetc...java Dynamic Proxy通常也是实现AOP的方式,但它是通过Java Reflection的机制,动态的产生一个被修改过的method,从而达到预处理和后处理的目的,个人觉得这种方式粗暴不够优雅,并且可扩展性不好。不过其优点是它是内嵌在JDK中的AOP,使用比较方面,对于那些不需要扩展性的需求,动态代理也是可以考虑的。

Pipeline:分层就是把复杂的问题分层多个层次,每一层只处理问题的一小部分。通信的7层模型就是典型的范例

Interceptor Pattern 不仅为系统进行分层,而且还提供了AOP的处理,此外,还以一种plug-in的方式为用户提供了无限扩展的可能。

应该怎么实现?

Interceptor的调用过程类似于一个栈式调用,所以想到递归是很自然的,这既避免了像Dynamic Proxy那样的classhack,又提供了更好的扩展性。

 还是以Struts中的类作为例子,其类图如下:

调用过程:

Pseudo 代码:

ActionInvocation

[java] view plain copy

1.   public Result invoke(){  

2.      if( interceptors.hasNext() ){  

3.           Interceptor interceptor = interceptors.next();  

4.          result = interceptor.intercept(this);  

5.       }  

6.      else {  

7.           action.execute();//如果没有更多的Interceptor,停止递归,调用action  

8.      }  

9.   }  

InterceptorImpl

[java] view plain copy

1.   public SomeInterceptor implements Interceptor{  

2.      public Result intercept(ActionInvocation actionInvocation){       

3.          //pre-processing  

4.          

5.           // 递归调用  

6.          result = actionInvocation.invoke();  

7.     

8.         //post-processing  

9.            

10.       return result;  

11.     }  

12.}  

先后接触到命令(Command)模式、ThreadLocal模式(严格上来说不算入设计模式中)、装饰(Decorator)模式、策略(Strategy)模式、构造(Builder)模式、责任链(Chain Of Responsibility)模式(在过滤器和拦截器中体现)、适配器模式、代理(Proxy)模式(action中体现)、模板方法模式、组合模式、单例模式(线程安全threadlocal)等等。

 

第六节:struts2的文件上传和下载

struts2没有提供自己的请求解析器,也就是说,struts2不会自己去处理multipart/form-data的请求,它需要调用其他请求解析器,将HTTP请求中的表单域解析出来,但struts2在原有的上传解析器上作了进一步封装,更进一步简化了文件上传,Struts2的struts.properties配置文件中,配置struts2的上传文件解析器struts.multipart.parser=jakarta(srtuts2默认),也可以设置为常用的cos,pell等。

struts2实现上传下载所必须的2个jar包:commons-fileupload-xxx.jarcommons-io-xxx.jar

一、文件上传

  1.  单文件上传

(1)单文件上传表单视图:

[java] viewplain copy

1.   <body>  

2.   <form action="/test/upload.action" enctype="multipart/form-data" method="post">  

3.        <input name="uploadfile" type="file">  

4.       <input type="submit" value="上传">  

5.    </form>  

6.  </body>  

注意:表单必须设置enctype="multipart/form-data"method="post"属性,否则上传会出错。

2. 单文件上传Action

[java] viewplain copy

1.   /** 

2.   * 文件上传类 

3.    * @author Ye 

4.   * 

5.    */    

6.   public class UploadAction extends ActionSupport{  

7.     

8.          <!--获取上传文件,名称必须和表单file控件名相同-->       

9.            private File uploadfile;  

10.  

11.          <!--获取上传文件名,命名格式:表单file控件名+FileName(固定)-->      

12.         private String uploadfileFileName;  

13.   

14.         //获取上传文件类型,命名格式:表单file控件名+ContentType(固定  

15.          private String uploadfileContentType;       

16.  

17.          ...//省略属性的gettersetter方法  

18.        

19.          //方法一:使用FileUtilscopyFile来实现文件上传  

20.         public String upload() throws IOException  

21.          {  

22.                 //设置上传文件目录    

23.                  String realpath = ServletActionContext.getServletContext().getRealPath("/image");  

24.  

25.                  //判断上传文件是否为空      

26.                 if(uploadfile!=null)  

27.                  {  

28.                         //设置目标文件(根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例)  

29.                          File savefile = new File(realpath,uploadfileFileName);  

30.  

31.                          // 判断上传目录是否存在            

32.                         if(!savefile.getParentFile().exists())  

33.                                savefile.getParentFile().mkdirs();  

34.  

35.                          //把文件uploadfile 拷贝到 savefile ,FileUtils类需要commons-io-x.x.x.jar包支持  

36.                         FileUtils.copyFile(uploadfile,savefile);  

37.   

38.                         //设置request对象值       

39.                          ActionContext.getContext().put("message", "上传成功!");  

40.                 }  

41.                  return "success";  

42.          }  

43.   

44.          //方法二:使用文件流来实现文件上传  

45.           public String upload() throws IOException  

46.          {  

47.                 FileOutputStream fos = new FileOutputStream("D:\\"+uploadfileFileName);  

48.          

49.                 FileInputStream fis = new FileInputStream(uploadfile);  

50.          

51.                 byte[] buffer = new byte[1024];  

52.          

53.                 int len = 0;  

54.          

55.                 while((len=fis.read(buffer))>0)  

56.                {  

57.                    fos.write(buffer,0,len);  

58.                }  

59.                 return "success";  

60.           }  

61.   

62. }  

注意:记得Action类要继承ActionSupport

3.  配置struts.xml文件

[html] viewplain copy

1.   <package name="hello" namespace="/test" extends="struts-default">  

2.      <action name="upload" class="action.UploadAction" method="upload">    

3.        

4.          <!-- struts.xml文件中使用constant元素指定在全局的struts.properties文件中自定义出错信息,value值为*.properties类型的文件名 -->  

5.           <constant name="struts.custom.i18n.resources" value="struts"></constant>   

6.    

7.           <!-- 配置fileUpload的拦截器 -->  

8.          <interceptor-ref name="fileUpload">  

9.      

10.           <!-- 配置允许上传的文件类型 -->  

11.            <param name="allowedTypes">image/bmp,image/gif,image/jpg</param>      

12.        

13.            <!--配置允许上传文件的扩展名,如果有多个用","隔开 -->      

14.           <param name="allowedExtensions">txt,excel,ppt</param>    

15.                 

16.           <!-- 配置允许上传的文件大小,最大为20k -->  

17.            <param name="maximumSize">20480</param>  

18.  

19.         </interceptor-ref>  

20.  

21.         <!-- 配置struts2的默认拦截器栈  -->  

22.        <interceptor-ref name="defaultStack"></interceptor-ref>  

23.   

24.       <result>/success.jsp</result>  

25.   

26.       <!-- 上传失败时返回的视图页面 -->  

27.        <result name="input">/index.jsp</result>     

28.  

29.      </action>  

30.</package>  

注意:(1) 拦截器实现要在UploadAction类中继承ActionSupport,否则拦截器无效。

          (2)必须显示配置引用Struts默认的拦截器栈:defaultStack

          (3) struts的默认上传文件大小为2M,可以用常量来控制扩大上传限制:

                 <constantname="struts.multipart.maxSize"value="104857600"></constant>   //设置上传上限为10M

                  真正的视频网站会使用插件而不是这种web上传的方式上传文件,因为Web上传的稳定性差。

          (4)在失败的页面视图中使用<s:fielderror/>标签可以输出上传失败的原因,错误信息默认是struts提供的英文字符,如果需要将其转换为中文字符,可在国际化资源文件中配置以下代码:

struts.properties

[html] viewplain copy

1.   struts.messages.error.content.type.not.allowed = 上传的文件类型不正确           

2.  struts.messages.error.file.too.large= 上传的文件太大  

3.   struts.messages.error.uploading=上传时出现未知错误  

  2.  多文件上传:

   多文件上传原理同单文件上传,主要是在action中定义File[]数组来接收多文件数据。

(1)多文件上传表单视图:

[html] viewplain copy

1.   <body>  

2.     <s:form action="multiUpload" method="post" namespace="/test3" enctype="multipart/form-data">  

3.          <s:file name="uploadfile" label="文件1"></s:file>  

4.         <s:file name="uploadfile" label="文件2"></s:file>  

5.          <s:file name="uploadfile" label="文件3"></s:file>  

6.         <s:file name="uploadfile" label="文件4"></s:file>  

7.          <s:submit value="上传"></s:submit>  

8.      </s:form>  

9.   </body>   

(2)多文件上传Action类:

[java] viewplain copy

1.   public class UploadAction extends ActionSupport {  

2.      private File[] uploadfile;  

3.       private String[] uploadfileFileName;  

4.      private String[] uploadfileContentType;  

5.     

6.          ...//省略属性的gettersetter方法  

7.     

8.          //方法一:  

9.       public String upload() throws IOException {  

10.        String realpath = ServletActionContext.getServletContext().getRealPath("/image");  

11.         if (uploadfile != null) {  

12.            File savepath = new File(realpath);  

13.             if (!savepath.exists())  

14.                savepath.mkdirs();  

15.             for (int i = 0; i < uploadfile.length; i++) {  

16.                File savefile = new File(realpath, uploadfileFileName[i]);  

17.                 FileUtils.copyFile(uploadfile[i], savefile);  

18.            }  

19.             ActionContext.getContext().put("message", "上传成功!");  

20.        }  

21.         return "success";  

22.    }  

23.   

24.        //方法二:  

25.         public String upload() throws IOException  

26.        {  

27.              for(int i = 0 ; i < uploadfile.length; i ++)  

28.             {  

29.                     FileOutputStream fos = new FileOutputStream("D:\\"+uploadfileFileName[i]);  

30.              

31.                     FileInputStream fis = new FileInputStream(uploadfile[i]);  

32.              

33.                     byte [] buffer = new byte[1024];  

34.              

35.                     int len = 0 ;  

36.              

37.                     while((len = fis.read(buffer))>0)  

38.                    {  

39.                        fos.write(buffer,0,len);  

40.                    }  

41.                       

42.               }  

43.                return "success";  

44.         }  

45.   

46. }  

二、文件下载

(1)文件下载Action类

[java] viewplain copy

1.   public class DownloadAction extends ActionSupport {  

2.        

3.           //downloaPath属性用于封装被下载文件的路径  

4.          private String downloadPath;  

5.     

6.          //初始化要保存的文件名  

7.           private String filename;  

8.    

9.           //文件保存路径  

10.        private String savePath;  

11.   

12.        public String getSavePath() {  

13.             return savePath;  

14.        }  

15.   

16.        public void setSavePath(String savePath) {  

17.             this.savePath = savePath;  

18.        }    

19.        

20.        public String getFilename() {  

21.                return filename;  

22.        }  

23.   

24.        public void setFilename(String filename) {  

25.                this.filename = filename;  

26.        }  

27.   

28.        public void setDownloadPath(String downloadPath) {  

29.         this.downloadPath = downloadPath;  

30.        }  

31.   

32.       // 隐含属性 targetFile ,用于封装下载文件  

33.     public InputStream getTargetFile() throws FileNotFoundException  

34.    {  

35.         return new FileInputStream(downloadPath);  

36.    }  

37.       

38.    public String execute()  

39.     {  

40.        return "success";  

41.     }  

42.  

43. }  

(2)配置struts.xml文件:

[html] viewplain copy

1.   <package name="test3_download" extends="struts-default">   

2.    <action name="download" class="com.action.DownloadAction" method="execute">  

3.       <!-- Action类中的文件路径参数设置初始值 -->  

4.        <param name="downloadPath">${savePath}</param>  

5.            

6.           <!-- 设置一个stream类型的result -->  

7.          <result name="success" type="stream">  

8.               

9.             <!-- 设置下载文件的输入流属性名 -->  

10.            <param name="inputName">targetFile</param>  

11.  

12.            <!-- 设置下载文件的文件类型,配置后可以直接在浏览器上浏览,省略则会弹出是保存文件还是打开文件信息的对话框 -->  

13.          <!-- <param name="contentType">image/jpg</param> -->  

14.   

15.           <!-- 设置下载文件的文件名 -->  

16.            <param name="contentDisposition">attachment;filename=${filename}</param>  

17.  

18.            <!-- 设置下载文件的缓冲 -->  

19.           <param name="bufferSize">2048</param>  

20.       </result>  

21.   </action>  

22.  </package> 

 

原创粉丝点击