Hibernate+Spring+Struts扩展Struts
程序员文章站
2023-12-13 09:20:10
简介: 我看到很多项目中,开发者实现了自己的mvc框架,并不是因为他们想做同struts根本不同的东西,而是因为他们并没有意识到如何扩展struts。开发自己的mvc框...
简介:
我看到很多项目中,开发者实现了自己的mvc框架,并不是因为他们想做同struts根本不同的东西,而是因为他们并没有意识到如何扩展struts。开发自己的mvc框架可以获得全部的控制权,但是这也意味着需要很多资源来实现它(人力物力),在紧张的日程安排下,有时候这是不可能的。
struts不仅仅是一个强大的框架,同时它也是可扩展的。你可以以三种方式来扩展struts。
1、plugin:如果你想在application startup或shutdown的时候做一些业务逻辑的话,那就创建你自己的plugin类。
2、requestprocessor:如果你想在请求被处理的过程中某个时刻做一些业务逻辑的话,那么创建你自己的requestprocessor类。比如说,在每次请求执行之前,你可以扩展requestprocessor来检查用户是否登陆了以及他是否有权限去执行某个特定的action。
3、actionservlet:如果你想在application startup和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是一个接口,你可以创建一个实现该接口的类,当application startup或shutdown的时候做些事情。
比方说,我创建了一个使用hibernate作为持久层的web应用,我想当应用启动的时候就初始化hibernate,这样子当我的web应用受到第一个请求的时候,hibernate就已经是配置好的并且可用的。同时我们想当application关闭的时候关闭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()。当application startup的时候init()方法被调用,当shutdown的时候destroy()方法被调用。struts还允许给你的plugin类传递初始化参数。为了传递参数,你必须在plugin类中为每一个参数创建javabean式的setter方法。在我们的hibernateplugin类中,我会把configfile的name作为参数传进去,而不是硬编码到程序中。
2、在struts-config.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接口的类的全限定名。对于每一个初始化参数,可以使用<set-property>元素传递参数。在我们的例子中,我要把config文件的名字传进去,所以使用了一个带有配置文件路径的<set-property>。
struts的tiles和validator框架都使用plugin来读取配置文件进行初始化。另外两件plugin可以帮你做到的事情是:
·如果你的application依赖于某些配置文件,那么你可以在plugin类中检查它们是否可用,如果不可用的话抛出一个servletexception,这样就可以使actionservlet变为不可用。
·plugin接口的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()方法使用了template method模式实现,其中有很多独立的方法来执行请求处理的每一步骤,这些方法将会在process方法中依次被调用。比如,将会有一个独立的方法用来寻找当前request对应的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、processmutipart():在这个方法中,struts将会读取request来检查request的contenttype是否是multipart/form-data。如果是的话,将会解析request并且将之包装到httpservletrequest中。当你创建了一个html form用来提交数据,那么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-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():如果配置是no-cache,struts将会为每个response设置下面三个headers:
requested in struts config.xml
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,可以在子类中覆盖它。它的缺省实现没有作任何事情,总是返回true。返回false的话将会终止当前请求的处理。
7、processmapping():这个方法将会用路径信息得到一个actionmapping对象。也就是struts-config.xml文件中的<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能够增加新的user。你所要做的就是给你的adduseraction元素增加一个role属性,这个属性的值为administrator。这样,在运行adduseraction之前,这个方法会确保用户拥有administraotr的角色。
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>
在我们的例子中,它会先在request scope中检查是否有一个org.apache.struts.action.dynaactionform类的对象存在。如果有它将会使用这个对象,如果没有它将会创建一个新的对象并把它设置在request scope。
10、processpopulate():在这个方法中,struts将会用相匹配的request参数装配actionform的实例变量。
11、processvalidate():struts将会调用你的actionform类的validate方法。如果你从validate()返回actionerrors,它将会将user重定向到<action>元素的input属性指定的页面。
12、processforward()和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()。如果你同时配置了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,我们将会让我们的示例实现下面两个业务需求:
·我们想创建一个contactimageaction类,它将生成图片而不是平常的html页面。
·在每个请求处理之前,我们都想通过检查session中的username属性来确定用户是否已经登陆。如果那个属性没有找到,我们会把用户重定向到登陆页面。
我们将分两步实现这些业务需求。
1、创建你的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属性,如果没有找到,就将用户重定向到登陆页面。
对于生成图片作为输出的需求,我们必须覆盖processcontent方法,首先检查请求是否是/contactimage路径。如果是的话,我们就会将contenttype设置为image/gif;否则设置为text/html。
2、在你的struts-config.xml文件的<action-mappint>元素之后加入下面的文字,告诉struts customrequestprocessor应当被用作requestprocessor类:
<controller>
<set-property property="processorclass"value="com.sample.util.customrequestprocessor"/>
</controller>
请注意,当你只有很少的action类需要生成非text/html类型的输出时,你覆写processcontent()方法是ok的。如果不是这样子的话,你应该创建一个struts的子应用来处理请求生成图片的action,并为它们将contenttype设置为image/gif。
struts的tiles框架就是使用它自己的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 >
这意味着actionservlet负责处理你所有struts的请求。你可以创建一个actionservlet的子类,当应用启动,关闭,每个请求的时候做一些特定的事情。但是在继承actionservlet类之前,你应该尽量创建一个plugin或requestprocessor去解决你的问题。在servlet1.1之前,tiles框架是基于actionservlet来修饰生成的响应。但是从1.1之后,它开始使用tilesrequestprocessor类。
总结
决定开发你自己的mvc框架是一个非常大的决定,你必须要考虑开发和维护框架代码所花费的时间和资源。struts是一个非常强大和稳定的框架,你可以修改它来满足你绝大多数的业务需求。
但另一方面,也不要草率地做出扩展struts的决定。如果你在requestprocessor中写了一些性能比较低的代码,它将会在每次请求时执行,因而降低你整个应用的效率。而且还是有一些情况,开发自己的mvc框架要比扩展struts好。
我看到很多项目中,开发者实现了自己的mvc框架,并不是因为他们想做同struts根本不同的东西,而是因为他们并没有意识到如何扩展struts。开发自己的mvc框架可以获得全部的控制权,但是这也意味着需要很多资源来实现它(人力物力),在紧张的日程安排下,有时候这是不可能的。
struts不仅仅是一个强大的框架,同时它也是可扩展的。你可以以三种方式来扩展struts。
1、plugin:如果你想在application startup或shutdown的时候做一些业务逻辑的话,那就创建你自己的plugin类。
2、requestprocessor:如果你想在请求被处理的过程中某个时刻做一些业务逻辑的话,那么创建你自己的requestprocessor类。比如说,在每次请求执行之前,你可以扩展requestprocessor来检查用户是否登陆了以及他是否有权限去执行某个特定的action。
3、actionservlet:如果你想在application startup和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是一个接口,你可以创建一个实现该接口的类,当application startup或shutdown的时候做些事情。
比方说,我创建了一个使用hibernate作为持久层的web应用,我想当应用启动的时候就初始化hibernate,这样子当我的web应用受到第一个请求的时候,hibernate就已经是配置好的并且可用的。同时我们想当application关闭的时候关闭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()。当application startup的时候init()方法被调用,当shutdown的时候destroy()方法被调用。struts还允许给你的plugin类传递初始化参数。为了传递参数,你必须在plugin类中为每一个参数创建javabean式的setter方法。在我们的hibernateplugin类中,我会把configfile的name作为参数传进去,而不是硬编码到程序中。
2、在struts-config.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接口的类的全限定名。对于每一个初始化参数,可以使用<set-property>元素传递参数。在我们的例子中,我要把config文件的名字传进去,所以使用了一个带有配置文件路径的<set-property>。
struts的tiles和validator框架都使用plugin来读取配置文件进行初始化。另外两件plugin可以帮你做到的事情是:
·如果你的application依赖于某些配置文件,那么你可以在plugin类中检查它们是否可用,如果不可用的话抛出一个servletexception,这样就可以使actionservlet变为不可用。
·plugin接口的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()方法使用了template method模式实现,其中有很多独立的方法来执行请求处理的每一步骤,这些方法将会在process方法中依次被调用。比如,将会有一个独立的方法用来寻找当前request对应的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、processmutipart():在这个方法中,struts将会读取request来检查request的contenttype是否是multipart/form-data。如果是的话,将会解析request并且将之包装到httpservletrequest中。当你创建了一个html form用来提交数据,那么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-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():如果配置是no-cache,struts将会为每个response设置下面三个headers:
requested in struts config.xml
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,可以在子类中覆盖它。它的缺省实现没有作任何事情,总是返回true。返回false的话将会终止当前请求的处理。
7、processmapping():这个方法将会用路径信息得到一个actionmapping对象。也就是struts-config.xml文件中的<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能够增加新的user。你所要做的就是给你的adduseraction元素增加一个role属性,这个属性的值为administrator。这样,在运行adduseraction之前,这个方法会确保用户拥有administraotr的角色。
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>
在我们的例子中,它会先在request scope中检查是否有一个org.apache.struts.action.dynaactionform类的对象存在。如果有它将会使用这个对象,如果没有它将会创建一个新的对象并把它设置在request scope。
10、processpopulate():在这个方法中,struts将会用相匹配的request参数装配actionform的实例变量。
11、processvalidate():struts将会调用你的actionform类的validate方法。如果你从validate()返回actionerrors,它将会将user重定向到<action>元素的input属性指定的页面。
12、processforward()和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()。如果你同时配置了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,我们将会让我们的示例实现下面两个业务需求:
·我们想创建一个contactimageaction类,它将生成图片而不是平常的html页面。
·在每个请求处理之前,我们都想通过检查session中的username属性来确定用户是否已经登陆。如果那个属性没有找到,我们会把用户重定向到登陆页面。
我们将分两步实现这些业务需求。
1、创建你的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属性,如果没有找到,就将用户重定向到登陆页面。
对于生成图片作为输出的需求,我们必须覆盖processcontent方法,首先检查请求是否是/contactimage路径。如果是的话,我们就会将contenttype设置为image/gif;否则设置为text/html。
2、在你的struts-config.xml文件的<action-mappint>元素之后加入下面的文字,告诉struts customrequestprocessor应当被用作requestprocessor类:
<controller>
<set-property property="processorclass"value="com.sample.util.customrequestprocessor"/>
</controller>
请注意,当你只有很少的action类需要生成非text/html类型的输出时,你覆写processcontent()方法是ok的。如果不是这样子的话,你应该创建一个struts的子应用来处理请求生成图片的action,并为它们将contenttype设置为image/gif。
struts的tiles框架就是使用它自己的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 >
这意味着actionservlet负责处理你所有struts的请求。你可以创建一个actionservlet的子类,当应用启动,关闭,每个请求的时候做一些特定的事情。但是在继承actionservlet类之前,你应该尽量创建一个plugin或requestprocessor去解决你的问题。在servlet1.1之前,tiles框架是基于actionservlet来修饰生成的响应。但是从1.1之后,它开始使用tilesrequestprocessor类。
总结
决定开发你自己的mvc框架是一个非常大的决定,你必须要考虑开发和维护框架代码所花费的时间和资源。struts是一个非常强大和稳定的框架,你可以修改它来满足你绝大多数的业务需求。
但另一方面,也不要草率地做出扩展struts的决定。如果你在requestprocessor中写了一些性能比较低的代码,它将会在每次请求时执行,因而降低你整个应用的效率。而且还是有一些情况,开发自己的mvc框架要比扩展struts好。