`
isiqi
  • 浏览: 16562399 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

Struts-menu源码分析(转贴)

阅读更多
好的代码读起来让人如饮醍醐,读完以后神清气爽。如果你想提高你的编程水平,如果你想提高你的设计能力,如果你也想成为大师,那么就去阅读代码吧。以本人十几年来的编程经验,阅读代码能让你得到的比阅读文章(那怕是大师的文章)得到的更多。优秀而且实用的代码有很多,比如Junit,比如Jive,比如petStore,甚至是tomcat的Example、Log4j的Example。

Struts-Menu也来自一位大师的作品, Matt Raible。有很多优秀的作品,比如使用struts和hibernate的struts-resume。官方网站是http://raibledesigns.com/wiki/Wiki.jsp?page=Main。Struts-Menu的最新版本是2.1。功能是使用struts技术,构建树形菜单。应该说是一个非常实用的技术,极大的方便了广大的开发人员。与此同时,个人认为它的作用还不止于些。比如,同时它也是一个使用Mavenvelocity的一个很好的例子。

首先,我们去看一下它的效果。http://www.raibledesigns.com/struts-menu/。可以看到,如此丰富多彩的菜单效果,都是在演示一个配置文件里的内容。这是一个非常好的数据与表示相分离的实现。我们打开它的源码来看。首先看一下它的包图

共有五个包,其中menu自然是完成数据组织功能,是核心之一,displayer是显示方式包,完成数据显示大部分功能。也是核心之一。taglib意义明显。example自然是一些example。util是读取资源文件的包。因些,我们重点研究的包只有三个menu,displayer和taglib。

首先我们来看menu包的类图

首先是MenuPlugIn这个类。这个类的功能很明显,就是一个struts的plug-in。可以看到,它只有一个参数menuConfig,就是menu的配置文件路径。果然,在struts-conf文件中有这么一段

  <!-- ========== Plug Ins Configuration ================================== -->

<plug-in className="net.sf.navigator.menu.MenuPlugIn">
<set-property property="menuConfig" value="/WEB-INF/menu-config.xml"/>
</plug-in>

说明配置文件来自于/WEB-INF/menu-config.xml,当然,我们可以找到相应路径下找到这个文件。如果你以前没有做过struts的plug-in,现在该知道怎么做了吧,就这么简单。通过阅读初始化函数,知道它的功能就是调用MenuRepository来建立菜单。因此。我们知道MenuRepository必然是一个组织管理管理菜单的组织类。

public void init(ActionServlet servlet, ModuleConfig config)
		throws ServletException {
		if (log.isDebugEnabled()) {
			log.debug("Starting struts-menu initialization");
		}

		this.servlet = servlet;
		repository = new MenuRepository();
		repository.setLoadParam(menuConfig);
		repository.setServlet(servlet);

		try {
			repository.load();
			servlet.getServletContext().setAttribute(
				MenuRepository.MENU_REPOSITORY_KEY,
				repository);

			if (log.isDebugEnabled()) {
				log.debug("struts-menu initialization successfull");
			}
		} catch (LoadableResourceException lre) {
			throw new ServletException(
				"Failure initializing struts-menu: " + lre.getMessage());
		}
	}

打开MenuRepository类,我们可以看到这个类也很简单,不过已经有少可以学习的了。首先是FastHashMap,可以看到,这个类里有三个FastHashMap。顾名思议,是快速HashMap了,再看一下,它来自org.apache.commons.collections.FastHashMap;。看到org.apache.commons这个著名的包了?如果你以前从没使用过它,那么建议你花上一段时间去研究使用它,我保证物有所值。

protected FastHashMap menus = new FastHashMap();
protected FastHashMap displayers = new FastHashMap();
protected FastHashMap templates = new FastHashMap();

接下来我们看到log的定义。对了,log,调试的核心之一。而下面这一句则是commons log的最常用的使用方法。快快让你的程序使用上commons log吧,第一,它功能强大,第二,它使用简单,就是这么简单。

private Log log = LogFactory.getLog(getClass().getName());

下面看一个的函数

 protected Digester initDigester() {
        Digester digester = new Digester();
        digester.setClassLoader(Thread.currentThread().getContextClassLoader());
        digester.push(this);

        //digester.setDebug(getDebug());
        // 1
        digester.addObjectCreate("MenuConfig/Menus/Menu",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu");
        digester.addSetNext("MenuConfig/Menus/Menu", "addMenu");

        // 2
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item", "addMenuComponent",
            "net.sf.navigator.menu.MenuComponent");

        // 3        
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 4
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 5
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 6
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        // 7
        digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "net.sf.navigator.menu.MenuComponent", "type");
        digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
        digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item",
            "addMenuComponent", "net.sf.navigator.menu.MenuComponent");

        digester.addObjectCreate("MenuConfig/Displayers/Displayer",
            "net.sf.navigator.displayer.MenuDisplayerMapping", "mapping");
        digester.addSetProperties("MenuConfig/Displayers/Displayer");
        digester.addSetNext("MenuConfig/Displayers/Displayer",
            "addMenuDisplayerMapping",
            "net.sf.navigator.displayer.MenuDisplayerMapping");
        digester.addSetProperty("MenuConfig/Displayers/Displayer/SetProperty",
            "property", "value");
            
        return digester;
    }

这里又是一个经典,digester,Digester的使用,如果你需要读一个XML配置文件,并且不想与DOM直接打交道的话,Digester将是一个很好的选择。实际上我们看到load函数调用一句 digester.parse(input);就已经把menu-config.xml建立到内存里了,就这么简单。如果你想要初始化你的系统,这种方法是不是可以学习呢?"工欲善其事,必先利其器"。我们可以看到Raible是怎么样利用现有的工具来减轻开发量的。

由于MenuRepository举重若轻的初始化过程,甚至都没有让我们看到树形结构是怎么建立到内存里去的。不过不要着急,类图给我们了明示。

看到MenuBase类了吗?对了,看名字就知道是一个Menu的基类。可以看到,它是一个简单的JavaBean。而且相信它的每个属性大家根据名字也能猜出来。所以重点讲解是MenuComponent,一个简化的 "Composite"模式。

如上图所示。由于此处的Leaf没有任何方法,只有属性。因此Leaf和Composite收缩成了一个MenuComponent类。大家都知道,Composite模式是实现树形结构最好的方法。如果你以前没有机会实现或者没有从Composite模式得到好处,那么,从这里看一下用Composite模式得到的好处。首先看它的简单,MenuComponet的实际代码很少,加起来不到十行。

public void addMenuComponent(MenuComponent menuComponent) {
        menuComponents.add(menuComponent);
        menuComponent.setParent(this);

        if ((menuComponent.getName() == null) ||
                (menuComponent.getName().equals(""))) {
            menuComponent.setName(this.name + menuComponents.size());
        }
    }

    public MenuComponent[] getMenuComponents() {
        MenuComponent[] menus =
            (MenuComponent[]) menuComponents.toArray(_menuComponent);

        return menus;
    }

如果你用十行来实现一个树型结构(并且还是通用的),你愿不愿意?就是通过简单的这么一些代码,实现的在内存中建立树型结构的目标。

下面我们来看DispLay包,这个包的功能也是很清楚的,就是用来显示啦。这个包的类图非常漂亮,遗憾的是也非常大。只能缩小了给大家看了。

从类图中可以看到一个非常极漂亮的面象对象的设计思路。通过一个接口,利用模板方法。最后具体实现树型结构的显示。其主要方法是displayComponents和display这两方法,init方法则实现了初始化的工作,读取javascript和图片等文件。displayComponents是一个迭代函数。从而可以遍历一个MenuCompont树。并将其显示出来。

应该说,Menu包是一个M层,而Dispplya包是一个view层,而加上TagLib包,就实现了MVC的完整结构。

两个Tag类很清楚,首先我们从怎么使用它来看它们实现的功能

<menu:useMenuDisplayer name="ListMenu"
bundle="org.apache.struts.action.MESSAGE">
<menu:displayMenu name="ToDoListMenuFile"/>
<menu:displayMenu name="ToDoListMenuEdit"/>
<menu:displayMenu name="CaseDetailMenuCase"/>
<menu:displayMenu name="Standalone"/>
</menu:useMenuDisplayer>

显而易见。useMenuDisplayer这个类是实现使用哪一种显示方式。在menu-config里我们看到ListMenu的定义

<Displayer name="ListMenu"
type="net.sf.navigator.displayer.ListMenuDisplayer"/>

displayMenu则是取得一菜单,并将其显示出来,同样在menu-config也能找到。

<Menu  name="ToDoListMenuEdit"  title="EDIT">
<Item name="TDLselect" title="SELECT_ALL" image="images/select-all.png"
location="index.jsp" width="100" />
<Item name="TDLprefs" title="USER_PREFERENCES" image="images/prefs.png"
location="index.jsp" width="150" />
<Item title="Action Test" action="setPermissions?displayer=${displayer}"/>
</Menu>

查看 DisplayMenu的代码,可以看到。它完成的功能只是从context里取得MenuComponent对象,然后通过 displayer.display(menu);把它交给一个MenuDisplayer的实例来负责画出来。

因此,Control层很好的完成了控制的功能。

综上所述。通过这样一个优美的设计,把各个功能都他离开来了。如果我们需要增加一种显示方式,只要继承MenuDisplayer或它的一个子类,然后写出我们的方法,而不需要修改系统的其他部分。同样的,如果我们的菜单不准备存放在ServletContext而准备存放在比如Session里了,那么我们也只需要修改control部分和生成部分(即MenuRepository)部分。而不影响Display部分。

OK,对struts-menu的介绍结束了,下一篇文章将是如果使用struts-menu和数据库技术动态生成菜单了。请大家继续关注我的Blog。

分享到:
评论

相关推荐

    Struts-menu源码分析(转贴).rar

    分析源码时,还可以关注Struts-menu是如何优化菜单的生成和缓存的。例如,可能会有预加载菜单结构、缓存策略或者动态加载菜单节点等优化措施。 通过以上分析,我们可以了解到Struts-menu如何在Struts框架下实现...

    struts-menu文件

    Struts-menu是一个专门为Apache Struts框架设计的菜单生成和管理工具。它简化了在Web应用中创建动态、可配置的导航菜单的过程。Struts-menu提供了一种声明式的方式来定义菜单结构,使得开发者无需编写大量的HTML和...

    Struts-Menu实例教程

    Struts-Menu是一个基于Apache Struts框架的菜单生成库,它可以帮助开发者轻松地在Web应用中创建动态、可配置的菜单结构。这个实例教程将深入介绍如何使用Struts-Menu来构建用户界面的导航系统。 1. **Struts框架...

    struts-menu-2.4.1.jar

    struts-menu-2.4.1.jar struts-menu-2.4.1.jar

    使用Struts-menu轻松实现网页菜单.doc

    ### 使用Struts-menu实现网页菜单的关键知识点 #### Struts-menu简介 Struts-menu是一个基于Apache Struts框架的扩展,用于简化网页菜单的创建与管理。它不仅提供了多种样式的菜单,如下拉菜单(`DropDownMenu`)、...

    struts-menu集成struts2.3.1例子以及注意的地方

    这个例子主要关注的是如何将`struts-menu`与Struts2.3.1进行集成,以便创建一个用户友好的菜单系统。 `struts-menu`是一个开源项目,用于生成基于JSP的动态菜单。它支持多种格式的数据源,如XML、数据库等,可以...

    struts-xwork-core源码

    在深入探讨Struts-xwork-core源码之前,我们需要了解一些基本概念。Struts2是一个基于MVC(Model-View-Controller)设计模式的Java Web应用框架,它的目标是简化开发并提供更强大的控制层。XWork则是Struts2的基础,...

    struts-menu 人门 demo

    Struts-menu入门Demo是一个针对Java Web开发人员的实践教程,主要使用了Struts、Hibernate和MySQL这三大核心技术。这个入门示例提供了一个完整的项目结构,包括数据库文件,使得开发者能够直接运行并理解其工作原理...

    struts-menu 2.4.3

    struts-menu 2.4.3.jar

    Struts-menu源码分析

    北京好的代码读起来让人如饮醍醐,读完以后... 一段广告完毕,下面就为大家分析一下struts-menu的源码,作为送给大家的圣诞礼物吧。Struts-Menu也来自一位大师的作品,MattRaible。有很多优秀的作品,比如使用stru

    struts-menu例子

    Struts-menu是一个基于Java的开源项目,用于在Struts框架中生成动态菜单。这个例子是Struts-menu的一个实例,提供给需要了解或使用Struts-menu的开发者参考。下面将详细介绍Struts-menu及其在实际开发中的应用,以及...

    Struts-menu例子加视频

    Struts-menu是一个基于Java EE平台的开源框架,用于构建企业级Web应用程序。它与Apache Struts框架相结合,提供了一种组织和展示应用菜单结构的方法。这个"Struts-menu例子加视频"的学习资源显然旨在帮助开发者更好...

    项目开发过程中使用Struts-menu

    Struts-menu是一个开源项目,主要用于在基于Struts的Web应用程序中生成动态菜单。它提供了多种显示样式,并且可以根据用户权限进行动态控制。以下是对Struts-menu集成和使用的详细说明: 首先,集成Struts-menu需要...

    struts-menu-2.4.jar

    struts-menu-2.4.jar,构建强大菜单

    struts-menu-2.0

    Struts-menu-2.0是基于Apache Struts框架的一个扩展组件,主要用于构建Web应用程序的菜单系统。这个组件的出现,极大地简化了在Struts应用中创建动态、可配置的菜单的过程,使得开发者能够更加专注于业务逻辑,而...

    struts-menu实例

    Struts-menu是一个专门为Apache Struts框架设计的菜单生成库,它可以帮助开发者在Web应用程序中创建动态、层次化的菜单结构。这个实例提供了三种不同的菜单实现方式,使得开发者可以根据项目需求选择最适合的方法。 ...

    struts2-2.5源码包含jar包

    struts2-2.5源码包含jar包struts2-2.5源码包含jar包struts2-2.5源码包含jar包struts2-2.5源码包含jar包struts2-2.5源码包含jar包struts2-2.5源码包含jar包struts2-2.5源码包含jar包struts2-2.5源码包含jar包struts2-...

    struts-menu

    struts-menu

    struts-menu权限按钮(项目例子)

    Struts-menu是一个专门为Struts框架设计的权限管理组件,它为Web应用提供了灵活且强大的菜单构建和权限控制功能。在实际的项目开发中,权限控制是必不可少的一部分,它确保了不同用户只能访问他们被授权的功能和数据...

Global site tag (gtag.js) - Google Analytics