Struts-Menu是一组从基于XML的配置文件中生成多种样式的菜单的JSP Tags,并且可以结合API开发通过数据库生成的动态菜单。Struts-Menu支持国际化和多种权限控制。
二、运行环境
Windows 2000 Professional
JDK 1.4.2_03
Eclipse 3.1
Tomcat 5.0.28
Tomcat Plugin 3.1Beta
Struts 1.2.7
Commons-Lang 2.1
Commons-Collections 3.1
Struts-Menu 2.3
MySQL 4.1.10a-nt
三、下载与安装
1:从http://java.sun.com下载J2SDK,当前1.4.x系列的最新版本为1.4.2_08
2:从http://www.eclipse.org下载Eclipse,当前最新版本为3.1正式版
3:从http://jakarta.apache.org/tomcat下载Tomcat,当前5.x系列的最新版本为5.0.28
4:从http://www.sysdeo.com/eclipse/tomcatplugin下载Eclipse的Tomcat插件,对应Eclipse3.1x的最新版本为3.1Beta
5:从http://struts.apache.org下载Struts,当前最新版本为1.2.7
6:从http://jakarta.apache.org/commons/下载Commons-Lang,当前最新版本为2.1,下载Commons-Collections,当前最新版本为3.1
7:从http://struts-menu.sourceforge.net下载Struts Menu,当前最新版本为2.3
8:从http://www.mysql.com下载MySQL数据库,4.x系列的最新版本是4.1.12a
9:MySQL、JDK、Eclipse、Tomcat和TomcatPlugin的安装及配置请参考相关资料
四、运行示例程序
1:安装好Tomcat后,解压缩struts-menu-2.3.zip,将struts-menu.war释放到Tomcat安装目录下的webapps下,运行Tomcat
2:在地址栏输入http://localhost:8080/struts-menu
五、安装与配置
1:在Eclipse中新建Tomcat项目,Context为/mymenu,Subdirectory为/web
2:在项目目录下面新建lib目录和web目录及WEB-INF,在web/WEB-INF目录下新建web.xml,内容如下:
<! DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
< web-app >
< display-name > My Example Application -- Vinton Lee </ display-name >
<!-- ============= The Struts ActionServlet Configuration ============= -->
< servlet >
< servlet-name > action </ servlet-name >
< servlet-class > org.apache.struts.action.ActionServlet </ servlet-class >
< init-param >
< param-name > config </ param-name >
< param-value > /WEB-INF/struts-config.xml </ param-value >
</ init-param >
< load-on-startup > 1 </ load-on-startup >
</ servlet >
<!-- ================================================================== -->
<!-- ============= The Struts Action Servlet Mapping ================== -->
< servlet-mapping >
< servlet-name > action </ servlet-name >
< url-pattern > *.do </ url-pattern >
</ servlet-mapping >
<!-- ================================================================== -->
<!-- The Welcome File List -->
< welcome-file-list >
< welcome-file > index.jsp </ welcome-file >
</ welcome-file-list >
<!-- =============== The Struts Taglib Definition ===================== -->
< taglib >
< taglib-uri > struts-bean </ taglib-uri >
< taglib-location > /WEB-INF/struts-bean.tld </ taglib-location >
</ taglib >
< taglib >
< taglib-uri > struts-html </ taglib-uri >
< taglib-location > /WEB-INF/struts-html.tld </ taglib-location >
</ taglib >
< taglib >
< taglib-uri > struts-logic </ taglib-uri >
< taglib-location > /WEB-INF/struts-logic.tld </ taglib-location >
</ taglib >
< taglib >
< taglib-uri > struts-nested </ taglib-uri >
< taglib-location > /WEB-INF/struts-nested.tld </ taglib-location >
</ taglib >
< taglib >
< taglib-uri > struts-tiles </ taglib-uri >
< taglib-location > /WEB-INF/struts-tiles.tld </ taglib-location >
</ taglib >
<!-- ================================================================== -->
<!-- ============= The Struts-Menu Taglib Definition ================== -->
< taglib >
< taglib-uri > struts-menu </ taglib-uri >
< taglib-location > /WEB-INF/struts-menu.tld </ taglib-location >
</ taglib >
<!-- ================================================================== -->
</ web-app >
<! DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://struts.apache.org/dtds/struts-config_1_2.dtd" >
< struts-config >
<!-- ========== Data Source Configuration =============================== -->
< data-sources />
<!-- ========== Form Bean Definitions =================================== -->
< form-beans />
<!-- ========== Global Exception Definitions ============================ -->
< global-exceptions />
<!-- ========== Global Forward Definitions ============================== -->
< global-forwards />
<!-- ========== Action Mapping Definitions ============================== -->
< action-mappings />
<!-- ========== Controller Configuration ================================ -->
<!-- ========== Message Resources Definitions =========================== -->
< message-resources parameter ="application" />
<!-- ========== Plug Ins Configuration ================================== -->
< plug-in className ="net.sf.navigator.menu.MenuPlugIn" >
< set-property property ="menuConfig" value ="/WEB-INF/menu-config.xml" />
</ plug-in >
</ struts-config >
< MenuConfig >
< Displayers >
< Displayer name ="DropDown"
type ="net.sf.navigator.displayer.DropDownMenuDisplayer" />
< Displayer name ="Simple"
type ="net.sf.navigator.displayer.SimpleMenuDisplayer" />
< Displayer name ="CoolMenu"
type ="net.sf.navigator.displayer.CoolMenuDisplayer" />
< Displayer name ="CoolMenu4"
type ="net.sf.navigator.displayer.CoolMenuDisplayer4" />
< Displayer name ="MenuForm"
type ="net.sf.navigator.example.PermissionsFormMenuDisplayer" />
< Displayer name ="ListMenu"
type ="net.sf.navigator.displayer.ListMenuDisplayer" />
< Displayer name ="TabbedMenu"
type ="net.sf.navigator.displayer.TabbedMenuDisplayer" />
< Displayer name ="Velocity"
type ="net.sf.navigator.displayer.VelocityMenuDisplayer" />
</ Displayers >
< Menus >
< Menu name ="DoorSite" title ="DoorSite" description ="Some famous doorsite" width ="50" >
< Item name ="Yahoo" title ="Yahoo" >
< Item name ="YahooIndex" title ="Yahoo Index" location ="http://www.yahoo.com.cn" />
< Item name ="YahooMail" title ="Yahoo Mail" location ="http://cn.mail.yahoo.com" />
</ Item >
< Item name ="Sohu" title ="Sohu" location ="http://www.sohu.com" />
< Item name ="Sina" title ="Sina" location ="http://www.sina.com.cn" />
</ Menu >
</ Menus >
</ MenuConfig >
6:你的目录结构应该类似如下:
%PROJECT_HOME%\lib
%PROJECT_HOME%\lib\antlr.jar
%PROJECT_HOME%\lib\commons-beanutils.jar
%PROJECT_HOME%\lib\commons-digester.jar
%PROJECT_HOME%\lib\commons-fileupload.jar
%PROJECT_HOME%\lib\commons-lang- 2.1 .jar
%PROJECT_HOME%\lib\commons-logging.jar
%PROJECT_HOME%\lib\commons-validator.jar
%PROJECT_HOME%\lib\jakarta-oro.jar
%PROJECT_HOME%\lib\jstl- 1.0.6 .jar
%PROJECT_HOME%\lib\standard- 1.0.6 .jar
%PROJECT_HOME%\lib\struts.jar
%PROJECT_HOME%\lib\struts-menu- 2.3 .jar
%PROJECT_HOME%\src
%PROJECT_HOME%\src\log4j.properties
%PROJECT_HOME%\src\application.properties
%PROJECT_HOME%\src\application_zh_CN.properties
%PROJECT_HOME%\web
%PROJECT_HOME%\web\images\
%PROJECT_HOME%\web\scripts\
%PROJECT_HOME%\web\styles\
%PROJECT_HOME%\web\WEB-INF
%PROJECT_HOME%\web\WEB-INF\menu-config.xml
%PROJECT_HOME%\web\WEB-INF\struts-bean.tld
%PROJECT_HOME%\web\WEB-INF\struts-config.xml
%PROJECT_HOME%\web\WEB-INF\struts-html.tld
%PROJECT_HOME%\web\WEB-INF\struts-logic.tld
%PROJECT_HOME%\web\WEB-INF\struts-menu.tld
%PROJECT_HOME%\web\WEB-INF\struts-menu-el.tld
%PROJECT_HOME%\web\WEB-INF\struts-nested.tld
%PROJECT_HOME%\web\WEB-INF\struts-tiles.tld
%PROJECT_HOME%\web\WEB-INF\web.xml
六、实战Struts-Menu
1:使用配置文件实现静态菜单
新建JSP文件web/static-menu.jsp,内容如下:
<% @ taglib uri = " struts-menu " prefix = " menu " %>
< menu:useMenuDisplayer name ="ListMenu"
bundle ="org.apache.struts.action.MESSAGE" >
< menu:displayMenu name ="DoorSite" />
</ menu:useMenuDisplayer >
2:实现中文化
> 在src\application_zh_CN.properties中增加下面的内容,Unicode可以通过JDK自带的native2ascii工具得到:
menu.DoorSite = \u95e8\u6237\u7f51\u7ad9
#雅虎
menu.Yahoo = \u96c5\u864e
#雅虎首页
menu.YahooIndex = \u96c5\u864e\u9996\u9875
#雅虎邮件
menu.YahooMail = \u96c5\u864e\u90ae\u4ef6
#搜狐
menu.Sohu = \u641c\u72d0
#新浪
menu.Sina = \u65b0\u6d6a
menu.DoorSite = DoorSite
#雅虎
menu.Yahoo = Yahoo
#雅虎首页
menu.YahooIndex = Yahoo Index
#雅虎邮件
menu.YahooMail = Yahoo Mail
#搜狐
menu.Sohu = Sohu
#新浪
menu.Sina = Sina
< Menu name ="DoorSite" title ="menu.DoorSite" description ="Some famous doorsite" width ="50" >
< Item name ="Yahoo" title ="menu.Yahoo" >
< Item name ="YahooIndex" title ="menu.YahooIndex" location ="http://www.yahoo.com.cn" />
< Item name ="YahooMail" title ="menu.YahooMail" location ="http://cn.mail.yahoo.com" />
</ Item >
< Item name ="Sohu" title ="menu.Sohu" location ="http://www.sohu.com" />
< Item name ="Sina" title ="menu.Sina" location ="http://www.sina.com.cn" />
</ Menu >
</ Menus >
3:结合数据库实现动态菜单
> 新建数据myexamples:
CREATE DATABASE myexamples
> 新建数据库表menu_item:
id BIGINT not null ,
parent_name VARCHAR( 30 ) ,
name VARCHAR( 30 ) ,
title VARCHAR( 30 ) ,
titleCN VARCHAR( 30 ) ,
description VARCHAR( 50 ) ,
location VARCHAR( 255 ) ,
target VARCHAR( 10 ) ,
onclick VARCHAR( 100 ) ,
onmouseover VARCHAR( 100 ) ,
onmouseout VARCHAR( 100 ) ,
image VARCHAR( 50 ) ,
altImage VARCHAR( 30 ) ,
tooltip VARCHAR( 100 ) ,
roles VARCHAR( 100 ) ,
page VARCHAR( 255 ) ,
width VARCHAR( 5 ) ,
height VARCHAR( 5 ) ,
forward VARCHAR( 50 ) ,
action VARCHAR( 50 ) ,
primary key (id)
)
> 插入测试数据:
(id , parent_name , name , title , titleCN , location)
VALUES
( 1 , null , 'DatabaseMenu' , 'Database Menu' , '数据库动态菜单' , null) ,
( 2 , 'DatabaseMenu' , 'Yahoo' , 'Yahoo Mail' , '雅虎邮件' , 'http://mail.yahoo.com') ,
( 3 , 'DatabaseMenu' , 'JavaBlogs' , 'JavaBlogs' , 'Java博客' , 'http://javablogs.com') ,
( 4 , null , 'StandaloneMenu' , 'Standalone Menu' , '独立的菜单' , 'http://www.sohu.com')
> 新建一个Struts的Action,代码如下:
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.ListResourceBundle;
import java.util.Map;
import com.mysql.jdbc.Driver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import net.sf.navigator.menu.MenuComponent;
import net.sf.navigator.menu.MenuRepository;
import net.sf.navigator.menu.PermissionsAdapter;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class MenuAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
buildMenuRepository(request);
buildMenuResourceBundle(request);
buildMenuPermissions(request);
return (mapping.findForward( " success " ));
}
/**
* 创建菜单数据结构
*
* @param request
*/
private void buildMenuRepository(HttpServletRequest request) {
MenuRepository repository = new MenuRepository();
// Get the repository from the application scope - and copy the
// DisplayerMappings from it.
MenuRepository defaultRepository = (MenuRepository) request
.getSession().getServletContext().getAttribute(
MenuRepository.MENU_REPOSITORY_KEY);
repository.setDisplayers(defaultRepository.getDisplayers());
Map[] menus = getMenuComponents();
for ( int i = 0 ; i < menus.length; i ++ ) {
MenuComponent mc = new MenuComponent();
Map row = menus[i];
String name = (String) row.get( " name " );
mc.setName(name);
String parent = (String) row.get( " parent_name " );
System.out.println(name + " , parent is: " + parent);
if (parent != null ) {
MenuComponent parentMenu = repository.getMenu(parent);
if (parentMenu == null ) {
System.out.println( " parentMenu ' " + parent + " ' doesn't exist! " );
// create a temporary parentMenu
parentMenu = new MenuComponent();
parentMenu.setName(parent);
repository.addMenu(parentMenu);
}
mc.setParent(parentMenu);
}
String title = (String) row.get( " title " );
mc.setTitle(title);
String location = (String) row.get( " location " );
mc.setLocation(location);
String description = (String) row.get( " description " );
mc.setDescription(description);
repository.addMenu(mc);
}
request.setAttribute( " examplesRepository " , repository);
}
/**
* 从数据库中读取菜单配置信息
*
* @return
*/
private Map[] getMenuComponents() {
ArrayList list = new ArrayList();
Connection conn = null ;
PreparedStatement pstmt = null ;
ResultSet rest = null ;
String sql = " select name,parent_name,title,location,description from menu_item order by id " ;
try {
Class.forName( " com.mysql.jdbc.Driver " ).newInstance();
conn = DriverManager.getConnection( " jdbc:mysql://localhost/myexamples?user=root&password=mywangya&useUnicode=true&characterEncoding=UTF-8 " );
pstmt = conn.prepareStatement(sql);
rest = pstmt.executeQuery();
while (rest.next()) {
int i = 1 ;
HashMap map = new HashMap();
map.put( " name " , rest.getString(i ++ ));
map.put( " parent_name " , rest.getString(i ++ ));
map.put( " title " , rest.getString(i ++ ));
map.put( " location " , rest.getString(i ++ ));
map.put( " description " , rest.getString(i ++ ));
list.add(map);
}
} catch (SQLException ex) {
ex.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if ( null != rest) rest.close();
if ( null != pstmt) pstmt.close();
if ( null != conn) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return (Map[]) list.toArray( new HashMap[ 0 ]);
}
/**
* 构造菜单权限
*
* @param request
*/
private void buildMenuPermissions(HttpServletRequest request) {
PermissionsAdapter permession = new PermissionsAdapter() {
public boolean isAllowed(MenuComponent menu) {
// 名称等于StandaloneMenu的菜单不显示
return ! " StandaloneMenu " .equalsIgnoreCase(menu.getName());
}
};
request.setAttribute( " examplesPermession " , permession);
}
/**
* 构造菜单显示标题
*
* @param request
*/
private void buildMenuResourceBundle(HttpServletRequest request) {
MenuResourceBundle resourceBundle = new MenuResourceBundle();
request.setAttribute( " examplesBundle " , resourceBundle);
}
/**
* MenuResourceBundle树状菜单国际语言显示
*
* @author wenbin.zhang
*
*/
class MenuResourceBundle extends ListResourceBundle {
private ArrayList list = new ArrayList();
public MenuResourceBundle() {
Connection conn = null ;
PreparedStatement pstmt = null ;
ResultSet rest = null ;
String sql = " select title,titleCN from menu_item order by id " ;
try {
Class.forName( " com.mysql.jdbc.Driver " ).newInstance();
conn = DriverManager.getConnection( " jdbc:mysql://localhost/myexamples?user=root&password=mywangya&useUnicode=true&characterEncoding=UTF-8 " );
pstmt = conn.prepareStatement(sql);
rest = pstmt.executeQuery();
while (rest.next()) {
int i = 1 ;
String[] message = new String[ 2 ];
message[ 0 ] = rest.getString(i ++ );
try {
message[ 1 ] = new String(rest.getString(i ++ ).getBytes( " latin1 " ), " gbk " );
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (message[ 0 ] != null && message[ 1 ] != null ) {
list.add(message);
}
}
} catch (SQLException ex) {
ex.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if ( null != rest) rest.close();
if ( null != pstmt) pstmt.close();
if ( null != conn) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public Object[][] getContents() {
return (String[][]) list.toArray( new String[ 0 ][ 0 ]);
}
}
}
< action path ="/menuAction" type ="cn.appex.menu.MenuAction" >
< forward name ="success" path ="/struts-menu/dynamic-menu.jsp" />
</ action >
</ action-mappings >
<% @ taglib uri="struts-menu" prefix="menu" %>
< p > 没有使用Bundle和权限控制:
< menu:useMenuDisplayer name ="ListMenu" repository ="examplesRepository" >
< menu:displayMenu name ="DatabaseMenu" />
< menu:displayMenu name ="StandaloneMenu" />
</ menu:useMenuDisplayer ></ p >
< p > 使用Bundle, 没有使用权限控制:
< menu:useMenuDisplayer name ="ListMenu" bundle ="examplesBundle" repository ="examplesRepository" >
< menu:displayMenu name ="DatabaseMenu" />
< menu:displayMenu name ="StandaloneMenu" />
</ menu:useMenuDisplayer ></ p >
< p > 使用Bundle和权限控制:
< menu:useMenuDisplayer name ="ListMenu" permissions ="examplesPermession" bundle ="examplesBundle" repository ="examplesRepository" >
< menu:displayMenu name ="DatabaseMenu" />
< menu:displayMenu name ="StandaloneMenu" />
</ menu:useMenuDisplayer ></ p >
七、关于Struts-Menu配置文件和API的使用方法,请参考附录中的官方网站
附:
Struts-Menu官方网站:http://struts-menu.sourceforge.net
数据库驱动的动态菜单:http://demo.raibledesigns.com/struts-menu/dynamicMenu.jsp