`

扩展struts的三种方式

阅读更多

扩展struts的三种方法

Struts不仅仅是一个强大的框架,同时它也是可扩展的。你可以以三种方式来扩展Struts。
1,PlugIn:如果你想在applicationstartup或shutdown的时候做一些业务逻辑的话,那就创建你自己的PlugIn类。
2,RequestProcessor:如果你想在请求被处理的过程中某个时刻做一些业务逻辑的话,那么创建你自己的RequestProcessor类。比如说,在每次请求执行之前,你可以扩展RequestProcessor来检查用户是否登陆了以及他是否有权限去执行某个特定的action。
3,ActionServlet:如果你想在applicationstartup和shutdown的时候以及请求被处理的时候做某些业务逻辑,你也可以扩张ActionServlet类。不过你应当在PlugIn和RequestProcessor都不能解决你的需求的时候来使用ActionServlet。

在这里将使用一个Struts应用的示例来示范如何使用这三种方式来扩展Struts。示例程序的代码可以从http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip下载。两个扩展Struts成功的范例是Struts自身的Validation和Tiles框架。

我们假设你已经比较熟悉Struts框架并且知道如何使用它创建一个简单的应用。如果你想知道更多关于Struts的内容,请参考官方主页。

PlugIn
PlugIn是一个接口,你可以创建一个实现该接口的类,当applicationstartup或shutdown的时候做些事情。
比方说,我创建了一个使用Hibernate作为持久层的web应用,我想当应用启动的时候就初始化Hibernate,这样子当我的web应用受到第一个请求的时候,Hibernate就已经是配置好的并且可用的。同时我们想当application关闭的时候关闭Hibernate。我们可以用一个HibernatePlugIn来实现这个需求,通过如下的两步:
1,创建一个类实现了PlugIn接口:


publicclassHibernatePlugInimplementsPlugIn{
privateStringconfigFile;
//Thismethodwillbecalledatapplicationshutdowntime
publicvoiddestroy(){
System.out.println("EnteringHibernatePlugIn.destroy()");
//Puthibernatecleanupcodehere
System.out.println("ExitingHibernatePlugIn.destroy()");
}
//Thismethodwillbecalledatapplicationstartuptime
publicvoidinit(ActionServletactionServlet,ModuleConfigconfig)
throwsServletException{
System.out.println("EnteringHibernatePlugIn.init()");
System.out.println("valueofinitparameter"+
getConfigFile());
System.out.println("ExitingHibernatePlugIn.init()");
}
publicStringgetConfigFile(){
returnname;
}
publicvoidsetConfigFile(Stringstring){
configFile=string;
}
}


实现PlugIn接口的类必须完成两个方法:init()和destroy()。当applicationstartup的时候init()方法被调用,当shutdown的时候destroy()方法被调用。Struts还允许给你的PlugIn类传递初始化参数。为了传递参数,你必须在PlugIn类中为每一个参数创建JavaBean式的setter方法。在我们的HibernatePlugIn类中,我会把configFile的name作为参数传进去,而不是硬编码到程序中。
2,在struts-config.xml中添加如下的代码来通告Struts有新的PlugIn:

<struts-config>
...
<!--MessageResources-->
<message-resourcesparameter=
"sample1.resources.ApplicationResources"/>

<!--Declareyourplugins-->
<plug-inclassName="com.sample.util.HibernatePlugIn">
<set-propertyproperty="configFile"
value="/hibernate.cfg.xml"/>
</plug-in>
</struts-config>


属性className是实现了PlugIn接口的类的全限定名。对于每一个初始化参数,可以使用<set-property>元素传递参数。在我们的例子中,我要把config文件的名字传进去,所以使用了一个带有配置文件路径的<set-property>。

Struts的Tiles和Validator框架都使用PlugIn来读取配置文件进行初始化。另外两件PlugIn可以帮你做到的事情是:
l如果你的application依赖于某些配置文件,那么你可以在PlugIn类中检查它们是否可用,如果不可用的话抛出一个ServletException,这样就可以使ActionServlet变为不可用。
lPlugIn接口的init()方法是你可以改变ModuleConfig的最后机会,ModuleConfig是一组静态配置信息的集合,用来描述基于Struts模块。Struts将会在所有PlugIn处理完后释放ModuleConfig。

Request是如何被处理的
ActionServlet是Struts框架中唯一的Servlet,它负责处理所有request。无论何时接收到一个request,它都会先尝试为当前的request寻找一个sub-application。一旦一个sub-application被找到,ActionServlet就会为那个sub-application创建一个RequestProcessor对象,调用这个对象的process()方法并把HttpServletRequest和HttpServletResponse对象传入。

RequestProcessor.process()就是大部分request被处理的地方。process()方法使用了TemplateMethod模式实现,其中有很多独立的方法来执行请求处理的每一步骤,这些方法将会在process方法中依次被调用。比如,将会有一个独立的方法用来寻找当前request对应的ActionForm类,一个方法来检查当前用户是否有执行actionmapping所必须的权限。这些给与我们极大的灵活性。在发布的Struts包中有一个RequestProcessor类提供了请求处理每一步骤的默认实现。这就意味着你可以仅仅重写你感兴趣的方法,其它的使用默认的实现。举例来说,默认地Struts调用request.isUserInRole()来检查用户是否有权限执行当前的ActionMapping;这时如果你想通过查询数据库来实现,你所要做的就是重写processRoles()方法,通过查询出的用户是否拥有必须的权限来返回true或false。

首先我们将会看到缺省情况下,process()方法是如何实现的,然后我将会详细解释默认的RequestProcessor类中的每一个方法,这样你就可以决定哪一部分是你想要改变的。

publicvoidprocess(HttpServletRequestrequest,HttpServletResponseresponse)
throwsIOException,ServletException{
//Wrapmultipartrequestswithaspecialwrapper
request=processMultipart(request);
//Identifythepathcomponentwewill
//usetoselectamapping
Stringpath=processPath(request,response);
if(path==null){
return;
}
if(log.isDebugEnabled()){
log.debug("Processinga'"+request.getMethod()+
"'forpath'"+path+"'");
}
//SelectaLocaleforthecurrentuserifrequested
processLocale(request,response);
//Setthecontenttypeandno-cachingheaders
//ifrequested
processContent(request,response);
processNoCache(request,response);
//Generalpurposepreprocessinghook
if(!processPreprocess(request,response)){
return;
}
//Identifythemappingforthisrequest
ActionMappingmapping=
processMapping(request,response,path);
if(mapping==null){
return;
}
//Checkforanyrolerequiredtoperformthisaction
if(!processRoles(request,response,mapping)){
return;
}
//ProcessanyActionFormbeanrelatedtothisrequest
ActionFormform=
processActionForm(request,response,mapping);
processPopulate(request,response,form,mapping);
if(!processValidate(request,response,form,mapping)){
return;
}
//Processaforwardorincludespecifiedbythismapping
if(!processForward(request,response,mapping)){
return;
}
if(!processInclude(request,response,mapping)){
return;
}
//CreateoracquiretheActioninstanceto
//processthisrequest
Actionaction=
processActionCreate(request,response,mapping);
if(action==null){
return;
}
//CalltheActioninstanceitself
ActionForwardforward=
processActionPerform(request,response,
action,form,mapping);
//ProcessthereturnedActionForwardinstance
processForwardConfig(request,response,forward);
}


1,processMutipart():在这个方法中,Struts将会读取request来检查request的contentType是否是multipart/form-data。如果是的话,将会解析request并且将之包装到HttpServletRequest中。当你创建了一个HTMLFORM用来提交数据,那么request的contentType默认就是application/x-www-form-urlencoded。但是如果你的form使用了file类型的input控件允许用户上传文件的话,你就必须将contentType改为multipart/form-data。如果是这样的情况,你就不能再通过getParameter()来获取用户提交的数据;你必须将request作为一个InputStream来读取,并且自己解析它来获得参数值。
2,processPath():在这个方法中,Struts将会读取request的URI,来确定路径元素,这个元素是用来获取ActionMappint元素。
3,processLocale():在这个方法中,Struts将会为当前request取得Locale,如果配置过的话,还可以将这个对象作为HttpSession中org.apache.struts.action.LOCALE属性的值而保存。作为这个方法的副作用,HttpSession将会被创建,如果你不想创建的话,你可以在ControllerConfig中将locale属性设为false,在struts-config.xml中象如下这样:

<controller>
<set-propertyproperty="locale"value="false"/>
</controller>


4,processContent():通过调用response.setContentType()来为response设置contentType。这个方法首先会尝试从struts-config.xml配置中得到contentType。缺省情况下使用text/html。如果想覆盖它,可以象如下这样:

<controller>
<set-propertyproperty="contentType"value="text/plain"/>
</controller>


5,processNoCache():如果配置是no-cache,Struts将会为每个response设置下面三个headers:

requestedinstrutsconfig.xml
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires",1);


如果你想设置no-cacheheader,在struts-config.xml中加入下面信息:

<controller>
<set-propertyproperty="noCache"value="true"/>
</controller>


6,processPreprocess():这个方法为预处理提供一个hook,可以在子类中覆盖它。它的缺省实现没有作任何事情,总是返回true。返回false的话将会终止当前请求的处理。
7,processMapping():这个方法将会用路径信息得到一个ActionMapping对象。也就是struts-config.xml文件中的<action>元素:

<actionpath="/newcontact"type="com.sample.NewContactAction"
name="newContactForm"scope="request">
<forwardname="sucess"path="/sucessPage.do"/>
<forwardname="failure"path="/failurePage.do"/>
</action>


ActionMapping元素包含了Action类的名称和处理请求使用的ActionForm等等信息。它还包含当前ActionMapping配置的ActionForwards信息。
8,processRoles():Strutsweb应用提供了一个授权方案。也就是说,一旦一个用户登入了容器,struts的processRoles()方法将会通过调用request.isUserInRole(),来检查他是否有必须的角色来运行一个给定的ActionMapping。

<actionpath="/addUser"roles="administrator"/>


假设你有一个AddUserAction并且你只想让administrator能够增加新的user。你所要做的就是给你的AddUserAction元素增加一个role属性,这个属性的值为administrator。这样,在运行AddUserAction之前,这个方法会确保用户拥有administraotr的角色。
9,processActionForm():每一个ActionMapping都一个相应的ActionForm类。当Struts处理一个ActionMapping的时候,它将会从<action>元素的name属性中找出对应的ActionForm类的名称。

<form-beanname="newContactForm"
type="org.apache.struts.action.DynaActionForm">
<form-propertyname="firstName"
type="java.lang.String"/>
<form-propertyname="lastName"
type="java.lang.String"/>
</form-bean>


在我们的例子中,它会先在requestscope中检查是否有一个org.apache.struts.action.DynaActionForm类的对象存在。如果有它将会使用这个对象,如果没有它将会创建一个新的对象并把它设置在requestscope。
10,processPopulate():在这个方法中,Struts将会用相匹配的request参数装配ActionForm的实例变量。
11,processValidate():Struts将会调用你的ActionForm类的validate方法。如果你从validate()返回ActionErrors,它将会将user重定向到<action>元素的input属性指定的页面。
12,processForward()和processInclude():在这些方法中,Struts将会检查<action>元素的forward或include属性,如果找到了,将会把forward或include请求放置到配置的页面中。

<actionforward="/Login.jsp"path="/loginInput"/>
<actioninclude="/Login.jsp"path="/loginInput"/>


你可以从这些方法的名字上猜测它们的不同:processForward()最终调用RequestDispatcher.forward(),而processInclude()调用RequestDispatcher.include()。如果你同时配置了forward和include属性,它将会总是调用forward,因为forward先被处理。
13,processActionCreate():这个方法从<action>元素的type属性中获取获得Action类的名字并且创建返回它的实例。在我们的例子中,它将会创建一个com.sample.NewContactAction类的实例。
14,processActionPerform():这个方法调用你的Action类的excute()方法,你的业务逻辑也就是在excute方法中。
15,processForwardConfig():你的Action类的excute()方法将会返回一个ActionForward对象,这个对象将指出哪个页面是显示给用户的页面。因此,Struts将会为那个页面创建一个RequestDispatcher,并且调用RequestDispatcher.forward()。
上面的列表说明了默认的RequestProcessor实现在处理请求时每一步作的工作,以及执行的顺序。正如你所看到的,RequestProcessor是非常灵活的,允许你通过设置<controller>元素的属性来配置它。举例来说,如果你的应用准备生成XML内容来代替HTML,你就可以通过设置controller元素的属性来通知Struts这些情况。

创建你自己的RequestProcessor
通过上面,我们了解到了RequestProcessor的默认实现是如何工作的。现在我们要演示一个例子来说明如何定制你自己的RequestProcessor。为了展示创建用户定制的RequestProcessor,我们将会让我们的示例实现下面两个业务需求:
l我们想创建一个ContactImageAction类,它将生成图片而不是平常的HTML页面。
l在每个请求处理之前,我们都想通过检查session中的userName属性来确定用户是否已经登陆。如果那个属性没有找到,我们会把用户重定向到登陆页面。

我们将分两步实现这些业务需求。
1,创建你的CustomRequestProcessor类,它将继承自RequestProcessor类,如下:

publicclassCustomRequestProcessor
extendsRequestProcessor{
protectedbooleanprocessPreprocess(
HttpServletRequestrequest,
HttpServletResponseresponse){
HttpSessionsession=request.getSession(false);
//Ifuseristryingtoaccessloginpage
//thendon'tcheck
if(request.getServletPath().equals("/loginInput.do")
||request.getServletPath().equals("/login.do"))
returntrue;
//CheckifuserNameattributeisthereissession.
//Ifso,itmeansuserhasallreadyloggedin
if(session!=null&&
session.getAttribute("userName")!=null)
returntrue;
else{
try{
//IfnoredirectusertologinPage
request.getRequestDispatcher
("/Login.jsp").forward(request,response);
}catch(Exceptionex){
}
}
returnfalse;
}

protectedvoidprocessContent(HttpServletRequestrequest,
HttpServletResponseresponse){
//CheckifuserisrequestingContactImageAction
//ifyesthensetimage/gifascontenttype
if(request.getServletPath().equals("/contactimage.do")){
response.setContentType("image/gif");
return;
}
super.processContent(request,response);
}
}


在CustomRequestProcessor类的processPreprocess方法中,我们检查session的userName属性,如果没有找到,就将用户重定向到登陆页面。

对于生成图片作为输出的需求,我们必须覆盖processContent方法,首先检查请求是否是/contactimage路径。如果是的话,我们就会将contentType设置为image/gif;否则设置为text/html。

2,在你的struts-config.xml文件的<action-mappint>元素之后加入下面的文字,告诉StrutsCustomRequestProcessor应当被用作RequestProcessor类:

<controller>
<set-propertyproperty="processorClass"
value="com.sample.util.CustomRequestProcessor"/>
</controller>


请注意,当你只有很少的action类需要生成非text/html类型的输出时,你覆写processContent()方法是OK的。如果不是这样子的话,你应该创建一个Struts的子应用来处理请求生成图片的Action,并为它们将contentType设置为image/gif。

Struts的Tiles框架就是使用它自己的RequestProcessor来装饰Struts的输出。

ActionServlet
如果你查看你的Strutsweb应用的web.xml,你会看到这样的文字:

<web-app>
<servlet>
<servlet-name>action=</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<!--Allyourinit-paramsgohere-->
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>


这意味着ActionServlet负责处理你所有Struts的请求。你可以创建一个ActionServlet的子类,当应用启动,关闭,每个请求的时候做一些特定的事情。但是在继承ActionServlet类之前,你应该尽量创建一个PlugIn或RequestProcessor去解决你的问题。在Servlet1.1之前,Tiles框架是基于ActionServlet来修饰生成的响应。但是从1.1之后,它开始使用TilesRequestProcessor类。

总结
决定开发你自己的MVC框架是一个非常大的决定,你必须要考虑开发和维护框架代码所花费的时间和资源。Struts是一个非常强大和稳定的框架,你可以修改它来满足你绝大多数的业务需求。
但另一方面,也不要草率地做出扩展Struts的决定。如果你在RequestProcessor中写了一些性能比较低的代码,它将会在每次请求时执行,因而降低你整个应用的效率。而且还是有一些情况,开发自己的MVC框架要比扩展Struts好。
分享到:
评论

相关推荐

    Spring + struts 整合的三种主要方式

    ### Spring与Struts整合的三种主要方式 在Java Web开发领域,Spring框架和Struts框架都是非常重要的技术。Spring框架以其强大的依赖注入(DI)和面向切面编程(AOP)功能,为Java应用提供了轻量级的解决方案。而...

    在struts2的freemarker模板中扩展struts标签

    众所周知,struts2宣称freemarker模板中不再支持自定义标签,但如果工程UI仅用freemarker模板可以通过扩展struts标签简单实现,不是采用官方不推荐的配置JspSupportServlet实现的!内付详细说明及范例,此方法仅为团队...

    struts 三种配置方式.txt

    ### Struts 2 的三种配置方式详解 #### 一、引言 Struts 2 是一个基于Java的开源Web应用框架,它继承了Struts 1的优点并在此基础上进行了改进和扩展,支持更加灵活的配置方式。Struts 2 的核心配置文件是`struts....

    Struts三种开发方法案例

    在Struts 1.x版本中,开发人员通常有三种不同的方法来构建应用程序:全手工编写、半手工半工具辅助以及全工具自动化。下面将详细阐述这三种方法,并通过案例分析它们的特点与使用场景。 1. **全手工开发** 全手工...

    使用freemarker扩展struts标签

    本话题将深入探讨如何使用FreeMarker扩展Struts2的标签库,以增强视图层的表现力和灵活性。 首先,FreeMarker是一个基于模板的语言,它允许开发者用简单的语法来处理数据模型并生成HTML或其他文本。在Struts2中,...

    扩展Struts示例程序的代码

    使用一个Struts应用的示例来示范如何使用这三种方式来扩展Struts。两个扩展Struts成功的范例是Struts自身的Validation和Tiles框架 用到了Hibernate, Spring, Struts的结构 此源代码教程: ...

    struts2的一些扩展用法

    Struts2是一个强大的Java web应用程序框架,用于构建和维护可扩展、高效且易于维护的Web应用。这篇博客文章“struts2的一些扩展用法”很可能深入探讨了如何超越Struts2的基本功能,利用其丰富的扩展机制来提升开发...

    spring struts 融合的三种方式详解

    ### Spring Struts融合的三种方式详解 #### 一、引言 随着企业级应用的不断发展,集成多种框架来构建高效、灵活的应用系统已成为一种趋势。Spring 和 Struts 分别作为 Java 开发领域内的两个重要框架,Spring 提供...

    SSH-struts第一种方式整合

    在本项目"SSH-struts第一种方式整合"中,我们将深入探讨如何将这三个组件结合在一起,构建出强大的后端业务逻辑和直观的前端展示。 首先,Struts作为MVC(模型-视图-控制器)设计模式的实现,主要负责处理HTTP请求...

    Struts2 Rest方式和非Rest方式共存

    这篇博客文章探讨了如何在同一个Struts2应用中实现这两种方式的并存。 RESTful API设计原则强调资源的表示和通过HTTP方法操作这些资源来改变状态。在Struts2中,可以通过配置特定的ActionMapper来支持REST风格的URL...

    三种整合 Struts 应用程序与 Spring 的方式

    本文主要介绍了三种整合Struts应用程序与Spring的方法。 首先,Spring的ActionSupport类提供了一种简单的方式将Struts动作与Spring容器集成。通过继承Spring的ActionSupport类,Struts的动作类可以直接访问由Spring...

    扩展Struts1.x标签

    总之,扩展Struts1.x的标签功能是一种常见的优化视图层开发的策略,它能提高代码的灵活性和可重用性,同时也降低了维护成本。通过定制Select和Table标签,并结合XML文件来动态管理数据,开发者可以更高效地构建和...

    Struts2 三种下载方法 源码(有图有真相)

    为了帮助理解,`三种下载方式.png`很可能是展示了这三种方法在Web页面上的呈现效果,而`13Struts2DownLoad`可能包含了具体实现这些功能的源代码文件。研究这些文件将有助于深入理解Struts2文件下载的实现过程。 总...

    去掉.action去掉.do字样 隐藏struts2 URL地址action, strus1的扩展名do也是同理.zip

    对于Struts1,隐藏.do扩展名的处理方式略有不同。在web.xml文件中,我们需要配置Front Controller(DispatcherServlet)来处理请求,如下所示: ```xml &lt;servlet-name&gt;action &lt;url-pattern&gt;/ ``` 这样,所有以...

    Struts的小列子三个Action

    在描述中提到的"三个Action"可能分别代表了三种不同的用户请求处理逻辑。每个Action类对应一个特定的用户交互,负责接收HTTP请求,处理数据,然后转发到相应的视图进行展示。 1. **Action类的创建**:在Struts中,...

    struts1和struts2的区别

    OGNL提供了一种简洁的方式来访问和修改对象的属性,极大地提高了开发效率。 #### 视图技术 - **Struts1**: 使用标准的JSP页面作为视图层,页面上的数据显示主要依赖于JSP标签。 - **Struts2**: 引入了ValueStack...

    利用Turbine的事件映射来扩展Struts的功能.doc

    本文旨在介绍如何利用Turbine的事件映射扩展Struts的功能,使得Struts的Action类可以更加灵活和高效。 【详细知识点】: 1. **Turbine与Struts的比较**: - Turbine是一个全面的Web应用框架,而Struts更专注于MVC...

    struts2.0整合Struts 1

    4. **配置方式**:Struts 2支持XML和注解两种配置方式,注解配置更加简洁,减少了XML的繁琐。 5. **Tiles视图技术**:虽然不是Struts 2的核心部分,但可以通过Struts 2的集成来实现页面布局和组件重用,提高开发...

Global site tag (gtag.js) - Google Analytics