详谈 Struts2 的核心概念
本文将深入探讨Struts2 的核心概念,首先介绍的是Struts2 的体系结构和几个重要的配置文件,并会举例说明Struts2 的核心对象如何配置。然后介绍Struts2 最重要的3 个组成部分Action 、Result 、Interceptor (拦截器)的原理和使用方法。
1 术语概述
• Action 在Struts2 中是负责Web 应用程序中具体逻辑实现的。Action 是一个Java类,一般的继承于com.opensymphony.xwork.ActionSupport 类,这个类在Struts2 的Dispatcher 接受到HTTP 请求的时候被调用。
• 当一个action 执行完毕之后,它将返回一个返回码,譬如“SUCCESS ”“INPUT ”或者其他“返回代码”。这些“返回代码”通过查找struts.xml 中的定义告诉Struts2 下一步该做什么而这个下一步就称为result 。Struts2 支持许多种不同的result 类型,比如返回结果页面给用户。可选择的显示模板技术有JSP 、Velocity 或者是FreeMarker 。
2 Struts2 的体系结构
• Struts2 的核心体系结构如图5.1 所示。用户在Struts2 框架下只需编写自己的Action 类来处理逻辑、编写JSP 页面(或者其他方式)来展示用户界面和在struts.xml 配置映射关系就可以完成基本的业务流程。
3 Struts2 的配置文件
• Struts2 框架主要有两个核心配置文件:struts.properties 和struts.xml 。struts.xml 与Struts1 版本中的struts-config.xml 非常类似,主要负责管理应用中的Action 映射,以及该Action 包含的Result 定义等,而struts.properties 文件则定义了Struts2 框架的全局属性。所有的配置文件说明见表所示,图展示了几个配置文件所在位置和相互关系。
3.1 全局配置文件--struts.properties
• struts.properties 文件是一个标准的Properties 文件,该文件包含了系列的key-value 对象,每个key 就是一个Struts2 属性,该key 对应的value 就是一个Struts2 属性值。struts.properties 文件通常放在Web 应用的WEB-INF/classes 路径下,实际上,只要将该文件放在Web 应用的CLASSPATH 路径下,Struts2 框架就可以加载该文件。以下是一部分配置片段。
• struts.enable.DynamicMethodInvocation=false"
• struts.devMode=false
3.2 核心配置文件--struts.xml
• struts.xml 文件主要负责管理应用中的action 映射,以及该action 包含的result 定义等。在struts.properties 配置中的有一项struts.configuration.files ,这里可以看出struts.xml 这个文件名不一定是固定的,可以配置为其他文件名的。struts.xml 内容主要包括:Action 、Interceptor 、Packages 、Namespace 等。后面的章节将详细介绍如何配置这些元素。
• 在struts.xml 中可以使用 标签把内容分到几个文件中去。这里非常像JSP 中的 动作标签,可以把其他文件的内容导入进来,被导入的每个配置文件必须和struts.xml 文件有一样的格式。 标签的格式如下所示。
3.3 struts.xml 的缺省实现
• struts-default.xml 这个文件被包含在struts2-core.jar 中,文件名已经可以看出这个文件的作用是struts.xml 的缺省配置,它将自动被加载然后导入到struts.xml 中去。代码5-1 是struts-default.xml 的部分片段。
3.4 Velocity 模板规则文件
• 如果在程序中使用了Velocity (一个基于java 的模板引擎,可以替代JSP 作为显示页面)可以把文件velocity.properties 放到classpath 中去,系统将自动加载。同时还要配置struts-default.vm 文件,
• 代码 velocity.properties
• # Velocity 资源定义.
• velocimacro.library = action-default.vm , tigris-macros.vm , myapp.vm
4 struts.xml 的配置
• Struts2 绝大多数的配置都是在struts.xml 中完成的,学习struts.xml 文件是学习使用struts2 的基础。本节将详细讲述如何在struts.xml 中定义和配置各种元素。
4.1 action 配置
• action 是Struts2 的基础“工作单元”。配置一个基本的action 只需要两个信息:action 名字和对应的action 类,这两部分就建立了一个最简单的action 配置。属性“method ”用来告诉Struts2 调用action 的那个方法。在action 处理之后一般的需要展示处理结果给用户,所以还需要把action 和result 映射在一起。如代码所示。
• 代码 Action 配置:struts.xml
• <! —Struts action 配置-->
•<action name="helloWorld" class="example.HelloWorld" method= ”doWork ”>
•<result name="failure" path="Error.jsp"/>
•<result name= ”ok ” path="HelloWorld.jsp"/>
•</action>
4.3 拦截器(interceptor )配置
• interceptor 是能在一个action 执行的前后执行的代码。它是做Web 应用程序时很有用的工具。最常见的由Interceptor 实现的功能如:安全检查( 确保访问者是登陆用户) 、跟踪日志( 记录每个action) 、效率瓶颈检查( 记录每个action 开始和结束的时间以检查程序中的瓶颈) 。也可以把interceptor 连在一起组成interceptor 栈(interceptor-stack )。比如在action 执行前同时做登陆检查,安全检查和记录日志,可以定义一个interceptor 的栈。interceptor 必须事先定义好,然后可以连在一起组成一个栈。如代码5-7 所示,定义了一个interceptor 和一个interceptor 栈。
4.4 包(package )配置
• 所谓packages 就是把actions 、results 、results 、types 、interceptors 这些元素打包到一个逻辑单元中去,从概念上讲,packages 就更像一个程序中的对象,可以被其他子包从写,而且可以拥有自己独立的部分。Name 属性是packages 的必填元素,它作为一个关键字被后边的包引用;extends 元素是可选的,它允许包扩展一个和多个前边定义的包。Abstract 元素是可选的,如抽象类和抽象函数一样它是必须被继承的,可以申明一个不包含actions 的package 。
4.5 命名空间(Namespace )配置
• 命名空间属性允许把action 配置分成不同的命名空间,这样可以使功能不同action 中使用相同的名字。默认命名空间用“”( 空字符串) 表示。如果系统在指定的命名空间中没有找到某个action ,就会到默认命名空间中查找。可以在所有用"extends" 扩展的命名空间外配置全局action 不指定命名空间。
• Struts2 中有以“/ ”命名的根命名空间,它是请求直接来自应用程序根路径的时候的命名空间。和其他命名空间一样,如果在根命名空间中没有所需的action 别名,系统会回到默认命名空间中查找。如代码5-9 所示,这里使用了默认命名空间、“/ ”和声明了的命名空间“barspace ”
4.6 在struts.xml 中定义Bean
• 在struts.xml 中还可以作JavaBean 的定义如下: s
<!-- 在struts.xml 中定义Bean --->
<struts>
<bean type="com.opensymphony.xwork2.ObjectFactory"
name="myfactory"
class="com.company.myapp.MyObjectFactory" />
</struts>
4.7 在struts.xml 中使用通配符
• 当配置文件中action mapping 的数量很多的时候,使用通配符是一个很好的办法,可以将一些相似的mapping 绑在一起,用一个比较通用的mapping 来表示。在路径中用* 来代替变化的部分,而action 的处理类和JSP 中{1} 刚好是代替这个变量。
• <!-- 在struts.xml 中使用通配符-->
<action name="/edit* ” class="example.Edit{1}Action">
<result name="failure" path="/mainMenu.jsp"/>
<result name= ”ok ” path="//{1/}.jsp"/>
</action>
</result>
5 实现Action
• Action 是Struts2 编程的核心部分,反映了对Web 应用程序的功能需求。Action 在MVC 模式中担任控制部分的角色,在Struts2 中也使用的最多。每个请求的动作都对应于一个相应的action ,action 还可以负责存储数据/ 状态(以getter 和setter 的方式)并且执行逻辑处理。
• 在本章中将关注如何实现action ,以及action 如何提供Web 应用程序中所需的通用功能。除了Action 接口之外,Struts2 的action 也可以选择实现其他可选择的接口,从而使action 能够提供诸如国际化、校验、负责工作流和错误信息处理等功能。ActionSupport 基类实现了Action 接口并提供了大部分可选择口默认实现,将在本章深入讲述这个类。除此之外,也将探讨action 是如何通过使用JavaBean 属性提供输入和输出的,最后将介绍如何处理文件上传。
5.1 实现Aciton 接口
• Struts2 的Action 接口来源于WebWork ,全包名为com.opensymphony.xwork2.Action 如代码5-10 所示。在Struts2 中定义action 类时已经可以不实现Aciton 接口,Struts2 会以反射的方式来调用action 类。
5.2 扩展ActionSupport 类
• ActionSupport 是一个让action 类能够更快开始工作的基类。它包含了action 能够提供的许多可选服务的默认实现,让开发者更容易地开始开发自己的action 类,不需要在为这些可选服务提供具体实现了。同时能够改写可选择接口的任意一个方法实现并保持其他方法的默认实现。由于ActionSupport 预建了许多开箱即用的功能,建议读者创建自己的action 时都扩展ActionSupport 类。ActionSupport 实现了以下可选择接口,
5.3 实现基本校验
• 通常在执行业务逻辑之前,校验用户提供的数据是十分表要的。这种字段校验包括“某个字段是必须的”、“某个字段必须大于某个值,小于某个值“等内容。为了自动执行校验,Sturts 2 提供了一种能够在excuete() 方法被调用之前调用其他方法对action 进行处理的机制,这个机制由com.opensymphony.xwork2.Validateable 接口提供,它包含了一个方法:
• public void validate()
• Validateable 接口为action 增加了一个标记,通过以上方法使得action 能够自动被校验。
• 保存和显示校验的错误信息有接口ValidationAware 来负责,这两个接口一般会同时使用。
5.4 使用本地的资源文件
• 本节中将介绍另外两个接口TextProvider 和LocalProvider ,它们都是为了使用本地的资源文件而设计的。
• 在Java 中用户语言和地区的信息被封装在java.util.Local 类中,而action 则通过一个定义与com.opensymphony.xwork.LocaleProvider 接口的方法判断使用哪个Locale 获取用于显示的信息文本,这个接口中只定义了一个方法:
• Public Locale getLocale()
• 在ActionSupport 中,这个方法的默认实现为:通过调用AcitonContext.getContext ().getLocale() 方法,利用ActionContext 获得locale 的值(关于ActionContext 的使用将在后面的章节详细描述)。Struts2 通过查询HttpServletRequest 对象并调用它的getLocale () 方法将Local 与action 调用联系起来。
5.5 用ActionContext 与Web 容器发生联系
• 在Action 的接口定义中,excute() 方法并没有HttpServletRequest 和HttpServletResponse 参数也就是说Struts2 的Action 不用去依赖于任何Web 容器(不像Struts 1 必须在Web 容器中才能运行),不用与那些JavaServlet 复杂的请求(Request )、响应(Response) 关联在一起。但在Web 应用程序开发中,往往需要在Action 里直接获取请求(Request) 或会话(Session )的一些信息,甚至需要直接对JavaServlet Http 的请求、响应操作。Struts2 提供了一个工具,用ActionContext 对象来与Web 容器发生联系。
• ActionContext (com.opensymphony.xwork.ActionContext )是Action 执行时的上下文,上下文可以把它看作是一个Map ,它存放是Action 在执行时需要用到的对象,比如:上下文放有请求的参数(Parameter )、会话(Session )、Servlet 上下文(ServletContext )、本地化(Locale )信息等。在每次执行Action 之前都会创建新的ActionContext ,ActionContext 是线程安全的,也就是说在同一个线程里ActionContext 里的属性是唯一的,这样的Action 就可以在多线程中使用。
5.6 高级输入
• 应用程序经常使用JavaBean 表示一个域中的对象,包括User 、Address 、Block 在内地的类就是这种JavaBean 很好的例子。而在Web 程序中很大一部工作都是将信息填充到这些对象中去和从Bean 中获取数据信息在网页中表现。本节将以一个完整的实例来说明Sturts 2 在这些方面提供了那些便利。
5.7 使用Model-Driven
• Struts2 中,提供了两种Action 驱动模式:Property-Driven (属性驱动),Model-Driven (模型驱动的)。
• 模型驱动的Action 很像Struts1 中的FormBean ,在传递过程中有一个单独的值对象来作为参数的载体,但在Struts2 中这个值对象不必再继承任何接口,只要普通JavaBean 就可以充当模型部分。很多情况下Bean 的定义已经存在了,而且是不能修改的(如从外部引入的类或者是已经被大量代码引用的类),如果必须实现某个接口才能作为FromBean ,不得不再新增一个类,Struts2 的这个改进非常及时。
5.8 使用Property-Driven
• Property-Driven 就是Action 将直接用自己的字段来充当FormBean 的功能,在Struts2 入门一章中,HelloReader 这个例子就是采用的这种方法,在Action 中直接包含了message 属性和它set 、get 方法。它一般用在页面表单比较简单的情况使用,而且可以直接把属性作为Action 的字段,这样就不用在另写FormBean ,减少了重复代码。
• 上一节的例子如果使用Property-Driven 方法,那就是将User 与action 类合并定义,把User 中的属性值直接转移到action 中去,在配置文件中也不必再增加modelDriven 这个过滤器。
6 Result 类型介绍
• Result 是在Action 执行完,一个结果返回后决定发生什么事的类。开发者可以自由的根据他们的应用和环境的需要创建自己的Result 类型。例如在Struts2 中Servlet 和Velocity 结果类型已经被创建用来显示web 应用程序的画面。本节将介绍Struts2 内置的几种Result 类型和如何自定义开发Result 。
6.1 内置Result 类型
• 所有的Result 类型都实现了com.opensymphony.xwork.Result 接口。这个接口是所有action 执行结果的通用接口,不管这个结果是用来显示一个网页还是产生一个E-mail ,发送一个JMS 消息还是别的。
• 在struts-default.xml 中定义了系统提供的缺省Result 类型,把它们映射为action 配置中可以引用的名字,在action 配置就就不用再使用长类名直接使用这些别名就可以了。
6.2 默认Result
• Dispatcher Result 是最常用的一种result ,它也是Struts2 默认的result ,又称为通用resut 。action 执行完后,请求会导向对应的View ,相当于 标签实现的跳转功能。将同一个HTTP 请求中的内容分发至某一个页面(dispatcher 类型的result 的使用) 只要配置文件包含了struts-default.xml ,而且package 继承了struts-default ,那么使用dispatcher result 并不需要其他设置。示例:
<result name="success" type="dispatcher">
<param name="location">foo.jsp</param>
</result>
6.3 页面跳转 Result
• Redirect Result 与Dispatcher Result 作用类似也是实现页面跳转。对上次的响应将重定向到指定的位置,可以理解为在客户端跳转用户又重新请求了一个新的URL 。redirect 是重新产生一个新的request ,因此原来request 保存的东西将不再有效,比如不能通过再requet.getAtrribute() 取得对象,也不能取得action 的实例、errors 、field errors 等。
• Redirect Result 与Dispatcher Result 的区别于源于JSP 篇中 标签与response.redeiret() 的区别。
6.4 创建action 链
• Chain Result 是一种result 类型,它基于自己的拦截器stack (堆栈)和result 调用一个action ,这样允许一个action 附带着原来的状态将请求转到目标action
• Struts2 提供把多个Action 按照预先定义好的顺序或者流程链接起来的能力。这个特性通过给指定的Action 设置一个Chain Result ,然后通过一个ChainingInterceptor 拦截目标Action 来实现。
6.5 整合各种View 技术
• Velocity 、Freemarker 、JasperReports 、xslt 这4 种result 都是为了整合不同的视图技术而设计的。
• 1 .Velocity Result:Velocity 是一个基于java 的模板引擎(template engine )。
• 2 .Freemarker Result:Freemarker 也是一个模板引擎,允许JavaServlet 保持图形设计同应用程序逻辑的分离,这是通过在模板中密封HTML 完成的。模板用servlet 提供的数据动态地生成 HTML 。
• 3 .JasperReports result:JasperReports 是一个基于Java 的开源报表工具,它可以在Java 环境下像其他IDE 报表工具一样来制作报表。
• 4 .XSLT Result:XSLT Result 用XSLT 来转换action 对象到XML 。
6.6 自定义result
• Struts2 也允许用户自定义自己的result 类型,只要实现com.opensymphony.xwork2.Result 接口就可以了。如代码5-29 所示,模拟了一种result 作用是根据处理结果将给指用户发送一份E-mail 。这个result 需要4 个参数to 、from 、subject 和body
7 拦截器(Interceptors )介绍
• 拦截器(Interceptor )是Struts2 的一个强有力的工具,有许多功能都是构建于它之上,如国际化、转换器,校验等。Interceptor 是Struts2 的一大特色,在执行action 之前和之后可以使请求通过一个或多个Interceptor 。多个连接器组合在一起实现某一个功能称为interceptor 链(Interceptor Chain ,在Struts2 中称为拦截器栈Interceptor Stack )。interceptor 链就是将interceptor 按一定的顺序联结成一条链。在访问被拦截的方法或字段时,interceptor 链中的interceptor 就会按其之前定义的顺序被调用。
7.1 Interceptor 的原理
• Struts2 的interceptor 实现相对简单。当请求到达Struts2 的ServletDispatcher 时,Struts2 会查找配置文件,并根据其配置实例化相对的interceptor 对象,然后串成一个列表(list ),最后一个一个地调用列表中的拦截器,
7.2 内置拦截器介绍
• Struts2 包含了许多内置的interceptor ,它们提供了很多核心功能和可选的高级特性。interceptor 在struts.default.xml 文件中被定义,而一些默认的interceptor 栈及interceptor 的命名也被定义其中。框架中提供了很多实用的Interceptor ,可以随时使用它们的名字来调用这些interceptor ,
7.3 使用内置interceptor
• 本节将介绍几种常用interceptor 的用法:
• 1 .使用timer 为action 即时
• 2 .使用logger 为aciton 提供日志
• 3 .使用校验
• 4 .准备action
• 5 .实现ModelDriven
• 6 .token 和token-session
7.4 内置拦截器栈介绍
• 除了内置的interceptor 之外,struts.xml 还包含了内置的interceptor 组合,可以通过具体的命名的interceptor 栈来使用它们。
7.5 自定义拦截器
• 自定义一个拦截器需要3 个步骤:
• (1 )自定义一个实现Interceptor 接口的类。
• (2 )在strutx.xml 中注册上一步中定义的拦截器。
• (3 )在需要使用的Action 中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action 都被这个拦截器拦截。
8 小结
• 本文讲述的是Struts2 的核心构成元素及其使用方法,使读者对Sturts 2 的体系结构有了一个清晰的认识。Struts2 是一个开放的系统,它的很多实现对用户来说都是透明的,它们在struts-default.xml 中配置,开发人员配置自己的元素在struts.xml 中,这个文件是可以拆分并按用户需要组织的。Struts2 的核心部分由action 、interceptor 、result3 个主要部分构成,Interceptor 是它最大特色。