当前页面位置: » 丰搜网 » 文档中心 » 详细内容
推荐-扩展struts
扩展struts作者:sunil patil翻译:loryliu版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
作者:
sunil patil;loryliu
原文地址:
http://www.on
java.com/pub/a/on
java/2004/11/10/extending
struts.
html中文地址:
http://www.matrix.org.cn/resource/article/43/43857_
struts.
html关键词: extending
struts简介我见过许多项目开发者实现自己专有的mvc
框架。这些开发者并不是因为想实现不同于
struts的某些功能,而是还没有意识到怎么去扩展
struts。通过开发自己的mvc
框架,你可以掌控全局,但同时这也意味着你必须付出很大的代价;在项目计划很紧的情况下也许根本就不可能实现。
struts不但功能强大也易于扩展。你可以通过三种方式来扩展
struts:
1.plugin:在应用启动或关闭时须执行某业务逻辑,创建你自己的plugin类
2.requestprocessor:在请求处理阶段一个特定点欲执行某业务逻辑,创建你自己的requestprocessor。例如:你想继承requestprocessor来检查用户登录及在执行每个请求时他是否有权限执行某个动作。
3.action
servlet:在应用启动或关闭或在请求处理阶段欲执行某业务逻辑,继承action
servlet类。但是必须且只能在pligin和requestprocessor都不能满足你的需求时候用。
本文会列举一个简单的
struts应用来示范如何使用以上三种方式扩展
struts。在本文末尾资源区有每种方式的可下载样例源代码。
struts validation
框架和 tiles
框架是最成功两个的
struts扩展例子。
我是假设读者已经熟悉
struts框架并知道怎样使用它创建简单的应用。如想了解更多有关
struts的资料请参见资源区。
plugin根据
struts文档,“plugin是一个须在应用启动和关闭时需被通知的模块定制资源或服务配置包”。这就是说,你可以创建一个类,它实现plugin的接口以便在应用启动和关闭时做你想要的事。
假如创建了一个
web应用,其中使用
hibernate做为持久化机制;当应用一启动,就需初始化hinernate,这样在
web应用接收到第一个请求时,
hibernate已被配置完毕并待命。同时在应用关闭时要关闭
hibernate。跟着以下两步可以实现
hibernate plugin的需求。
1.创建一个实现plugin接口的类,如下:
public class hibernateplugin implements plugin{
private string configfile;
// this method will be called at application shutdown time
public void destroy() {
system.out.println("entering hibernateplugin.destroy()");
//put hibernate cleanup code here
system.out.println("exiting hibernateplugin.destroy()");
}
//this method will be called at application startup time
public void init(actionservlet actionservlet, moduleconfig config)
throws servletexception {
system.out.println("entering hibernateplugin.init()");
system.out.println("value of init parameter " +
getconfigfile());
system.out.println("exiting hibernateplugin.init()");
}
public string getconfigfile() {
return name;
}
public void setconfigfile(string string) {
configfile = string;
}
}实现plugin接口的类必须是实现以下两个方法:
init() 和destroy().。在应用启动时init()被调用,关闭destroy()被调用。
struts允许你传入初始参数给你的plugin类;为了传入参数你必须在plugin类里为每个参数创建一个类似
javabean形式的setter方法。在
hibernateplugin类里,欲传入configfile的名字而不是在应用里将它硬编码进去
2.在
struts-condig.
xml里面加入以下几行告知
struts这个新的plugin
<struts-config>
...
<!-- message resources -->
<message-resources parameter=
"sample1.resources.applicationresources"/>
<!-- declare your plugins -->
<plug-in classname="com.sample.util.hibernateplugin">
<set-property property="configfile"
value="/hibernate.cfg.xml"/>
</plug-in>
</struts-config>
classname属性是实现plugin接口类的全名。为每一个初始化传入plugin类的初始化参数增加一个<set-property>元素。在这个例子里,传入config文档的名称,所以增加了一个config文档路径的<set-property>元素。
tiles和validator
框架都是利用plugin给初始化读入配置文件。另外两个你还可以在plugin类里做的事情是:
假如应用依赖于某配置文件,那么可以在plugin类里检查其可用性,假如配置文件不可用则抛出
servletexception。这将导致action
servlet不可用。
plugin接口的init()方法是你改变moduleconfig方法的最后机会,moduleconfig方法是描述基于
struts模型静态配置信息的集合。一旦plugin被处理完毕,
struts就会将moduleconfig冻结起来。
请求是如何被处理的action
servlet是
struts框架里唯一一个
servlet,它负责处理所有请求。它无论何时收到一个请求,都会首先试着为现有请求找到一个子应用。一旦子应用被找到,它会为其生成一个requestprocessor对象,并调用传入http
servletrequest和http
servletresponse为参数的process()方法。
大部分请处理都是在requestprocessor.process()发生的。process()方法是以模板方法(template method)的设计
模式来实现的,其中有完成request处理的每个步骤的方法;所有这些方法都从process()方法顺序调用。例如,寻找当前请求的actionform类和检查当前用户是否有权限执行action mapping都有几个单独的方法。这给我们提供了极大的弹性空间。
struts的requestprocessor对每个请求处理步骤都提供了默认的实现方法。这意味着,你可以重写你感兴趣的方法,而其余剩下的保留默认实现。例如,
struts默认调用request.isuserinrole()检查用户是否有权限执行当前的actionmapping,但如果你需要从
数据库中查找,那么你要做的就是重写processroles()方法,并根据用户角色返回true 或 false。
首先我们看一下process()方法的默认实现方式,然后我将解释requestprocessor类里的每个默认的方法,以便你决定要修改请求处理的哪一部分。
public void process(httpservletrequest request,
httpservletresponse response)
throws ioexception, servletexception {
// wrap multipart requests with a special wrapper
request = processmultipart(request);
// identify the path component we will
// use to select a mapping
string path = processpath(request, response);
if (path == null) {
return;
}
if (log.isdebugenabled()) {
log.debug("processing a '" + request.getmethod() +
"' for path '" + path + "'");
}
// select a locale for the current user if requested
processlocale(request, response);
// set the content type and no-caching headers
// if requested
processcontent(request, response);
processnocache(request, response);
// general purpose preprocessing hook
if (!processpreprocess(request, response)) {
return;
}
// identify the mapping for this request
actionmapping mapping =
processmapping(request, response, path);
if (mapping == null) {
return;
}
// check for any role required to perform this action
if (!processroles(request, response, mapping)) {
return;
}
// process any actionform bean related to this request
actionform form =
processactionform(request, response, mapping);
processpopulate(request, response, form, mapping);
if (!processvalidate(request, response, form, mapping)) {
return;
}
// process a forward or include specified by this mapping
if (!processforward(request, response, mapping)) {
return;
}
if (!processinclude(request, response, mapping)) {
return;
}
// create or acquire the action instance to
// process this request
action action =
processactioncreate(request, response, mapping);
if (action == null) {
return;
}
// call the action instance itself
actionforward forward =
processactionperform(request, response,
action, form, mapping);
// process the returned actionforward instance
processforwardconfig(request, response, forward);
}
1、processmultipart(): 在这个方法中,
struts读取request以找出contenttype是否为multipart/form-data。假如是,则解析并将其打包成一个实现http
servletrequest的包。当你成生一个放置数据的
html form时,request的contenttype默认是application/x-www-form-urlencoded。但是如果你的form的input类型是file-type允许用户上载文件,那么你必须把form的contenttype改为multipart/form-data。如这样做,你永远不能通过http
servletrequest的getparameter()来读取用户提交的form值;你必须以inputstream的形式读取request,然后解析它得到值。
2、processpath(): 在这个方法中,
struts将读取request的uri以判断用来得到actionmapping元素的路径。
3、processlocale(): 在这个方法中,
struts将得到当前request的locale;locale假如被配置,将作为org.apache.
struts.action.locale属性的值被存入httpsession。这个方法的附作用是httpsession会被创建。假如你不想此事发生,可将在
struts-config.
xml 文件里controllerconfig的local属性设置为false,如下:
<controller>
<set-property property="locale" value="false"/>
</controller>
4、processcontent():通过调用response.setcontenttype()设置response的contenttype。这个方法首先会试着的得到配置在
struts-config.
xml里的contenttype。默认为text/
html,重写方法如下:
<controller>
<set-property property="contenttype" value="text/plain"/>
</controller>
5、processnocache():struts将为每个response的设置以下三个header,假如已在
struts 的config.
xml将配置为no-cache。
response.setheader("pragma", "no-cache");
response.setheader("cache-control", "no-cache");
response.setdateheader("expires", 1);
假如你想设置为no-cache header,在
struts-config.
xml中加如以下几行
<controller>
<set-property property="nocache" value="true"/>
</controller>
6、processpreprocess():这是一个一般意义的预处理hook,其可被子类重写。在requestprocessor里的实现什么都没有做,总是返回true。如此方法返回false会中断请求处理。
7、processmapping():这个方法会利用path信息找到actionmapping对象。actionmapping对象在
struts-config.
xml file文件里表示为<action>
<action path="/newcontact" type="com.sample.newcontactaction"
name="newcontactform" scope="request">
<forward name="sucess" path="/sucesspage.do"/>
<forward name="failure" path="/failurepage.do"/>
</action>
actionmapping元素包含了如action类的名称及在请求中用到的actionform的信息,另外还有配置在当前actionmapping的里的actionforwards信息。
8、processroles(): struts的
web 应用
安全提供了一个认证机制。这就是说,一旦用户登录到容器,
struts的processroles()方法通过调用request.isuserinrole()可以检查他是否有权限执行给定的actionmapping。
<action path="/adduser" roles="administrator"/>
假如你有一个adduseraction,限制只有administrator权限的用户才能新添加用户。你所要做的就是在adduseraction 的action元素里添加一个值为administrator的role属性。
9、processactionform():每个actionmapping都有一个与它关联的actionform类。
struts在处理actionmapping时,他会从<action>里name属性找到相关的actionform类的值。
<form-bean name="newcontactform"
type="org.apache.struts.action.dynaactionform">
<form-property name="firstname"
type="java.lang.string"/>
<form-property name="lastname"
type="java.lang.string"/>
</form-bean>
在这个例子里,首先会检查org.apache.
struts.action.dynaactionform类的对象是否在request 范围内。如是,则使用它,否则创建一个新的对象并在request范围内设置它。
10、processpopulate()::在这个方法里,
struts将匹配的request parameters值填入actionform类的实例变量中。
11、processvalidate():struts将调用actionform的validate()方法。假如validate()返回actionerrors,
struts将用户转到由<action>里的input属性标示的页面。
12、processforward() and processinclude():在这两个方法里,
struts检查<action>元素的forward和include属性的值,假如有配置,则把forward和include 请求放在配置的页面内。
<action forward="/login.jsp" path="/logininput"/>
<action include="/login.jsp" path="/logininput"/>
你可以从他们的名字看出其不同之处。processforward()调用requestdispatcher.forward(),,processinclude()调用requestdispatcher.include()。假如你同时配置了orward 和include 属性,
struts总会调用forward,因为forward,是首先被处理的。
13、processactioncreate():这个方法从<action>的type属性得到action类名,并创建返回它的实例。在这里例子中
struts将创建一个com.sample.newcontactaction类的实例。
14、processactionperform():这个方法调用action 类的execute()方法,其中有你写入的业务逻辑。
15、processforwardconfig():action类的execute()将会返回一个actionforward类型的对象,指出哪一页面将展示给用户。因此
struts将为这个页面创建requestdispatchet,然后再调用requestdispatcher.forward()方法。
以上列出的方法解释了requestprocessor在请求处理的每步默认实现及各个步骤执行的顺序。正如你所见,requestprocessor很有弹性,它允许你通过设置<controller>里的属性来配置它。例如,假如你的应用将生成
xml内容而不是
html,你可以通过设置controller的某个属性来通知
struts。
创建你自己的requestprocessor从以上内容我们已经明白了requestprocessor的默认实现是怎样工作的,现在我将通过创建你自己的requestprocessor.展示一个怎样自定义requestprocessor的例子。为了演示创建一个自定义requestprocessor,我将修改例子实现以下连个业务需求:
我们要创建一个contactimageaction类,它将生成images而不是一般的
html页面
在处理这个请求之前,将通过检查session里的username属性来确认用户是否登录。假如此属性没有被找到,则将用户转到登录页面。
分两步来实现以上连个业务需求。
创建你自己的customrequestprocessor类,它将继承requestprocessor类,如下:
public class customrequestprocessor
extends requestprocessor {
protected boolean processpreprocess (
httpservletrequest request,
httpservletresponse response) {
httpsession session = request.getsession(false);
//if user is trying to access login page
// then don't check
if( request.getservletpath().equals("/logininput.do")
request.getservletpath().equals("/login.do") )
return true;
//check if username attribute is there is session.
//if so, it means user has allready logged in
if( session != null &&
session.getattribute("username") != null)
return true;
else{
try{
//if no redirect user to login page
request.getrequestdispatcher
("/login.jsp").forward(request,response);
}catch(exception ex){
}
}
return false;
}
protected void processcontent(httpservletrequest request,
httpservletresponse response) {
//check if user is requesting contactimageaction
// if yes then set image/gif as content type
if( request.getservletpath().equals("/contactimage.do")){
response.setcontenttype("image/gif");
return;
}
super.processcontent(request, response);
}
}
在customrequestprocessor 类的processpreprocess方法里,检查session的username属性,假如没有找到,将用户转到登录页面。
对于产生images作为contactimageaction类的输出,必须要重写processcontent方法。首先检查其request是否请求/contactimage路径,如是则设置contenttype为image/gif;否则为text/
html。
加入以下几行代码到sruts-config.
xml文件里的<action-mapping>后面,告知
struts ,customrequestprocessor应该被用作requestprocessor类
<controller>
<set-property property="processorclass"
value="com.sample.util.customrequestprocessor"/>
</controller>
请注意,假如你只是很少生成contenttype不是text/
html输出的action类,重写processcontent()就没有问题。如不是这种情况,你必须创建一个
struts子系统来处理生成image action的请求并设置contenttype为image/gif
title
框架使用自己的requestprocessor来装饰
struts生成的输出。
actionservlet假如你仔细研究
struts web应用的
web.
xml文件,它看上去像这样:
<web-app >
<servlet>
<servlet-name>action=</servlet-name>
<servlet-class>org.apache.struts.action.actionservlet</servlet-class>
<!-- all your init-params go here-->
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app >
这就是说,action
servlet负责处理所有发向
struts的请求。你可以创建action
servlet的一个子类,假如你想在应用启动和关闭时或每次请求时做某些事情。但是你必须在继承action
servlet类前创建plugin 或 requestprocessor。在
servlet 1.1前,title
框架是基于继承action
servlet类来装饰一个生成的response。但从1.1开始,就使用tilesrequestprocessor类。
结论开发你自己的mvc模型是一个很大的决心——你必须考虑开发和维护代码的时间和资源。
struts是一个功能强大且稳定的
框架,你可以修改它以使其满足你大部分的业务需求。
另一方面,也不要轻易地决定扩展
struts。假如你在requestprocessor里放入一些低效率的代码,这些代码将在每次请求时执行并大大地降低整个应用的效率。当然总有创建你自己的mvc
框架比扩展
struts更好的情况。
资源下载本文源码:[下载文件]
struts主页
"jakarta
struts框架介绍"
"学习jakarta
struts 1.1"
sunil pail已从事
j2ee四年,现今与ibm实验室合作。