Hibernate+Spring+Struts扩展Struts
副标题#e#
简介:
我看到许多项目中,开拓者实现了本身的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>
#p#副标题#e#
属性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是如何被处理惩罚的
#p#分页标题#e#
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>
#p#分页标题#e#
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()。
#p#分页标题#e#
上面的列表说明白默认的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是一个很是强大和不变的框架,你可以修改它来满意你绝大大都的业务需求。
#p#分页标题#e#
但另一方面,也不要纰漏地做出扩展Struts的抉择。假如你在RequestProcessor中写了一些机能较量低的代码,它将会在每次请求时执行,因而低落你整个应用的效率。并且照旧有一些环境,开拓本身的MVC框架要比扩展Struts好。