4.10 在ActionMapping中使用通配符... 22
0 序
0.1猜想
本手册适合于有创建Java Web 应用经验的Web 开发人员.在开始学习Struts之前你应该了解如下的技术:
1. HTTP,HTML,User Agents
2. HTTP 请求/响应生命周期
3. Java语言和应用框架
4. JavaBeans
5. 属性文件和资源绑定
6. Java Servlets
7. Java Server Pages ,JSP Tag Librariy
8. XML
0.2 HTTP,HTML,User Agents
WWW是基于超文本传输协议和超文本标识语言建立的.
用户代理,比如浏览器,他们使用HTTP去请求一个文档然后格式化显示.
0.3 HTTP 请求/响应周期
对于Web开发者来说,理解HTTP的请求和响应周期是很重要的.你要使用HTTP发送一个请求,然后服务器给你一个响应.当你创建WEB应用时,你需要返回一个响应对象来回应请求.
0.4 Java语言和应用框架
Struts是用JAVA编写的.JAVA是面向对象的语言,而Struts充分利用了面向对象的技术.除此而外JAVA支持线程.如果你很好的掌握了JAVA,特别是面向对象编程和线程,这将会有益于最有效的使用Struts和这个手册.
0.5 JavaBeans
象大多数JAVA应用一样,大多数Struts对象都是JavaBean.遵循JavaBean设计模式使得Struts更容易被开发者和JAVA开发工具使用.尽管JavaBean的最初是用来开发可视元素的,但它对开发可重用组件也非常有益.
0.5.1反射和内省
反射是决定什么样的函数或域存在某对象中的方法.内省是反射的特殊形式.
使用内省就可以知道哪些方法将会被其他对象使用(如getter,setter)
Struts框架使用内省来转换HTTP参数到JAVABEAN的属性中和把JAVABEAN中的属性数据组装到HTML的域.
0.5.2 MAP
JAVABEAN把数据存在属性中.JAVABEAN虽然是灵活而功能强的对象,但它并不是编程者存储数据的唯一对象.另一个熟为人知的对象是java.util.Map.Map它是一个名字/值对的数据集合,它经常用来存储动态数据.
0.5.3 DynaBeans
DynaBeans集成了JAVABEAN 的扩展性和MAP的灵活性.编写JAVABEAN要创建一个新类,并且要为每个属性编写方法.DynaBeans则将属性放在XML中配置.
在Struts应用中,你可以使用DynaBeans来描述你的HTML Form,这个策略可以避免编写普通的JAVABEAN来存储很简单的属性.
0.6属性文件和资源绑定
JAVA应用包括WEB应用的配置经常使用属性文件来完成. Struts的消息资源都是通过资源绑定和属性文件实现的.
JAVA资源绑定使用一个或多个属性文件来实现应用的国际化.
0.7 JavaServlet
由于JAVA是面向对象的语言,因此JAVA平台已经将HTTP转换成对象的形式.这将会使JAVA 程序员关注于应用本身而不是HTTP.
HTTP提供了一个标准的机制来扩展服务器的功能,我们称之为CGI. 服务器将请求发送到CGI程序,CGI程序则返回一个响应.同样的任何JAVA服务器则会接收请求,然后转发到Servlet.
Servelt 是javax.servlet.http.HttpServlet
的子类
,
每个
Servlet
必须完成四个方法:
- public void init(ServletConfig config)
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- public void destroy()
Struts提供了一个可用的Servlet--org
.apache.struts.action.ActionServlet.
作为一个
Struts
开发者
,
你只需要编写对象供
ActionServlet
调用就行了
.
但是
,
理解
Servlet
并熟悉他在
JAVA WEB
应用中扮演的角色那是最好不过了
.
0.7.1 Servlets 和多线程
为了提高性能,Servlet设计程多线程.每个Servlet仅创建一个实例,每一个请求都传递到同一个对象.这将有利于Servlet容器充分的利用资源.因此doGet,doPos在编程时必须保证他是线程安全的.
0.7.2 Servlet Context
ServletContext(javax.servlet.ServletContext)定义了Web 应用中Servlet的视图.在Servlet中通过getServletConfig()
可以访问得到
,
在
JSP
中则通过隐式对象
application
得到
.ServletContext
提供了几个对于创建
Struts
应用来说非常有用的几个方法
:
1.
访问
WEB
应用资源
Servlet
通过
getResource(),getResourceAsStream()
可以访问
WEB
应用中的静态资源文件
.
2.Servlet Context
属性
Servlet
上下文可以存储
JAVA
对象到属性中
.
这些属性对整个
WEB
应用都可见
.
0.7.3
Servlet
请求
Servlet请求就是javax.servlet.http.HttpServletRequest,
通过它可以访问
HTTP
请求的所有信息
:
Cookies
通过
getCookies()
可以得到当前请求的所有
cookie
HTTP
头
HTTP
请求的头可以通过对应的名字来访问
.
你当然可以通过枚举来列出所有的头
.
参数
你可以通过参数来访问
HTTP
请求的
URL
的参数或表单中的内容
.
请求特性
HTTP
请求表单的提交方式
(GET/POST),
用的是什么协议
(HTTP/HTTPS)
请求
URI
信息
通过
getRequestURI()
可以得到最初的请求
URI,
除此之外
,
我们还可以得到
contextPath,servletPath,pathInfo
用户信息
如果你正使用容器来进行安全管理
,
你可以得到一个
Principal
对象来代表当前用户
,
并确认该用户是否拥有某叫角色的权限
.
Servlet
请求拥有请求级别的属性
,
与前面提到的应用级别属性类似
.
请求级别的属性经常用来传递状态信息到可视化组件
(
如
JSP).
Servlet
容器保证被
Servlet
处理的请求处于单线程内
.
因此你不必担心在访问
request
对象的属性时会有多线程的问题
.
0.7.4 Servlet响应
Servlet的功能就是接收请求,然后生成相应的响应. 这是通过调用javax.servlet.http.HttpServletResponse
的方法实现的
.
1.
设置头
你可以设置包含在响应中的头
.
最重要的头就是
Content-Type,
它用来告诉客户端内容的格式
,
比如
:text/html
代表
html,text/xml
代表
XML
2.
设置
Cookies
你可以加入
cookie
到当前的响应中
3.
发送错误响应
你可以使用
sendError()
发送一个
HTTP
错误编号
4.
重定向到其他资源
你可以使用
sendRedirect()
定向到另外一个
URL
使用
Response API
的一个最重要的原则就是
:
操作
Header,Cookie
的任何方法必须在第一次输出缓冲区满且发送到客户端前调用
.
0.7.5过滤
如果你的Servlet容器是基于Servlet规范2.3或更高,那么你就可以使用javax.servlet.Filter
来对请求和响应作些处理.许多filter聚合在一起,每一个filter都有机会来对请求和响应做处理.
Struts1.0,1.1,1.2只需要Servlet规范2.2,因此这些版本的Struts并不支持filter.
Struts从1.3开始就需要Servlet规范2.3的容器了.
0.7.6 Session
HTTP的一个关键特性就是无状态,因此我们不知道某个请求是否是来自同一用户的请求,这将会使跨请求的会话变得很艰难.
为了解决这个问题,Servelet实现了一个javax.servlet.http.HttpSession.Servlet
容器将采用
Cookie
或
URL Rewriting
来保证接邻近的请求包含
session id
来标识该请求是同一个
session.
因此保存在
session
中的属性可以被多个请求共享
.
为了不浪费资源
,Session
有一个可配置的超时时间设置
.
如果两个请求间的时间差超过了超时时间间隔
,
那么
session
中的数据会失效
.
你可以定义一个默认的超时时间在
WEB
应用部署描述文件中
,
或者你也可以通过
setMaxInactiveInterval()
动态改变它
.
与
request
不一样的是
,
你必须关心
session
的属性是线程安全的
,(bean
提供的方法
,
并不是
session
的
getAttribute
和
setAttribute)
比较重要的一点需要考虑的是
:
因为
session
的属性是占用内存的时间较长
,
因此在用户量大时
,
要考虑
session
的属性占用内存的大小
.
0.7.7
分发请求
1介绍
1.1Struts的历史
Servlet刚出道时,很多Java开发者意识到它将会比CGI更快,更有效,更容易移植.
但是通过println()将HTML输出到客户端是很烦人和容易出错的.于是人们在JSP中找到了答案,通过将JAVA代码与HTML混合在一起.
JAVA WEB应用开发随之也变得以JSP为中心点.这并不是坏事.关键的是它没有给WEB应用的流程控制及其它问题提供更多的解决方法.
许多聪明的开发者意识到可以将Servlet,JSP同时部署到WEB应用中.Servlet来做控制流程,JSP来生成HTML页面.一个新的名词Model 2 由此而生.
Model 2 对于SUN来说并不是新奇的事物.许多人指出Model 2遵循从Smalltalk MVC框架抽象出来的MVC模式.从而人们开始把Model 2 与MVC关联到一起.
1.2 MVC模式
在MVC模式中,流程由控制器(Controller)管理.控制器将请求委派到相应的处理器,处理器与Model连在一起,每个处理器在请求与Model之间充当一个适配的角色.Model封装了商业逻辑或状态.然后控制流程将会转到View. 转向动作可以由存储在数据库或文件中的转向定义来决定.这会让View和Model之间的关系松散,从而有利于应用的创建和维护.
1.2.1 Model(系统状态和商业逻辑JavaBean)
基于MVC的系统可以的Model可分为两部分:一个是系统的状态,另一个是改变这些状态的行为.
大多数系统将系统状态存储在JAVABEAN中.根据系统复杂度的不同,有些Beans是自包含的(也就是BEAN本身知道如何持久化数据),有些是通过其它方式取得数据(数据库,搜索引擎,EJB,LDAP).
在大型系统中JAVABEAN可能有很多方法来维护状态信息.
在小型系统中,这些JAVABEAN的方法极有可能放到Struts框架的Action
类中
.
如果商业逻辑很简单或者它不会在其它地方重复调用
,
这是可以的
.
Struts
提供了许多灵活的方式来访问
Model,
但我们强烈建议你将商业逻辑和
Action
类分离开来
.
1.2.2
视图
(View)-JSP
和可视组件
Struts应用的视图部分经常是基于JSP的.JSP可以包含HTML,XML,及标签.
Struts拥有一个标签库可以让你创建国际化的用户界面以及很好的与ActionForm
beans集成在一起. ActionForm
捕获,校验应用的输入数据.
1.2.3控制器-ActionServlet,ActionMapping
控制器负责接受来自客户端的请求,并且决定将执行哪个业务逻辑功能,然后再委托相应的View来显示用户界面.
在Struts中,主要的控制器组件就是ActionServlet,它有一组ActionMapping,
每一个
ActionMapping
会有某个请求
URL
的路径和该
URL
对应的
Action
类
.
每一个
Action
都是
org.apache.struts.action.Action
的子类
.Action
去调用业务逻辑类
,
最终将控制权交给
View
组件
.
1.3 Struts
控制流程
Struts用几个组件组成了MVC框架的控制层.它们由Controller servlet,开发者自定义的请求处理器(handler)和其它一些辅助类构成.
Struts的自定义标签库为View层提供直接的支持.有些标签可以访问Control层的对象,有些则是为了方便开发应用而写的.其他的标签库如JSTL也可以与Struts一起使用.
Model层对不同的项目来说有很多不同.Struts让model很容易被应用的后台逻辑所使用.
让我们来看看Controller,View,Model是如何融合在一起的.
当初始化时,Controller解释struts-config.xml
并且使用它部署一些控制层的对象
,
这些对象构成了
Struts
的基础
.
这个基础当然少不了
ActionMapping[org.apache.struts.action.ActionMappings
].
当Struts要将请求传递到框架的其他组件时它就得参考ActionMapping,请求将会转移到一个JSP或者Action--org.apache.struts.action.Action.
通常
,
一个请求先派发到
Action,
然后再到
JSP.
一个
ActionMapping
通常包含如下的属性
:
请求路径
(
或者
URI)
响应请求的对象
(Action
子类
)
其它所需要的属性
Action
对象可以处理请求和响应
,
或者将控制流转向到其它地方
.
例如
,
如果登陆成功
,
将转向到主页面
.
Action
对象可以访问应用的
Controller servlet,
当控制流发生改变时
,
他可以将
JAVABEAN
放在合适的上下文中供多个
Servlet
共享
.
例如
,
一个
Action
对象创建了一个购物车
Bean,
并且往购物车中加入所够物品
,
然后把这个
BEAN
放在
session
中
,
当转向到其他
URL
时
,
就可以用到先前创建的购物车
.
在
Struts
应用中
,
大多数的商业逻辑都放在
JAVABEAN
中
,Action
调用
JAVABEAN
的方法而不需要知道它是如何实现的
.
这样一来就封装了商业逻辑
,
而
Action
本身将集中到错误处理和转向
(Forward)
的处理上
.
JAVABEAN也可以用来管理输入表单,设计WEB应用的一个关键问题是如何保持和校验在两个请求之间的用户所输入的数据.有了Struts,你就可以用org.apache.struts.action.ActionForm
来存储和校验输入表单
.ActionForm
被自动保存在合适的上下文中
,
因此它可以被其他的对象共享
,
如
Action
对象
,JSP.
FormBean
可以被
JSP
用来收集用户数据
,
也可以由
Action
对象来校验用户所输入的数据
,
还可由
JSP
把数据组装到
HTML
的输入域中
.Struts
拥有共享的抛出错误
,
显示错误的机制
.
另外一个
Struts
的培植元素就是
ActionFormBeans --org.apache.struts.action.ActionFormBeans,
它是一个描述性对象的集合
,
主要用于运行时动态创建
ActionForm的实例.当一个URL映射需要ActionForm,Servlet就会通过名字去查找form-bean的描述对象,从而用它去创建ActionForm的实例.
下面是请求需要ActionForm时的事件序列:
- Controller servlet检索或者创建ActionForm
- Controller servlet 将bean传递到ActionObject
- 如果请求提交了一个输入页面,Action 对象可以检查数据.
- 如果请求将要创建一个输入界面, Action 对象将会组装数据到任何输入页面中.
2
创建模型组件
2.1概述
许多创建WEB应用的文档都关注于View,然而我们应该在Model的角度清晰定义需求的处理逻辑或过程.总之,Model的开发者将会创建JAVABEAN来实现功能需求.一个应用的JAVABEAN需要什么样的功能完全取决于需求,但它可以分为如下三类,在介绍他们之前,让我们先看一下JAVABEAN的作用域
2.2 JavaBean和作用域
在WEB应用中,JAVABEAN存储许多不同的属性集合中。每一个不同的属性集合都有不同的生命周期规则。该规则定义了生命周期和可见性我们称之为作用域(Scope),JSP定义的作用域如下:
1. page Bean在当前请求的单个JSP页面可见(相当于Servlet的service方法中的局部变量)
2. Request Bean不仅在单个页面可见,也可以在被页面包含(include)的页面或servlet中可见,或者转向(Forward)的页面可见。
3. Session Bean在特殊用户session的所有JSP或Servlet中可见。
4. Application Bean在WEB 应用中的所有JSP,或Servelt中可见。
必须记住的是JSP和Servlet在同一个WEB应用中共享同一组Bean。例如一个Bean在Servlet中存在request的属性中:
MyCart mycart = new MyCart(...);
request.setAttribute("cart", mycart);
Mycart在该Servlet转向的JSP页面中马上可见。你可以在JSP中如下声明它:
<jsp:useBean id="cart" scope="request"
class="com.mycompany.MyApp.MyCart"/>
2.3 ActionForm Bean
注意:ActionForm Bean的属性经常会对应Model中Bean的属性。Form bean应该被看作是Controller的组件,同样的,它能在Model和View两层之间传递数据。
Struts假定你已经为应用的Form定义了ActionForm.ActionForm也通常成为form bean(表单bean)。一个bean可以服务于一个form,也可一服务于多个form或者整个应用,这完全取决于你在设计form bean时的粒度。
如果你定义了ActionForm Bean在Struts的配置文件中,那么Struts框架会在执行Action对象的方法之前为你自动执行如下服务:
1.检查Bean是不是在适当的作用域
2.如果Bean不存在,则创建一个实例,并且加入到适当的作用域上下文中。
3.如果请求的参数的名字与Bean中的属性名字一致,对应的setter方法就会被调用。
4.填满数据的ActionForm Bean将会传到org.apache.struts.Action的excute方法,因而系统状态Bean和商业逻辑Bean就会有数据可用。
2.4系统状态Bean
一个系统的状态实际上是由许多JAVABEAN组成的。例如一个购物车系统对于用户有个购物车的Bean,它里面有用户当前购买的商品。系统里还有Bean来保存用户的信息
2.5商业逻辑Bean
你需要封装你的商业逻辑在JAVABEAN的方法中。这些方法可能放在系统状态Bean中,但尽可能用一个单独类来实现。如果是用单独的类来实现,你可能需要将系统状态Bean 作为参数传到商业逻辑Bean的方法中。
3 创建视图组件
3.1 概述
这章将集中在Sruts的视图部分。很多应用依赖于JSP来创建展现层。Struts包含了支持国际化应用的标签,也有提供输入表单交互的标签。
3.2 国际化信息
多年以前,应用开发者只需要支持自己国家的公民来使用软件,他们可能使用一种语言或一种度量衡标准。随着WEB应用在全世界快速增长,应用的国界也逐渐消失。因此应用需要国际化和本地化。
Struts在JAVA平台的基础上来支持国际化和本地化。回顾一下我们熟悉的概念:
1. Locale - JAVA中支持国际化的最基本的类,它代表一个国家和语言,同时也假定了数字或时间的格式。
2.ResourceBundle - JAVA中工具类用来支持不同国家的信息。
3.PropertyResourceBundle - ResourceBundle的一个实现,它允许你使用”name=value”的格式来初始化属性文件。这对于准备WEB应用的信息是非常方便的,因为这些信息都是文本。
4.MessageFormat - java.text.MessageFormat允许你在运行时用参数替换信息字符串中的部分信息。当某些信息在运行时将会显示不同的顺序或不同的语言时,MessageFormat将会非常有用。占位符{0}将被第一个参数代替,{1}将被第二个参数代替,依此类推。
5.MessageResource - org.apache.struts.util.MessageResources类把所有的资源绑定当作一个数据库来看待,允许你请求不同的Locale的信息字符串(通常与当前用户相关),而不是服务器上的Locale。
必须注意的是:Struts框架支持国际化仅仅限于展现给用户的文本和图片,支持本地化的输入方法(例如使用何种语言)则由客户端的设备(通常是浏览器)实现。
让我们来了解一下将属性文件绑定到WEB应用的步骤,
假如你的代码在com.mycompany.mypackage,为了实现资源绑定,你需要创建如下文件:
1.MyApplication.properties – 默认语言的所有信息,如果默认语言是英语,在你的属性文件中可能有如下入口prompt.hello=Hello
2.MyApplication_xx.properties - ISO语言代码所对应语言的所有信息,如果语言是法语,属性文件中可能有如下入口prompt.hello=Bonjour
当你在WEB应用的部署描述文件中定义Controller Servlet时,你可能需要把应用的资源绑定名称作为初始化参数传进去。
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>
com.mycompany.mypackage.MyResources
</param-value>
</init-param>
<!-- ... -->
</servlet>
最重要的是要保证资源绑定能在你的应用的CLASSPATH找得到。另外一个方法就是把MyResources.properties放在classes目录,然后用MyResoureces来引用就行了。
3.3Forms 和 FormBeans的交互
很多WEB开发者使用HTML本身的功能来实现表单,比如Input。用户期望交互应用能具备某些必备的功能,比如错误处理(当出现错误时,只需要修改导致错误出现的地方,而不要重新输入页面或表单中的信息)。
用标准的HTML和JSP来实现错误处理是麻烦而且很乏味。一个输入用户名的界面在JSP中如下表示:
<input type="text" name="username"
value="<%= loginBean.getUsername() >"/>
输入如上的字符对于没有编程理念的HTML编写者来说是很容易出错的。Struts在提供了一些自定义标签来实现上述的功能:
<html:text property="username"/>;
上述表示法并不要很明显的指定与JAVABEAN相关联,它的处理完全由框架实现。
3.3.1索引和映射属性
JSP页面中的标签属性可以引用JAVABEAN中的属性。大多数都是引用简单类型或简单对象。但Struts允许你利用Jakarta Commons Beanutils library提供的功能来引用Array,Conllection,Map的单个项。
3.3.2 输入域的支持
Srust支持如下的输入域:
1.Checkbox
2.Hidden field
3.Password
4.Radio
5.Reset
6.Select
7.Option
8.Options
9.Submit
10. Text
11. TextArea
在任何情况下,这些标签必须嵌套在 form标签中,只有这样输入域才知道它要使用那个Bean 。
3.3.3 一些有用的标签
Iterate
Present
Nopresent
Link
Img
Parametr
3.3.4 自动表单校验
Strust提供了校验表单的功能,为了使用此功能,你需要重载ActionForm的如下方法:
validate(ActionMapping mapping,HttpServletRequest request);
当Bean的属性的数据被填充后,在调用Action的excute方法之前,Controller Servlet来调用validate方法。Validate方法的执行有如下几种情况:
1.执行校验但没有错误 返回NULL或0长度的ActionErrors实例,Controller Servlet将继续执行Action的方法。
2.执行校验但有错误 返回ActionErrors实例,每一个ActionError有对应的错误消息关键值。Controller Servlet将把错误消息数组保存到请求级别的上下文中供<html:errors>标签使用。
3.3.5 Struts校验器
为表单配置校验器是很简单的。
1.ActionForm须继承ValidatorForm
2.表单所在的JSP页面须包含<html:javascript>标签用于客户端的校验
3.你需要定义校验规则在XML文件中
<form-validation>
<formset>
<form name="logonForm">
<field property="username" depends="required">
<msg name="required" key="error.username"/>
</field>
</form>
</formset>
</form-validation>
4.最后,你必须在struts-config.xml文件中激活ValidatorPlugin
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,
/WEB-INF/validation.xml"/>
</plug-in>
3.4 其他表示层技术
尽管外观可以用JSP或Struts的标签来实现,但你仍然应该考虑融合其他技术来提高组件的重用,减少维护工作量,减少错误。
3.4.1 特定应用标签
忽略
3.4.2使用包含组合页面
将一个页面放在一个JSP文件中是一个普遍的设计方法,但是许多应用需要将应用中的不同部分显示在一个页面中。例如一个门户应用需要如下功能:
1. 访问门户的搜索引擎
2. 访问门户的讨论区
3. 用户感兴趣的话题
4. 邮件等待指示器
将这些不同的功能交给不同的开发者开发,要完成这个门户应用是比较容易的。然后你就可以使用包含( include)来
将它们组合到一个页面中。有三种不同的包含方式,选择哪种取决于你希望整个输出在什么时候整合:
1. <%@ include file="xxxxx" %>
2. <jsp:include page="xxxxx" flush="true" />
3. bean:include
3.4.3 使用Tiles组合页面
Tiles是一个功能很强的模板库,它可以将很多tile组合成最终的视图。以下是设置向导:
1.创建layout/layout.jsp,它包含标准外观
<html>
<body>
<tiles:insert attribute="body"/>
</body>
</html>
2.创建你的主页/index.jsp
<h1>This is my homepage</h1>
3.创建文件/WEB-INF/tiles-defs.xml
<tiles-definitions>
<definition
name="layout"
path="/layout/layout.jsp">
<put name="body" value=""/>
</definition>
<definition name="homepage" extends="layout">
<put
name="body"
value="/index.jsp"/>
</definition>
<tiles-definitions>
4.在文件struts-config.xml中设置TilesPlugin
<plug-in
className="org.apache.struts.tiles.TilesPlugin">
<set-property
property="definitions-config"
value="/WEB-INF/tiles-defs.xml"/>
</plug-in>
5.在struts-config.xml文件中设置一个Action指向你的主页
<action
path="/index"
type="org.apache.struts.actions.ForwardAction"
parameter="homepage"/>
3.4.4 图片渲染组件
一些应用需要动态产生图片,有两种方法符合如下需求:
1. 产生一个执行Servlet请求的超链接 Servlet将使用图形库来产生图片
2. 将JAVA Applet嵌入在HTML页面中来产生图片
3.4.5 文本输出
一些应用需要动态的产生文本(如XML),因为整个页面将通过PrinterWriter输出,我们可以通过设置PrinterWriter的属性来做到:
response.setContentType("text/plain"); // or text/xml
PrintWriter writer = response.getWriter();
// use writer to render text
return(null);
3.4.6 Struts EL 标签库
Struts基本的标签都是依赖rtexprvalue(runtime scriptlet expression)来动态计算属性
的值,例如,要根据资源关键值打印来自属性文件中的信息
<bean:message key='<%= stringvar %>'/> 这样写是假定stringvar是JSP中的
脚本变量,如果使用Struts的EL标签库就会是如下形式:
<bean-el:message key="${stringvar}"/>
4 创建控制(Controller)组件
4.1 概述
我们已经知道如何去构建Model和View组件,现在我们将集中到Controller组件。Struts包含了一个映射请求URI到Action类的Servlet。因此你编写WEB应用时在Controller组件这方面要做的工作如下:
1.编写AtionForm作为Model和View的中介
2.编写Action(继承org.apache.struts.action.Action)来处理请求
3.为每一个逻辑请求在struts-config.xml.中编写一个ActionMapping
4.2 ActionServlet
对于熟悉MVC架构的人来讲,ActionServlet就代表着 C – Controller。Controller的任务是:
1.处理用户请求
2.根据用户请求来决定用户将要完成什么任务
3.将Model的数据传到视图(View)
4.选择合适的视图响应请求
Controller会将大部分工作放到Request Processor和Action 类中。
4.2.1 请求处理器(Request Processor)
RequestProcessor对每个请求做核心处理的地方,它要做的处理如下:
1. processPath - 确定请求的路径,以备后面的处理检索ActionMapping
2. processLocale – 为请求选择一个locale
3. processContent - 设置默认的内容(Content)类型.
4. processNoCache – 设置响应头:Pragma,Cache-Control,Expires
5. processPreprocess - RequestProcessor让子类重载实现,默认返回真(True),如果子类重载此方法并返回真,则该请求将继续处理流程,如果返回假,则意味着你处理了该请求,处理流程直接返回。
6. processMapping - 确定请求所对应路径的ActionMapping
7. processRoles - 保证请求的用户具备特定的角色
8. processActionForm - 实例化ActioForm并且把它放在适当的作用域中。
9. processPopulate - 用请求中的数据组装ActionForm
10. processValidate - 校验ActionForm中的数据
11. processForward - 如果映射是一个转向( Forward)指令,就转向到特定的路径。
12. processInclude - 如果映射是一个包含(Include)指令,就将映射指定的路径的输出结果包含进来。
13. processActionCreate - 实例化映射指定的Action.
14. processActionPerform – 执行Action的perform或excute方法。
15. processForwardConfig - 最后RequetProcessor使用Action类返回的ActionForward来选择下一个资源。大多数的AtionForward将会导航到显示页面输出响应。
4.3 ActionForm类
一个ActionForm代表着一个与用户交互的HTML表单。ActionForm中的属性来存储表单中的状态,并且有getter,setter方法来访问他们。ActionForm可以存储在session或request的作用域中(默认的是session).如果ActionForm放在session中记得要实现reset方法,以备每次使用ActionForm时都会初始化。Struts根据请求中的参数设置ActionForm的属性并且把经过校验后的ActionForm传到Action的execute方法。
当你在编写ActionForm时必须坚持如下原则:
1.ActionForm本身没有任何特定的方法被实现。仅仅用来表示它是整个框架中一个特定的角色。ActinForm中只有getter,setter方法,并没有任何商业逻辑。
2.AtionForm提供标准的校验机制。如果你重载了ActionForm的validate方法,并且在资源属性文件中提供了错误消息,那么Struts就会自动校验表单中的数据。当然你也可以忽略ActionForm中的校验,在Action类中来实现校验。
3.为输入表单中的每一个输入定义属性。输入域的名称和ActionForm中属性的名称必须符合JAVA规范。例如一个输入域的名称username将会导致ActionForm中的setUsername被调用。
4.也可以为Form中的按钮或其他控件定义属性。当提交表单时这将有利于你知道哪个控件被选中了。
5.把ActionForm当作HTTP和Action之间的防火墙。ActionForm的方法可以校验所有必须的属性已经存在了并且包含合理的值。如果校验失败请求将不会被Action类处理。
6.你可能会放一个Bean的实例在ActinForm中,这样你就会用到嵌套属性引用。例如,你可能有一个“customer”在ActionForm中,然后在页面中用“customer.name”来引用属性。
4.3.1 DynaActionForm
维护一个具体的ActionForm是要耗费时间的。特别是ActionForm越来越多并且都是校验一些简单的属性时你可能会感觉到一股挫折感。
这个瓶颈通过DynaActionForm会有所减轻。通过在Struts的配置文件中列出属性,类型和默认值来替代以前的定义一个新类,并且添加getter/setter方法。例如,在struts-config.xml中添加一个UserForm:
<form-bean
name="UserForm"
type="org.apache.struts.action.DynaActionForm">
<form-property
name="givenName"
type="java.lang.String"
initial="John"/>
<form-property
name="familyName"
type="java.lang.String"
initial="Smith"/>
</form-bean>
DynaActionForm支持的数据类型:
· java.lang.BigDecimal
· java.lang.BigInteger
· boolean and java.lang.Boolean
· byte and java.lang.Byte
· char and java.lang.Character
· java.lang.Class
· double and java.lang.Double
· float and java.lang.Float
· int and java.lang.Integer
· long and java.lang.Long
· short and java.lang.Short
· java.lang.String
· java.sql.Date
· java.sql.Time
· java.sql.Timestamp
你可以指定成这些类型的数组,也可以时MAP的具体实现类。
4.3.2 后端映射ActionForm
DynaActionForm根据配置文件中的属性在初始化的时候来生成ActionForm。但有时候输入表单是动态生成的。因此表单的AtionForm的属性不能提前知道,所以需要一种新的方法。
Struts允许你将ActionForm属性存储在MAP中而不是JAVA的原子对象。
public FooForm extends ActionForm {
private final Map values = new HashMap();
public void setValue(String key, Object value) {
values.put(key, value);
}
public Object getValue(String key) {
return values.get(key);
}
}
在JSP页面中你可以通过特殊的符号来引用:mapname(keyname)。圆括号在Bean的属性中表明:
1.ActionForm的所有属性使用Sring做索引
2.Struts将使用带String参数的getter/setter方法来获取,设置ActionForm的值。
看如下的例子:
<html:text property="value(foo)"/>
这样你将会调用FooForm的getValue方法来得到键值为”foo”的值。为了创建一个包含动态输入域的表单你会如下做:
<%
for (int i = 0; i < 10; i++) {
String name = "value(foo-" + i + ")";
%>
<html:text property="<%= name %>"/>
<br/>
<%
}
%>
除了后端映射属性,你还可以创建后端列表属性。
public FooForm extends ActionForm {
private final List values = new ArrayList();
public void setValue(int key, Object value) {
values.set(key, value);
}
public Object getValue(int key) {
return values.get(key);
}
}
在你的JSP页面中,你需要用特殊的符号来引用值:listname[index]。
4.4 Action类
Ation有两个根据Servlet环境不同而调用的方法.
The Action class defines two methods that could be executed depending on your servlet environment:
public ActionForward execute(ActionMapping mapping,
ActionForm form,
ServletRequest request,
ServletResponse response)
throws Exception;
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception;
因为Struts主要用来创建WEB应用,所以大多数项目只用HttpServletRequest。Action主要用来处理请求并且返回对象ActionForward表明控制流将转向哪里(JSP,Action, Tile定义,Velocity模板) 。在MVC/Model2的设计模式里,Action将完成一下逻辑。
1.校验用户Session的状态(用户是否登录)。
2.校验表单Bean的属性。
3.执行该请求所需的逻辑处理。
4.修改Server端对象。以备下一个显示页面要用。
5.返回一个ActionForward对象来表明哪一个页面将用来产生输出。
4.4.1 Action类的设计方针
1.编写多线程安全代码 Controller Servlet只创建一个Action的实例来服务所有的请求,因此你必须编写线程安全的Action 类。下面是写Action类是的两个基本的方针:
只使用局部变量 编写线程安全的Action的最重要一点是不要使用实例变量而用局部变量。因为局部变量在每个线程的堆栈上创建,所以不需要考虑多线程的问题。
保存资源 当你分配有限的资源时可能会导致资源匮乏的问题。(比如分配JDBC 连接,这时就要尽可能采用链接池的方式)。
2.不要抛出异常,尽量Catch它
如果你的应用代码会抛出异常请Catch住它,记录他们到应用的Log中并且返回一个ActionFoward。
避免写很长,很复杂的Action类是明智的。如果你写很多的逻辑在Action类中,这将会使代码很难理解,维护,重用。
4.5 异常处理器
当Action的execute方法抛出异常时,你可以定义一个异常处理器来执行相应的处理。首先你需要一个继承org.apache.struts.action.ExceptionHandler的类并且重载execute方法。Execute方法将处理异常并且返回一个ActionForward来告诉struts下一步将去哪。然后在struts-config.xml中配置异常处理器:
<global-exceptions>
<exception
key="some.key"
type="java.io.IOException"
handler="com.yourcorp.ExceptionHandler"/>
</global-exceptions>
这个配置说明了当发生一个IOException时,com.yourcorp.ExceptionHandler.execute将会被调用。
...........
4.6 插件类
Plugin接口继承了Ation。它定义了两个接口init,destroy分别在应用启动和停止时调用。Plugin的一般用法就是在应用启动时来装载应用的特定数据。
在运行时,任何被Plugin装载的数据都可以被Action,或商业逻辑层的类所使用,不过Plugin并没有提供访问这些数据的方法。Plugin装载的数据经常以预先定义好的名字放在应用的上下文中供其他组件使用。
4.7 ActionMapping的实现
Controller Servlet需要知道如何将某个URI映射到一个Action。ActionMapping中可以找到所需的信息:
Type(类型) - Action类的全名。
Name - Action使用的Form Bean的名字。
Path - 匹配此映射的请求URI.
Unknown - 如果设置为真,应用的所有请求将有此AtionMapping来处理。
Validate - 设置为真,Action类的validate方法将会被调用。
Forward - 当映射被调用时,控制流将会传递到那个URI。
4.8 编写Action Mappings
Controller Servlet怎么知道ActionMapping要做什么呢?编写一个小的JAVA类并且持有一个ActionMapping的实例,通过setter方法来设置属性是可以做到的。不过Struts提供了通过Digester组件解释XML的方式来实现。
开发者负责创建一个名字叫struts-config.xml的文件并且把它放在WEB-INF目录下。
最外层的XML元素必须是<struts-config>,载它的里面有三个比较重要的元素来描述你的Action:
· <form-beans>
· <global-forwards>
· <action-mappings>
<form-beans>
这部分包含了表单Bean的定义。它将用于创建ActionForm。你将使用<form-bean>来定义每个ActionForm,它有如下属性:
· name: Bean的唯一标识符.
· type: ActionForm的类名。
<global-forwards>
这部分包含了全局性的转向定义。转向定义实际上就是Action的execute方法返回的ActionForward。这些定义将映射逻辑名称到某种资源(如JSP),当我们改变资源名称时就不要改所有引用的地方。你使用<forward>来定义转向,它有如下重要的属性:
· name: 转向的逻辑名称。
· path: 资源的上下文路径. 例如: /index.jsp or /index.do
· redirect: True 或 false (默认). ActionServlet 是不是用重定向(redirect)来取代 转向( forward)?
<action-mappings>
这部分包含所有的Action映射。你应该使用<action>来定义每一个映射,它包含如下的属性:
· path: Action所对应的上下文路径.
· type: Action类.
· name: 与Action配套的<form-bean> 元素的名称.
4.8.1 ActionMapping例子
<struts-config>
<form-beans>
<form-bean
name="logonForm"
type="org.apache.struts.webapp.example.LogonForm" />
</form-beans>
<global-forwards
type="org.apache.struts.action.ActionForward">
<forward
name="logon"
path="/logon.jsp"
redirect="false" />
</global-forwards>
<action-mappings>
<action
path="/logon"
type="org.apache.struts.webapp.example.LogonAction"
name="logonForm"
scope="request"
input="/logon.jsp"
unknown="false"
validate="true" />
</action-mappings>
</struts-config>
4.9 在页面中使用ActionMapping
在页面中配置AtionMapping是你无法通过其他方式设置时的一种方式,它的使用方法为:
<action path="/view" forward="/view.jsp"/>
4.10 在ActionMapping中使用通配符
随着Struts应用的增大,ActionMapping也就越来越多。通配符则可以将几个相似的AtionMapping组合成一个。
解释通配符的最好方法是来看一个例子:
<action
path="/edit*"
type="org.apache.struts.webapp.example.Edit{1}Action"
name="{1}Form"
scope="request"
validate="false">
<forward
name="failure"
path="/mainMenu.jsp"/>
<forward
name="success"
path="/{1}.jsp"/>
</action>
在path属性中的*号可以匹配请求URI/editSubscription,,
editRegistration和其他任何以/edit开头的URI,但/editSubscription/add将不会被匹配。被匹配的部分将会替换AtionMapping的属性和转向(Forward)中的{1}。
通配符可以包含如下特殊字符:
* 匹配零个或多个字符但不包括/
** 匹配零个或多个字符串并且包括/
/character 反斜杠字符串是转义字符。
4.11 公共的Log 接口
Struts本身并没有LOG配置,把它交给Commons-Logging是最好的。
5. 配置应用
5.1 概述
在你创建Struts应用之前,应该铺设一个好的基础。在你部署Struts应用之前,这里有几个安装任务你必须完成,它包括在Struts配置文件和WEB应用部署描述文件中定义相关的内容。
5.2 Struts配置文件
在创建控制组件一章中我们讲到了Struts的<form-bean>,<action-mapping>,这些元素在Struts应用的开发中扮演着很重要的角色。其他的配置元素是静态的:只需设置一次。
不变的配置元素是:
· <controller>
· <message-resources>
· <plug-in>
5.2.1 控制器的配置
<controller>允许你配置ActionServlet。
· bufferSize – 文件上传时的输入缓冲区的字节数. [4096] (可选)
· className – 配置Bean的类名. [org.apache.struts.config.ControllerConfig] (可选)
· contentType – 响应输出内容的类型.可能被the Action, JSP, 或其他资源 覆盖. [text/html] (可选)
· forwardPattern - <forward>元素的path属性如果以以斜杠开始时映射到上下文相关的路径时的替换模式。它可以由如下元素组成:
o $M – 替换成该模块的前缀.
o $P – 替换成<Forward>元素的path属性.
o $$ - 生成$符号.
$x – 保留字(x即不是上述字符的其他字符). [$M$P] (可选)
· inputForward - [false] (可选)
· locale - [true] (可选)
· maxFileSize – 上传文件的大小.可以以K,M,G为单位(分别代表千,兆,1024兆). [ 250M] (可选)
· multipartClass - multipart 请求处理类的名称。 [org.apache.struts.upload.CommonsMultipartRequestHandler] (可选)
· nocache – 设置HTTP头来设置是否允许缓存. [false] (可选)
pagePattern -
· processorClass - RequestProcessor 子类的类名. [org.apache.struts.chain.ComposableRequestProcessor] (可选)
· tempDir – 处理文件上传时的临时目录.
5.2.2 消息资源配置
Struts本身支持应用的国际化,你可以定义一个或多个<message-resources>在你的配置文件中。
· className – 配置Bean的类名. [org.apache.struts.config.MessageResourcesConfig] (可选)
· factory – MessageResourcesFactory的类名. [org.apache.struts.util.PropertyMessageResourcesFactory] (可选)
· key - ServletContext 关键值来存储此绑定. [org.apache.struts.action.MESSAGE] (可选)
· null – 设置为false,将会使找不到相关资源的键值显示 '???keyname???' 而不是null. [true] (可选)
· parameter – 资源绑定的名称. (必须)
5.2.3 插件配置
在Struts的配置文件中使用<plug-in>元素来配置插件。<plug-in>只有一个className的属性。它是完成了org.apache.struts.action.PlugIn interface接口的类的类名。插件需要配置它自身,所以可用<set-property>来做。
<plug-in className="org.apache.struts.tiles.TilesPlugin">
<set-property
property="definitions-config"
value="/WEB-INF/tiles-defs.xml"/>
</plug-in>
5.3 为应用配置模块
略
5.3.1 模块配置文件
略
5.3.2 通知控制器
略
5.3.3 切换模块
略
5.4 WEB应用部署描述符
安装应用的最后一步是在web.xml包含Struts组件。
5.4.1 配置ActionServlet实例
配置ActionServlet并加上适当的初始化参数
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>
/WEB-INF/struts-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
5.4.2 配置ActionServlet映射
有两种方法可以定义URL请求将会被Controller Servlet处理:前缀匹配和后缀匹配。
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/do/*</url-pattern>
</servlet-mapping>
按如上定义,下面就是前缀匹配http://www.mycompany.com/myapplication/do/logon
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
按如上定义,下面就是后缀匹配
http://www.mycompany.com/myapplication/logon.do
5.4.3 配置Struts标签库
<taglib>
<taglib-uri>
http://struts.apache.org/tags-bean
</taglib-uri>
<taglib-location>
/WEB-INF/struts-bean.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-html
</taglib-uri>
<taglib-location>
/WEB-INF/struts-html.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-logic
</taglib-uri>
<taglib-location>
/WEB-INF/struts-logic.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-tiles
</taglib-uri>
<taglib-location>
/WEB-INF/struts-tiles.tld
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://struts.apache.org/tags-nested
</taglib-uri>
<taglib-location>
/WEB-INF/struts-nested.tld
</taglib-location>
</taglib>