`
xiaoma1234520
  • 浏览: 2302 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
社区版块
存档分类
最新评论

Struts的扩展(来自LOVEEXCEPTION)

阅读更多

 我看到很多项目中,开发者实现了自己的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。示例程序的代码可以从java>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好。

分享到:
评论

相关推荐

    struts2的一些扩展用法

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

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

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

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

    本篇文章将详细介绍如何在Struts2中去除.action后缀,以及Struts1中隐藏.do扩展名,使URL更加简洁。 首先,让我们来看看Struts2的情况。在Struts2中,默认情况下,所有的请求都会映射到.action结尾的URL上。为了...

    Struts 学习笔记 Struts 中扩展JfreeChart 笔记

    在Struts框架中扩展JfreeChart,主要是为了在Web应用中生成动态的、交互式的图表,以直观地呈现数据。以下是对这一主题的详细说明: 1. **Struts 框架理解**: - **Action与DispatcherServlet**:Struts的核心是...

    struts2.0整合Struts 1

    7. **插件架构**:Struts 2的插件体系使得扩展和定制变得简单,如 strut2-dojo-plugin 提供了与Dojo JavaScript库的集成。 整合Struts 1和Struts 2涉及到的主要工作包括: 1. **Action映射**:将Struts 1的Action...

    struts与struts2的区别

    由于Struts2提供了更现代的特性和更好的可扩展性,通常被认为比Struts1有更高的开发效率和维护性。 总的来说,Struts2是对Struts1的一次重大升级,它在许多方面进行了优化和增强,包括更强大的控制层、更灵活的视图...

    使用freemarker扩展struts标签

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

    Struts2与Struts1区别

    - 在 Struts1 中,Action 类需要继承自一个抽象基类,这限制了其可扩展性和定制性。而 Struts2 改进了这一点,Action 类可以实现 Action 接口,也可以实现其他接口,提供了更多的灵活性。ActionSupport 类则提供了...

    struts2项目开发

    2. 可扩展性:Struts2 框架提供了一个可扩展的架构,可以轻松地添加新的功能。 3. 高效性:Struts2 框架提供了一个高效的架构,可以快速地处理用户的请求。 Struts2 框架的应用场景 Struts2 框架的应用场景包括: ...

    通信与网络中的浅谈STRUTS框架应用对于Web服务扩展的作用

    如果我们想混合使用Servlets和JSP的优点来建立可扩展的应用,struts是一个不错的选择。  1 Struts框架应用模式  1)建立在MVC这种公认的好的模式上的,struts在M,V,C上都有涉及,但它主要是提供一个好的控制器和...

    Struts1和Struts2区别

    - **Struts2**:继承了Struts1的Front Controller模式,并引入了拦截器(Interceptor)机制,使得扩展性和灵活性更强。拦截器链允许在请求到达Action之前和之后执行自定义逻辑。 2. 表达式语言(EL)支持: - **...

    struts1和struts2区别

    Struts1和Struts2是两个非常著名的Java Web框架,...总的来说,Struts2在设计上更加灵活、可测试和可扩展,而Struts1则相对更注重基础功能和简单的实现。在实际项目中,开发者会根据具体需求和项目规模选择适合的框架。

    struts1.2驱动包

    7. **Interceptor(拦截器)**:虽然Struts1.2的核心版本不包含拦截器,但可以通过扩展实现,提供在请求处理流程中的预处理和后处理功能,如日志记录、权限检查等。 8. **Internationalization(国际化)**:Struts...

    张孝祥Struts视频源代码

    6. **Tiles**:是Struts的一个扩展,允许开发者定义可重用的页面布局,增强了视图的结构化管理。 在张孝祥的视频教程中,可能涵盖了以下内容: 1. **环境搭建**:如何配置开发环境,包括安装Apache Tomcat服务器,...

    struts2+spring+velocity扩展实例V1版本

    在这个"struts2+spring+velocity扩展实例V1版本"中,我们可以看到这三个框架的集成与应用。 首先,Struts2是一个基于MVC(Model-View-Controller)设计模式的Java Web框架,它的主要职责是处理用户的请求,并将其...

    Struts2接口文档

    这些插件通过扩展Struts2的能力,使得开发者能够构建更复杂、更灵活的应用。 总的来说,Struts2接口文档是学习和使用Struts2框架不可或缺的工具,它能帮助开发者高效地理解和使用框架提供的各种组件和接口,提升...

    Struts2视频教程

    通过以上对Struts2各个方面的介绍,可以看出Struts2不仅具有丰富的功能,而且设计精巧、易于扩展。掌握了Struts2的核心技术和实践经验后,开发者可以更加高效地构建高质量的Web应用。无论是初学者还是有一定经验的...

    Hibernate+Spring+Struts扩展Struts

    简介: 我看到很多项目中,开发者实现了自己的MVC框架,并不是因为他们想做同Struts根本不同的东西,而是因为他们并没有意识到如何扩展Struts。开发自己的MVC框架可以获得全部的控制权,但是这也意味着需要很多资源...

Global site tag (gtag.js) - Google Analytics