好的代码读起来让人如饮醍醐,读完以后神清气爽。如果你想提高你的编程水平,如果你想提高你的设计能力,如果你也想成为大师,那么就去阅读代码吧。以本人十几年来的编程经验,阅读代码能让你得到的比阅读文章(那怕是大师的文章)得到的更多。优秀而且实用的代码有很多,比如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技术,构建树形菜单。应该说是一个非常实用的技术,极大的方便了广大的开发人员。与此同时,个人认为它的作用还不止于些。比如,同时它也是一个使用
Maven和
velocity的一个很好的例子。
首先,我们去看一下它的效果。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和数据库技术动态生成菜单了。
分享到:
相关推荐
在分析Struts-menu的源码时,我们可以深入理解Struts框架的工作原理、MVC模式的应用以及如何实现自定义标签库。下面将详细讨论相关知识点。 1. **Apache Struts框架**: Apache Struts是一个开源的Java EE Web应用...
- **阅读源码**:分析例子中的XML配置文件,理解菜单结构的定义方式。 - **运行示例**:将例子部署到本地服务器,查看实际效果,了解菜单如何根据配置动态生成。 - **调试与修改**:尝试修改配置文件或Action代码...
标签“源码”表明博客可能深入到StrutsMenu的源代码层面,分析其内部实现机制,这对于开发者理解工具的工作原理和进行二次开发非常有帮助。而“工具”标签则暗示了StrutsMenu是一个实用的开发辅助工具,可以帮助提高...
首先,我们来看《Struts Menu源码分析.doc》这份文档,它可能详细解释了StrutsMenu的内部工作原理和实现机制。源码分析通常会涵盖类结构、主要方法和核心逻辑。例如,StrutsMenu可能会有一个`MenuBuilder`类用于根据...
这是一个基于Struts和Hibernate框架开发的BBS(论坛社区)的源码项目,适用于毕业...通过对源码的阅读和分析,可以学习到如何组织MVC架构,如何使用Hibernate进行数据库操作,以及如何利用Tiles进行页面布局等技能。
通过分析和运行这个项目,你可以更深入地理解Struts1和Tiles的工作原理及使用方法。 总之,Struts1 Tiles提供了一种强大的方式来管理和组织Web应用程序的视图部分,使开发者能够专注于页面组件的设计和复用,而不是...
这个压缩包文件包含的是一份Struts2项目的源码,对于想要深入理解Struts2工作原理或者学习如何使用Struts2的人来说,是一个很好的学习资源。 首先,我们来逐一分析这些文件: 1. **struts-power.gif** - 这可能是...
【描述分析】:“Jsp javascript Struts tree menu attached code”描述指出,这个代码集包含了JSP、JavaScript以及Struts框架用于创建树形菜单的代码。JavaScript通常用于客户端的动态交互,而Struts是Java服务器端...
为了更好地学习这个项目,你需要解压文件并查看源码,理解每个类的作用,查看数据库表结构,以及分析Struts配置文件。这将帮助你深入理解Web应用的开发流程和Struts+MySQL的结合使用。同时,也可以尝试修改和扩展这...
- **上下文菜单 (Context Menu):** 提供针对选定元素的操作。 - **状态栏 (Status Bar):** 显示 IDE 的状态信息。 - **编辑器 (Editor):** 用于编辑源代码的区域。 - **常见概念和操作:** - **项目 ...
1. **menu1.jsp** 和 **menu2.jsp** - 这些可能是网站的导航菜单部分,用来呈现网站的各个页面链接。 2. **view1.jsp**、**view2.jsp**、**view3.jsp** 和 **view4.jsp** - 视图(View)通常在MVC(模型-视图-控制器...
│ │ │ Java程序员认证模拟题及详细分析.doc │ │ │ question.rar │ │ │ test4.doc │ │ │ 模拟题.rar │ │ │ 经典的104-147模拟题.rar │ │ │ │ │ ├─035 │ │ │ 2003.10.5.15.51.43.TestKing%...
│ │ │ Java程序员认证模拟题及详细分析.doc │ │ │ question.rar │ │ │ test4.doc │ │ │ 模拟题.rar │ │ │ 经典的104-147模拟题.rar │ │ │ │ │ ├─035 │ │ │ 2003.10.5.15.51.43.TestKing%...
│ │ │ Java程序员认证模拟题及详细分析.doc │ │ │ question.rar │ │ │ test4.doc │ │ │ 模拟题.rar │ │ │ 经典的104-147模拟题.rar │ │ │ │ │ ├─035 │ │ │ 2003.10.5.15.51.43.TestKing%...
│ │ │ Java程序员认证模拟题及详细分析.doc │ │ │ question.rar │ │ │ test4.doc │ │ │ 模拟题.rar │ │ │ 经典的104-147模拟题.rar │ │ │ │ │ ├─035 │ │ │ 2003.10.5.15.51.43.TestKing%...
│ │ │ Java程序员认证模拟题及详细分析.doc │ │ │ question.rar │ │ │ test4.doc │ │ │ 模拟题.rar │ │ │ 经典的104-147模拟题.rar │ │ │ │ │ ├─035 │ │ │ 2003.10.5.15.51.43.TestKing%...
开发者可能还使用了其他Java框架,如Spring MVC或Struts,这些框架可以帮助简化开发流程,提高代码的可维护性和复用性。 至于压缩包子文件的文件名称列表,"基于jsp的咖啡馆管理系统源码数据库"可能包含了以下组件...
EasyUI包含了一系列常见的前端组件,如表格(datagrid)、面板(panel)、窗口(window)、菜单(menu)、下拉框(combobox)、按钮(button)等。这些组件设计简洁,易于使用,并且支持丰富的定制选项,可以满足...