`
zhoujinxiong
  • 浏览: 25443 次
  • 性别: 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多模块开发,struts多模块开发,struts多模块开发,struts多模块开发,struts多模块开发,struts多模块开发,struts多模块开发,struts多模块开发,struts多模块开发,struts多模块...

    struts的模块话开发

    struts的模块话开发的技术事例

    struts1多模块多配置文件

    struts1多模块多配置文件的开发流畅图解

    Struts多模块[定义].pdf

    Struts多模块[定义].pdf

    Struts1.0 开发指南 多个文档

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

    使用 Easy Struts for Eclipse 开发 Struts

    网上常见本文,我把它整理了一下,更适于阅读。本文首先对 Easy Struts...本文还介绍了增强 Struts 应用程序的各种方法,例如连接数据库,对应用程序进行模块化,内容的国际化和本地化,异常处理和创建自定义插件等等。

    Struts简介 什么是Struts Struts基本运作流程

    Struts简介 什么是Struts Struts基本运作流程 ActionMapping类 Action类 ActionForm类 ActionError与ActionMessage 协同开发 模块化程序 Struts异常处理 Struts国际化支持 PlugIn接口 等等

    Struts2、Spring和Hibernate应用实例.

    而Spring的出现,在某些方面极大的方面了Struts的开发。同时,Hibernate作为对象持久化的框架,能显示的提高软件开发的效率与生产力。这三种流行框架的整合应用,可以发挥它们各自的优势,使软件开发更加的快速与...

    Java进阶:Struts多模块的技巧

    在使用struts多模块的,找到一些小技巧和经验,与大家分享一下。 关于多module的配置就不说了,只需要用不同的config, struts-config.xml作为默认module, struts-config-module.xml作为/module的配置  CSDN上有...

    struts的教程.doc

    使用应用模块(Application Modules) 21 把JSP放到WEB-INF后以保护JSP源代码 22 使用 Prebuilt Action类提升开发效率 23 Struts标记库 25 定制JSP标记 25 资源束 26 Bean标记 27 Bean复制标记 27 定义脚本...

    Struts原理、开发及项目实施

    Struts原理、开发及项目实施 Holen 2002-9-12 &lt;br/&gt;1、 摘要 2、 关键词 3、 Framework 4、 Struts的起源 5、 Struts工作原理 6、 Struts安装 7、 一个实例 8、 Struts优缺点...

    应用Struts实现网站流量统计模块源代码

    随着网站建设的日益深入,网站数量也在不断的增多。对于大型网站来说,网站的统计分析功能不可...本文件应用Struts技术实现一个网站流量统计模块。此源文件可以让我,恶魔更好的掌握应用Struts框架开发项目的业务流程!

    java Struts教程

    使用应用模块(Application Modules) 21 把JSP放到WEB-INF后以保护JSP源代码 22 使用 Prebuilt Action类提升开发效率 23 Struts标记库 25 定制JSP标记 25 资源束 26 Bean标记 27 Bean复制标记 27 定义脚本变量的...

    struts核心配置文件详解

    Struts应用的配置 多应用模块的配置 Struts配置文件 多应用模块的划分有助于应用的并行开发,提高效率

    struts2写的电子商城系统

    为了寝室里交大作业,用struts2写的一个电子商城系统。包含完整的数据库创建文件,在根项目的sql文件夹里,还有初步的测试数据。整个项目分包明确,每个功能模块一目了然。根据网上的资源实现了一个登陆时邮箱验证的...

    Struts2详细工作流程

    Struts 2框架本身大致可以分为3个部分:核心控制器FilterDispatcher、业务控制器Action和用户实现的企业业务逻辑组件...Struts 2框架按照模块来划分,可以分为Servlet Filters、Struts核心模块、拦截器和用户实现部分。

    jsp+JavaScript+struts+hibernate+css+mysql的网上书店

    利用jsp、struts、hibernate开发JavaWeb应用

Global site tag (gtag.js) - Google Analytics