`
zhoujinxiong
  • 浏览: 25872 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类

struts 多模块开发

阅读更多
扩展
多模块开发和软件扩展是现代软件开发过程中最重要的理念。对于一个框架型软件来说,能否进行多模块开发、能否根据需要进行扩展、能否与其他组件无缝合作是衡量一个优秀框架的重要因素。优秀的框架应具有良好的扩展性和协作性,Struts框架也不例外。Struts框架为开发人员提供了多模块开发的方法以及多个扩展点,本章将对这些内容进行介绍。
多模块开发
对于一些大型的Web应用,通常会分为几个模块,如用户管理模块,商品管理模块。如果设计得当,这些模块间就可以同时并行开发,大幅提高开发进度。模块化开发是现在大中型应用程序开发的流行选择。
并行开发的一个最大的问题就是资源访问冲突,如果处置不当,反而会影响开发效率。Struts的配置文件struts-config.xml是Struts框架最重要的资源之一,并且是需要频繁改动的。如果并行开发的各个团队都是用这一个配置文件,势必造成访问冲突。Strus框架的模块化机制就是专门应对这种情况的。
Struts从1.1版本开始增加了模块化支持,并且一直在强化对模块化的支持。不同的应用模块可以拥有各自的struts-config配置文件、消息资源、Validator框架配置文件。不同的模块可以协同开发,互不影响。
模块在Struts中的英文术语是module,一些与模块化相关的组件的名称中都包含有module这样的字眼,如模块配置类为ModuleConfig,模块实用工具类ModuleUtils等。
如果要将Struts应用配置为多模块应用,需要如下三个步骤:
l                为每个模块分别建立一个struts配置文件;
l                通知模块控制器;
l                使用特定的Action在模块间跳转。
下面分别对这个三步骤进行详细介绍,以掌握如何实现多模块的Struts应用程序。
Struts应用程序是通过配置文件组织资源的,对于多模块应用,每个模块也是通过各自的配置文件组织各自的资源。当只有一个模块时,即只有默认模块,通常默认模块的配置文件名为 struts-config.xml。其他模块的命名方式一般为struts-config-模块名.xml。如用户管理模块的配置文件可命名为 struts-config-usermanage.xml,商品管理模块的配置文件名为struts-config- productmanage.xml。当然,这样的文件名并不是必须的,但按照这种方式给文件命名从配置文件名本身就可以看出对应的模块和意义。
在多模块环境中,每个模块都有各自独立的配置文件,十分有利于多个小组的协同开发。在小组协同开发的环境中,通常是一个小组负责一个开发模块。Struts的多模块机制可以有效地避免多个小组协同开发中的资源访问冲突,因为每个模块的资源都由各自的配置文件组织,绝大部分的修改都限于本模块内。
通知控制器即是将多模块的配置信息注册到控制器中去。在单模块Struts应用中,只需要把默认的Struts配置文件注册到控制器,这是通过将默认配置文件作为 ActionServlet类的一个初始化参数实现的。对于多模块的情况,配置的示例代码片段如下(该代码片断来自web.xml)。
代码12.1
<init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
        <param-name>config/module1</param-name>
        <param-value>/WEB-INF/struts-config-module1.xml</param-value>
</init-param>
在这段配置中,配置了两个应用模块:默认模块和名为module1的模块。对于默认模块,配置文件对应的ActionServlet初始化参数名为config。对于其他模块,ActionServlet初始化参数的命名原则是“config/模块名”。如上面的代码示例中,module1模块的配置文件对应的初始化参数为 config/modul1。其中前缀“config/”是不能缺少的,后面跟模块名。在Struts控制器中,是通过模块名来区分不同模块的。在资源访问中,也是一模块名作为前缀来区分对不同模块的访问。如以“/module1”开头的路径会告诉控制器所要访问的将是module1模块的资源。
在模块间跳转不同于模块内部的跳转,如果需要跳转到其他模块,在连接中必须明确指出。进行模块跳转的URL必须给定两个参数:prefix和page。其中参数prefix指明要跳转到的模块前缀,其值为以“/”开头的模块名。如前面配置片段中的module1,其前缀prefix的值就是/module1。page指明要跳转的页面或其他资源。
模块间的跳转之所以不同于一般的页面调转,是因为模块间的跳转涉及到资源的转换,包括消息资源和其他可配置资源。有3种方法可以实现模块间跳转。
1. 使用Struts内建的SwitchAction类
SwitchAction类是 Struts内建的最有用的Action类之一,是专门为实现页面调转而设计的。在SwitchAction类内部,自动实现了消息资源和模块前缀的转换等操作。直接使用SwitchAction类只需要在Struts配置文件中声明即可,声明使用SwitchAction类的配置片段如下。
代码12.2
……
<action-mappings>
    <action
path="/toModule"
type="org.apache.struts.actions.SwitchAction"/>
……
</action-mappings>
其中path="/toModule"指明了该Action类的访问路径。如果要从当前模块跳转到另一模块moduleB,则该链接的形式为:
http://localhost:8080/xxx/toModule.do?prefix=/moduleB&page=/index.do
如果要调转到的模块是默认模块,默认模块的模块前缀是空串,链接的形式为:
http://localhost:8080/xxx/toModule.do?prefix=&page=/index.do
2. 使用转发
可以在全局或局部转发中显式地将转发配置为跨模块的转发。配置全局转发为跨模块转发的示例代码如下:
代码12.3
<global-forwards>
    <forward
name="toModuleB"
contextRelative="true
path="/moduleB/index.do"
redirect="true"/>
……
</global-forwards>
其中contextRelative属性设为true时表示当前path属性以/开头时,给出的是相对于当前上下文的URL。
也可以把局部转发配置为跨模块转发,如代码12.4所示。
代码12.4
<action-mappings>
   <action ... >
       <forward name="success" contextRelative="true" path="/moduleB/index.do" redirect="true"/>
   </action>
   ……
</action-mappings>
3. 使用<html:link>标记
<html:link>是Struts自定义标记,对超链接的行为进行了定制和封装。利用<html:link>标记可以直接将超链接声明为跨模块的跳转,使用方法为:
<html:link module="/moduleB" path="/index.do"/>
 使用定制的控制器
Struts在控制器层提供了多种扩展点,如扩展ActionServet、扩展RequestProcessor类、扩展Action类和ActionForm类等。下面将详细介绍如何在这些扩展点上实现扩展。
在Struts的早期版本中,ActionServlet类承担了除Action类以外大部分的控制器能,Struts一般都需要扩展ActionServlet类,来实现各种自定义的控制功能。在Struts 1.1之后,大多数执行实际功能调用的操作都被迁移到RequestProcessor类中执行,ActionServlet类只是调用 RequestProcessor对象的process()方法处理用户请求,如代码12.5所示。
代码12.5
public class ActionServlet extends HttpServlet {
      ……
public void doGet(HttpServletRequest request,
              HttpServletResponse response)
        throws IOException, ServletException {
        // 调用process()方法处理Get请求
        process(request, response);
}
public void doPost(HttpServletRequest request,
               HttpServletResponse response)
        throws IOException, ServletException {
        // 调用process()方法处理Post请求
process(request, response);
}
    ……
//在process()方法中,调用RequestProcessor对象的process()方法处理请求
protected void process(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {
       
        // 配置请求的模块信息
        ModuleUtils.getInstance().selectModule(request, getServletContext());
        ModuleConfig config = getModuleConfig(request);
        // 获取请求对应的RequestProcessor实例
RequestProcessor processor = getProcessorForModule(config);
        if (processor == null) {
           processor = getRequestProcessor(config);
        }
        // 调用RequestProcessor对象的process()方法处理请求
        processor.process(request, response);
}
    ……
}
因此,如果需要在处理机制和功能上扩展Struts框架,扩展ActionServlet类已经没有必要。但是,处理一些特殊的需求时,ActionServlet类仍然具有扩展的意义。
在Struts控制器组件一章已经介绍过,Struts应用程序启动时,在ActionServlet类的init()方法中将初始化Struts框架。如果想改变框架包的初始化行为,就可以通过扩展ActionServlet类实现。扩展ActionServlet类,可以创建一个 org.apache.struts.action.ActionServlet类的子类,然后复写init()方法,实现自定义的Struts框架初始化过程。
一旦扩展了ActionServlet类,就需要在部署描述文件web.xml文件配置中心Servlet为扩展后的Servlet。
从Struts1.1开始,绝大部分的请求处理行为已经交给RequestProcessor类,如要扩展Struts框架的请求处理方式,可以通过扩展RequestProcessor类完成,这将在下一小节中介绍。
如果要改变Struts框架处理请求的过程,扩展RequestProcessor类是一个合适的选择。如果扩展了RequestProcessor类,需要通知Struts框架使用自定义的请求处理器。在Struts配置文件中controller元素用于配置请求处理器信息。从配置文件一章中我们知道,controller元素位于 action-mappings元素和message-resources元素之间。如果配置文件中还没有任何controller元素的配置信息,则意味着当前的Struts应用正在使用默认的RequestProcessor类及默认的controller属性。controller元素的配置如下。
代码12.6
<controller nocache="true"
inputForward="true"
maxFileSize="2M"
contentType="text/html;charset=UTF-8"
processorClass=
"org.digitstore.web.struts.CustomRequestProcessor"/>
controller元素的 processorClass属性指定了请求处理类,默认时即为org.apache.struts.action.RequestProcessor 类。Struts框架在启动时将创建一个该类的实例,并用这个实例处理应用程序的所有请求。由于每个子应用模块都可以有独立的Struts配置文件,因此可以为每个子应用模块配置不同的RequestProcessor类。
RequestProcessor 类的一个典型的扩展点就是processPreprocess()方法。顾名思义,processPreprocess()的意思就是在处理请求前的一些预处理操作。在默认的RequestProcessor类中,该方法不执行任何操作,直接返回true。processPreprocess()方法在默认RequestProcessor类中的实现如下。
protected boolean processPreprocess(HttpServletRequest request,
                        HttpServletResponse response) {
return (true);//在默认的实现中什么操作也不做,直接返回true
}
RequestProcessor类在process()方法中处理请求,对processPreprocess()方法也存在该方法的过程中。代码12.7为processPreprocess()方法在process()方法中的调用情况。
代码12.7
public void process(HttpServletRequest request,
                        HttpServletResponse response)
        throws IOException, ServletException {
    ……
// 调用processPreprocess方法,如果返回false,则终止请求处理
    if (!processPreprocess(request, response)) {
        return;
    }
    ……
    // 构建ActionForm
    ActionForm form = processActionForm(request, response, mapping);
    processPopulate(request, response, form, mapping);
    if (!processValidate(request, response, form, mapping)) {
        return;
    }
    ……
    // 构建请求对应的Action
    Action action = processActionCreate(request, response, mapping);
    if (action == null) {
        return;
    }
    // 调用Action执行请求逻辑
    ActionForward forward =
processActionPerform(request, response,action, form, mapping);
……
}
从代码12.7可以看到,process()方法在调用processActionForm()方法构建ActionForm之前(当然也在调用Action类的 execute()方法之前)就调用processPreprocess()方法。默认时该方法返回true,使得处理流程继续前进。如果该方法返回 false,process()方法就将停止请求处理的执行,并从doGet()或doPost()方法中返回。
在实现自定义的 RequestProcessor类时,可以覆盖processPreprocess()方法来实现一些特定的逻辑。例如,可以在 processPreprocess()方法中实现用户验证:如果用户已经登录,则按照正常的流程处理请求;如果用户没有登录或session已过期,则把请求重定向到登录页面。实现这段逻辑的processPreprocess()方法的代码如下。
代码12.8
public class CustomRequestProcessor
    extends RequestProcessor {
    // 自定义的processPreprocess()方法,检查用户是否已经登录
    protected boolean processPreprocess (
        HttpServletRequest request,
        HttpServletResponse response) {
        HttpSession session = request.getSession(false);
       
//如果用户请求的是登录页面则不需要检查
        if( request.getServletPath().equals("/loginInput.do")
            || request.getServletPath().equals("/login.do") )
            return true;
        // 检查session中是否存在userName属性,如果存在则表示拥护已经登录
        if( session != null &&
        session.getAttribute("userName") != null)
            return true;
        else{
            try{
                // 用户未登录则重定向到登录页面
                request.getRequestDispatcher
                    ("/Login.jsp").forward(request,response);
            }catch(Exception ex){
            }
        }
        return false;
    }
    // 自定义的processContent ()方法,用于设置响应类型
    protected void processContent(HttpServletRequest request,
                HttpServletResponse response) {
            // 检查用户是否请求ContactImageAction,如果是则设置
            // contentType 为image/gif
            if( request.getServletPath().equals("/contactimage.do")){
                response.setContentType("image/gif");
                return;
            }
        super.processContent(request, response);
    }
}
processPreprocess()方法能够截获所有Struts请求,达到对用户进行验证的效果。当然RequestProcessor类中并非只有这一个扩展点。如果想按照自己的方式处理请求,可以根据需要扩展其他方法。
Action类是Struts框架中应用最广泛的扩展点。一般情况下,其他的Action类都是直接扩展org.apache.struts.action.Action类来实现控制逻辑。在实现具体的Struts应用时,许多Action类都要执行一些公共的逻辑,如session验证、初始化session变量、错误处理等以及一些通用的方法等。这时就可以创建一个Action基类,在Action基类中定义应用中所有Action的一些公共逻辑,其他具体的Action都扩展这个 Action基类,而不是直接扩展org.apache.struts.action.Action类。当然这个Action基类扩展Struts的 Action基类或其他扩展Struts的Action基类的Action。这种处理方式可以提高代码的可重用性,减少代码冗余。代码12.9为一个 Action基类的代码示例,在该Action基类中实现了一些Action的公共逻辑。该Action基类扩展了Struts框架内建的 LookupDispatchAction类。
代码12.9
import java.util.Date;
import java.util.Enumeration;
……
// BaseAction扩展Struts内建的LookupDispatchAction
public class BaseAction extends LookupDispatchAction {
    // 日志工具
    protected transient final Log log = LogFactory.getLog(getClass());
    private static final String SECURE = "secure";
    protected Map defaultKeyNameKeyMap = null;
    public Map getKeyMethodMap() {
        Map map = new HashMap();
 
        String pkg = this.getClass().getPackage().getName();
        ResourceBundle methods =
                ResourceBundle.getBundle(pkg + ".LookupMethods");
        Enumeration keys = methods.getKeys();
        while (keys.hasMoreElements()) {
            String key = (String) keys.nextElement();
            map.put(key, methods.getString(key));
        }
        return map;
    }
 
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
            throws Exception {
        // 如果是“取消”操作直接返回
        if (isCancelled(request)) {
            ActionForward af = cancelled(mapping, form, request, response);
            if (af != null) {
                return af;
            }
        }
        MessageResources resources = getResources(request);
        // 获取本地化的取消按钮的标识
        String edit = resources.getMessage(Locale.ENGLISH, "button.edit").toLowerCase();
        String save = resources.getMessage(Locale.ENGLISH, "button.save").toLowerCase();
        String search = resources.getMessage(Locale.ENGLISH, "button.search").toLowerCase();
        String view = resources.getMessage(Locale.ENGLISH, "button.view").toLowerCase();
        String[] rules = {edit, save, search, view};
        // 从配置文件中取得对应方法名称的参数名
        String parameter = mapping.getParameter();
        // 被调用的方法名称
        String keyName = null;
       
        // 根据parameter从获取请求的方法名称
        if (parameter != null) {
            keyName = request.getParameter(parameter);
        }
        // 如果调用的方法名为空,则选择一个最接近的方法
        if ((keyName == null) || (keyName.length() == 0)) {
            for (int i = 0; i < rules.length; i++) {
                // 如果Servet路径中含有该请求规则,则调用相应方法
                if (request.getServletPath().indexOf(rules[i]) > -1) {
                    return dispatchMethod(mapping, form, request, response, rules[i]);
                }
            }
            // 无法将请求匹配到任何方法,调用unspecified()方法
return this.unspecified(mapping, form, request, response);
        }
        // parameter 村在,获取方法名
        String methodName =
                getMethodName(mapping, form, request, response, parameter);
        // 调用指定方法处理请求
        return dispatchMethod(mapping, form, request, response, methodName);
    }
   
    // 获取请求对应的ActionForm
    protected ActionForm getActionForm(ActionMapping mapping,
                                       HttpServletRequest request) {
        ActionForm actionForm = null;
        // 删除过时的form bean
        if (mapping.getAttribute() != null) {
            // 如果ActionForm的作用于为request
if ("request".equals(mapping.getScope())) {
                actionForm =
                        (ActionForm) request.getAttribute(mapping.getAttribute());
            } else {// 如果ActionForm的作用于为session
                HttpSession session = request.getSession();
                actionForm =
                        (ActionForm) session.getAttribute(mapping.getAttribute());
            }
        }
        return actionForm;
    }
   
    // 删除请求对应的ActionForm
    protected void removeFormBean(ActionMapping mapping,
                                  HttpServletRequest request) {
        // Remove the obsolete form bean
        if (mapping.getAttribute() != null) {
            if ("request".equals(mapping.getScope())) {
                request.removeAttribute(mapping.getAttribute());
            } else {
                HttpSession session = request.getSession();
                session.removeAttribute(mapping.getAttribute());
            }
        }
    }
   
    // 更新请求对应的ActionForm
    protected void updateFormBean(ActionMapping mapping,
                                  HttpServletRequest request, ActionForm form) {
        // Remove the obsolete form bean
        if (mapping.getAttribute() != null) {
            if ("request".equals(mapping.getScope())) {
                request.setAttribute(mapping.getAttribute(), form);
            } else {
                HttpSession session = request.getSession();
                session.setAttribute(mapping.getAttribute(), form);
            }
        }
    }
 
    // 从方法-名称映射表中获取方法名称
    protected String getLookupMapName(HttpServletRequest request,
                                      String keyName,
                                      ActionMapping mapping)
            throws ServletException {
        String methodName = null;
        try {
            this.setLocale(request, request.getLocale()); 
            methodName = super.getLookupMapName(request, keyName, mapping);// 获取方法名
        } catch (ServletException ex) {
                System.out.pringln("BaseAction: keyName not found in resource bundle with locale ");
            }
            // 无法在资源文件中找到对应的本地化消息文本,就使用默认地区的消息文本
            if (defaultKeyNameKeyMap == null) {
                defaultKeyNameKeyMap = this.initDefaultLookupMap(request);
            }
            // 获取消息文本
            String key = (String) defaultKeyNameKeyMap.get(keyName);
            if (key == null) {
                System.out.println("keyName '" + keyName + "' not found in resource bundle with locale ");
分享到:
评论

相关推荐

    struts多模块.pdf

    ### Struts多模块开发概述与实践 #### 一、引言 在当今的软件开发领域,多模块开发和软件扩展性已成为评估一个框架优劣的关键指标。尤其对于框架型软件而言,其是否支持多模块开发、是否具备良好的扩展性及与其他...

    struts多模块配制

    在Struts多模块配置中,每个模块代表一个独立的功能或者业务领域,它们之间通过定义良好的接口进行通信,降低了模块间的耦合度。以下是一些关键的知识点: 1. **模块化设计**:模块化是软件工程中的最佳实践,通过...

    struts2 模块包含

    博客链接可能提供了更深入的讲解,包括如何配置和使用Struts2的模块,以及如何利用模块化设计优化项目开发。 通过了解和掌握Struts2的模块化设计,开发者可以更高效地组织和管理代码,降低项目的复杂性,并提高团队...

    struts的模块话开发

    以下是对Struts模块化开发的详细说明。 在Struts框架中,模块通常指的是ActionServlet配置中的 `&lt;package&gt;` 元素。每个`&lt;package&gt;`都可以看作是一个独立的处理单元,包含了特定业务逻辑的Action类、配置文件以及...

    struts1多模块多配置文件

    本文将详细介绍如何在Struts1框架中实现多模块开发,并通过多配置文件来管理不同模块的配置信息。 #### 二、多模块架构的优势 1. **清晰的代码结构**:每个模块都有明确的功能边界,便于理解和维护。 2. **易于扩展...

    Struts多模块[定义].pdf

    Struts框架从1.1版本开始引入了对多模块开发的支持,使得开发者能够将复杂的应用拆分成更小、更易于管理和测试的部分。 在Struts中,每个模块对应一个独立的`struts-config.xml`配置文件,用于定义该模块的行为、...

    Java进阶Struts多模块的技巧.rar

    在Java开发领域,Struts框架是一个非常经典的MVC(Model-View-Controller)架构,用于构建Web应用...通过深入学习和实践“Java进阶Struts多模块的技巧”,开发者可以更好地掌握Struts框架,提升开发效率和软件质量。

    struts多模块

    在开发大型复杂系统时,通常会采用多模块架构来提高代码的可维护性和可扩展性。本文将详细介绍如何在Struts框架下实现多模块配置。 在Struts框架中,多模块的实现主要依赖于Action和配置文件的划分。每个模块可以视...

    struts文档

    ### Struts框架下的多模块开发与扩展 #### 引言 在现代软件工程实践中,多模块开发和软件的可扩展性成为了评估一个框架优劣的关键指标。尤其在Web开发领域,像Struts这样的MVC(Model-View-Controller)框架因其...

    Struts2程序开发实验

    Struts2 程序开发实验报告 Struts2 程序开发实验报告是沈阳工学院的一个实验报告,旨在让学生掌握 Struts2 框架的使用和配置方式,以设计和实现一个论坛系统。该实验报告涵盖了 Struts2 的 action、配置方式、拦截...

    Struts2注解开发jar

    标题提到的"Struts2注解开发jar"主要指的是`struts2-convention-plugin`,这是一个Struts2的插件,它的主要作用是支持基于约定优于配置(Convention over Configuration)的开发模式。描述中提到的`struts2-...

    Struts2独立开发包(2.5.10)

    10. **国际化与本地化**:Struts2支持多语言环境,通过资源文件可以轻松实现应用的国际化和本地化。 11. **插件体系**:Struts2具有丰富的插件系统,如Freemarker插件、Tiles插件、Spring插件等,能够满足各种特定...

    Struts1.0 开发指南 多个文档

    Struts1.0学习文档-初学者入门.doc ...Struts模块化编程教程 .doc struts傻瓜式学习(一天篇).doc 实例学习 Struts.doc 样章第02章 第一个Struts应用helloapp应用.doc 用Struts建立MVC应用的介绍.doc

    struts 实现模块化

    在Struts.xml文件中,你可以为每个模块定义一个或多个Action,每个Action对应一个特定的业务操作。通过配置`&lt;action&gt;`标签,指定Action类、输入输出结果以及异常处理。 2. **包的概念**:在Struts 2中,引入了包...

    Struts更多文章模块

    在本篇文章模块中,我们将深入探讨Struts框架在处理"更多文章"功能时的关键技术和实现方式。 一、Struts框架基础 1.1 Struts概述:Struts是由Apache软件基金会维护的,它的核心是ActionServlet,负责处理HTTP请求,...

    Struts开发模式经验总结

    这种做法可以实现模块化开发,每个模块有自己的配置文件和消息资源,便于管理和维护。在web.xml中通过`config-file`指定多个Struts配置文件,而消息资源文件则根据需要加载不同的语言包。 6. **实战Struts**: - ...

Global site tag (gtag.js) - Google Analytics