上一篇博文中介绍了java的反射机制的原理和基本功能,读后肯定对反射机制有一定的了解,本篇博文将以我的项目要求为例子,讲述动态菜单的生成和java反射机制的使用。
任务要求:所有的菜单项都从数据库中取得,从数据库中取得的只是一个字符串,程序区分菜单,和一级菜单,二级菜单的多级菜单,以后若想增加或减少菜单项,只需更改数据库表即可(数据库表自己设计),生成好菜单后,点击菜单项弹出对应的模块。先看下面的具体的需求和操作要求吧!
1、菜单权限设置
1、如果需要对不同的用户采取权限设置,可以建立相关的数据库表,进行设置,程序可以提取数据,自动生成 设置有权限的菜单。(本例子中代码有从权限数据库表中取 得相关用户的菜单权限)
2、菜单可见和不可见。
3、菜单可用和不可用(灰色状态)
2、 自动生成菜单
1、数据库表中所包含的字段有:菜单编号,菜单名字,菜单所对应类名字,父菜单编号,菜单类型。
2、若用户想增加或减少菜单,只需改变数据库表,而程序不需要更改。
3、 多级菜单的生成
1、生成多级菜单只需更改数据库表,在数据库表中填写清楚菜单类型,和子菜单编号,父菜单的编号。
2、多级菜单的寻找方案为递归寻找。
4、 java反射机制动态调用类
1、数据库表中,字段:菜单类名字保存的是相对应的菜单项对应的类名字。
2、点击菜单项,程序能够动态的调用相应的类,生成界面参见图3-1 微易码信息管理系统 – 主界面菜单。
5、数据库表的设计:
1、菜单中有JMen类型,类似于记事本中“文件”、“编辑”、“格式”等等;
2、菜单中有JMenuItem类型,类似于记事本中的文件下的“新建”、“打开”、“保存”等等;
3、数据库表中要存储有菜单项中对应的类名(方便使用反射机制);
4、数据库表中还包含分割线“-”,以区分不同类别的菜单项;
根据要求,数据库以Access为例,我对数据库表的设计是这样的,字段有:
Menu_Id(菜单编号 ,短文本)、Menu_Name(菜单名,短文本)、Class_Name(菜单项对应的类名,短文本)、Father_Id(父菜单的编号、短文本)、Menu_Type(菜单类型、数字)如下图:
注意数据库表的设计,数据库表是怎么设计的,就几乎决定了你的程序的设计思路,因为数据是从数据库中取得的。主菜单的Fatner_Id编号为"0000" ,Menu_Type为0;分割线的Menu_Type为1,工具栏的菜单项Father_Id为"0000",Menu_Type为2(工具栏菜单上表中未出现)。
说了这么多,还是看图清楚,其实就是要完成这样的结果:
下面的这张就是点击菜单项后弹出对应的窗口,
想要看完成这个项目的代码就接着往下看吧,直接上代码了:
连接数据库代码:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 此类供调用者完成对数据库的链接,并对数据进行操作的工作
* @author Administrator
*
*/
public class MECDatabase {
private int databaseType; /*要链接的数据库类型*/
private static final String[] DB_DRIVER = {"oracle.jdbc.OracleDriver", /*链接数据库的驱动*/
"sun.jdbc.odbc.JdbcOdbcDriver",
"com.mysql.jdbc.Driver"};
private static final String[] URL = {"jdbc:oracle:thin:@","jdbc:odbc:","jdbc:mysql://"}; /*链接数据库的URL*/
private static final int ORACLE = 0; /*数据库类型*/
private static final int ACCESS = 1;
private static final int MYSQL = 2;
private Statement stmt;
private Connection con;
private ResultSet rst;
/**
* 带参的构造方法
* @param databaseType 传入要链接的数据库类型(int 类型)
* 0表示链接Oracle数据库
* 1表示链接Access数据库
* 2表示链接MySql数据库
*/
public MECDatabase(int databaseType){
this.databaseType = databaseType;
stmt = null;
con = null;
rst = null;
}
/**
* 获得数据库类型
* @return 返回数据库类型
*/
private int getDatabaseType() {
return databaseType;
}
/**
* 链接不需要IP和端口号的数据库
* @param databaseName 数据源名称
* @throws Exception 链接数据库时遇到的异常
*/
public void connection(String databaseName) throws Exception{
try {
Class.forName(DB_DRIVER[ACCESS]);
con = DriverManager.getConnection(URL[ACCESS]+databaseName);
stmt = con.createStatement();
} catch (ClassNotFoundException e) {
throw new Exception("错误:" + e.getMessage() + "\n" +
"类:MECDatabase\n方法:public void connection() throws Exception\n\n" +
"请咨询微易码开发技术人员,解决这个问题!");
}
}
/**
* 链接数据库
* @param hostIp 主机IP地址
* @param port 端口号
* @param databaseName 数据源名称
* @param userName 用户名
* @param password 密码
* @throws Exception 连接数据库是遇到的异常
*/
public void connection(String hostIp,int port,String databaseName,String userName,String password) throws Exception {
try {
if(this.getDatabaseType() == ORACLE){
Class.forName(DB_DRIVER[ORACLE]);
con = DriverManager.getConnection(URL[ORACLE]+hostIp+":"+port+"/"+databaseName,userName,password);
}else{
Class.forName(DB_DRIVER[MYSQL]);
con = DriverManager.getConnection(URL[MYSQL]+hostIp+":"+port+"/"+databaseName,userName,password);
}
stmt = con.createStatement();
} catch (ClassNotFoundException e1) {
throw new Exception("错误:" + e1.getMessage() + "\n" +
"类:MECDatabase\n方法:public void connection() throws Exception\n\n" +
"请咨询微易码开发技术人员,解决这个问题!");
} catch (SQLException e2) {
throw new Exception("错误:" + e2.getMessage() + "\n" +
"类:MECDatabase\n方法:public void connection() throws Exception\n\n" +
"请咨询微易码开发技术人员,解决这个问题!");
}
}
/**
* 关闭与当前数据库的链接
* @throws Exception 关闭数据库链接时遇到的异常
*/
public void disConnection()throws Exception{
try
{
if(rst != null)
rst.close();
if(stmt != null)
stmt.close();
if(con != null)
con.close();
}catch(SQLException sqle)
{
throw new Exception("错误:" + sqle.getMessage() + "\n" +
"类:MECDatabase\n方法:public void disConnection() throws Exception\n");
}
}
/**
* 执行SQL语句,doSql方法只能完成查询功能
* @param sqlSwing 传入的SQL语句
* @return 返回查询的对象
* @throws Exception 执行SQL语句时遇到的异常
*/
public ResultSet doSql(String sql)throws Exception{
if(stmt != null){
try{
rst = stmt.executeQuery(sql);
}catch(SQLException sqlE){
sqlE.printStackTrace();
throw new Exception("错误:" + sqlE.getMessage() + "\n" +
"类:MECDatabase\n" +
"方法:public ResultSet doSql(String SQL) throws Exception\n");
}
}
return rst;
}
/**
* 执行SQL语句,doSql方法只能完成查询功能,并将查询的内部指针最后指向首位
* @param sqlSwing 传入的SQL语句
* @return 返回查询的对象
* @throws Exception 执行SQL语句时遇到的异常
*/
public ResultSet select(String sql)throws Exception{
if(stmt != null){
try{
//默认的ResultSet对象不可更新,仅有一个向前移动的光标,因此只能迭代它一次,并且只能按从第一行到
//最后一行的顺序进行,可以生成可以滚动的/或可更新的ResultSet对象,以下这句话就是实现该功能,
//不然主程序中result的执行就会出错
stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
rst = stmt.executeQuery(sql);
}catch(SQLException sqlE){
throw new Exception("错误:" + sqlE.getMessage() + "\n" +
"类:MECDatabase\n" +
"方法:public ResultSet select(String SQL) throws Exception\n");
}
}
return rst;
}
/**
* 执行SQL语句,upDate方法可以完成创建,修改,插入,删除等功能
* @param sqlSwing 传入的SQL语句
* @return 返回该操作是否成功
* @throws Exception 执行SQL语句时遇到的异常
*/
public boolean update(String sql)throws Exception{
boolean flag = false;
if(stmt != null){
try{
flag = stmt.execute(sql);
}catch(SQLException sqlE){
throw new Exception("错误:" + sqlE.getMessage() + "\n" +
"类:MECDatabase\n" +
"方法:public ResultSet update(String SQL) throws Exception\n");
}
}
return flag;
}
}
DestopPane类:
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JDesktopPane;
public class DesktopPane extends JDesktopPane
{
private static final long serialVersionUID = 1L;
private ImageIcon ico = new ImageIcon("E:\\java练习\\9353.jpg");
public DesktopPane()
{
this(0,0);
}
public DesktopPane(int width, int heght)
{
getimage(width,heght);
}
public void getimage(int width, int heght)
{
ico.setImage(ico.getImage().getScaledInstance(width,heght,Image.SCALE_DEFAULT));
}
public void paintComponent(Graphics g)
{
g.drawImage(ico.getImage(),0,0,this);
}
}
重头戏代码:
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JToolBar;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;
public class myWindowsDate2
{
/** 创建窗口的基本组件 */
private JFrame jfrmMain;
private Container con;
private JMenuBar jmenbTopBar = new JMenuBar();
private JInternalFrame jInternalFrame;
private DesktopPane DesktopPane ;
private JToolBar toolBar ;
/** 分别用来存储SQL语句,取出的结果集,菜单类名字 */
String SQlString;
ResultSet ResString ;
ResultSet result;
String className;
/** 连接数据库 */
Connection connection ;
/** 存储从数据库中取出的菜单的类型(类型有0、1、2) */
int type;
/** 接收登入界面传入的用户姓名和ID*/
private String userId;
private String userName;
/** 存放已点击的菜单所调用的相对应的类名 */
private ArrayList<String> arrayListWindowCanSee = new ArrayList<String>();
/** 存放可见和可用的的菜单项 */
private ArrayList<String> arrayListDo = new ArrayList<String>();
private ArrayList<String> arrayListIsCanUse = new ArrayList<String>();
/**
* 建立基本的窗体
* @author 王长春
* @param void
* @return void
* @exception void
* */
public void creatJFram()
{
jfrmMain = new JFrame("微易码科技管理信息系统");
con = jfrmMain.getContentPane();
jfrmMain.setVisible(true);
jfrmMain.setExtendedState(jfrmMain.MAXIMIZED_BOTH);
Dimension don =Toolkit.getDefaultToolkit().getScreenSize();
jfrmMain.setSize(don.width,don.height);
jfrmMain.setLocation(0, 0);
DesktopPane = new DesktopPane(jfrmMain.getWidth(),jfrmMain.getHeight());
toolBar = new JToolBar();
con.add("North",toolBar);
con.add(DesktopPane);
jfrmMain.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jfrmMain.setVisible(true);
}
/**
* 从数据库中取得菜单信息
* @author 王长春
* @param void
* @return void
* @exception 连接数据库和执行SQL语句,断开数据库时会出现异常
* */
public void DateFromDateBase()
{
SQlString = "SELECT Menu_Id, Menu_Name, Class_Name, Father_Id, Menu_Type " +
"FROM SYS_INF_MAINMENU ";
MECDatabase date = new MECDatabase(1);
try
{
//连接数据库
date.connection("SYS_MEC_INFOTABLE");
result = date.select(SQlString);
//调用获得权限的方法
getPowerState();
//调用区分区分菜单项的方法
soutJMenu();
//断开数据库
date.disConnection();;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 从数据库中取得用户对应的菜单应有的菜单权限
* @author 王长春
* @param void
* @return void
* @exception 连接数据库和执行SQL语句,断开数据库时会出现异常
* */
public void getPowerState()
{
String sqlString;
String menuIdString;
int state;
//这里为简化操作(应该从登入界面传入的id号区别用户身份,直接从权限表中取得编号为02用户的权限
sqlString = "SELECT MenuId, MenuState FROM SYS_INF_POWER WHERE PostId = '02'";
MECDatabase date1 = new MECDatabase(1);
try
{
date1.connection("SYS_MEC_INFOTABLE");
ResString = date1.select(sqlString);
} catch (Exception e)
{
e.printStackTrace();
}
try
{ //将用户可见和可用的挑出,并存放起来
while (ResString.next())
{
menuIdString = ResString.getString("MenuId");
state = ResString.getInt("MenuState");
if(state != 0)
arrayListDo.add(menuIdString);
if (state == 2 )
arrayListIsCanUse.add(menuIdString);
}
date1.disConnection();
} catch (SQLException e)
{
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/** 区分菜单项所属的属性
* @author 王长春
* @param void
* @return void
* @exception ClassNotFoundException,InstantiationException,
* IllegalAccessException,SQLException
*
* */
public void soutJMenu()
{
int Finger_Move = 1;
try
{
while (result.next())
{
String idString =result.getString("Menu_Id");
String name = result.getString("Menu_Name");
className = result.getString("Class_Name");
String fatherIdString = result.getString("Father_Id");
int type = result.getInt("Menu_Type");
if (0 == type && fatherIdString.equals("0000"))
{//建立主菜单项
JMenu tempJmenu = new JMenu(name);
setJMenuItem(name, idString, fatherIdString, tempJmenu);
}else if(2 == type)
{//建立工具栏
final JButton tempjButton = new JButton(name);
tempjButton.setName(className);
tempjButton.setFont(new Font("隶书",Font.BOLD,20));
toolBar.add(tempjButton);
tempjButton.addActionListener
(
new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
try {//调用反射机制的方法
creatNewWindow(tempjButton.getName(), userId, userName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
);
}
Finger_Move++;
result.absolute(Finger_Move);
}
}catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 建立菜单项并且实现多级菜单的建立
* @author 王长春
* @return Cycle_Index int类型,记录的是result.next的执行次数,为返回result的指向做准备
* @param nameString(String) idstString(String) fathetidString(String) jmenu(String)
* 传入soutJMenu()中的菜单名称,菜单编号,父菜单编号,和建立的JMenu
* @exception ClassNotFoundException, InstantiationException,IllegalAccessException
* */
public int setJMenuItem(String nameString, String idstString, String fathetidString, JMenu jmenu) throws SQLException
{
int Cycle_Index = 0;
boolean isSetSeparator = false;
boolean ifHeadhasItem = false;
if (fathetidString.equals("0000"))
jmenbTopBar.add(jmenu);
while (result.next())
{
Cycle_Index++;
String id = result.getString("Menu_Id");
String Name = result.getString("Menu_Name");
className = result.getString("Class_Name");
String fatherIdString = result.getString("Father_Id");
int type = result.getInt("Menu_Type");
if (fatherIdString.equals(idstString))
{
if (0 == type)
{
if(ifHeadhasItem && isSetSeparator)
{
jmenu.addSeparator();
isSetSeparator = false;
}
if (arrayListDo.contains(id))
{
JMenu tempjmenu = new JMenu(Name);
//递归调用
int count = setJMenuItem(Name, id, fatherIdString, tempjmenu);
jmenu.add(tempjmenu);
//将 result的游标往回指
result.absolute(-count);
}
}
else
{
if(Name.equals("-"))
isSetSeparator = true;
else
{ //添加分隔线,因为根据不同用户的权限不同,出现多根分隔线时只能添加一条,这里保证了
//既不多添加,也不少添加
if(ifHeadhasItem && isSetSeparator)
{
jmenu.addSeparator();
isSetSeparator = false;
ifHeadhasItem = false;
}
if (arrayListDo.contains(id))
{
//建立菜单项
final JMenuItem tempJMenuItem = new JMenuItem(Name);
tempJMenuItem.setName(className);
jmenu.add(tempJMenuItem);
ifHeadhasItem = true;
//判断是否可用
if (arrayListIsCanUse.contains(id))
tempJMenuItem.setEnabled(false);
tempJMenuItem.addActionListener
(
new ActionListener()
{
<span style="white-space:pre"> </span>public void actionPerformed(ActionEvent arg0)
{
<span style="white-space:pre"> </span>String ItemName = tempJMenuItem.getName();
try {
if (!(arrayListWindowCanSee.contains(ItemName)))
{//计算器和万年历窗口要依附在我的主窗口中,两个是特殊处理
if (!ItemName.equals("MECCalender") && !ItemName.equals("Calculator")) { <span style="white-space:pre"> </span> <span style="white-space:pre"> </span>creatNewWindow(ItemName, userId, userName);
arrayListWindowCanSee.add(ItemName);
DealAction(tempJMenuItem);
}
else
creatSpecliWindow(ItemName, userId, userName);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
);
}
}
}
}
}
return Cycle_Index + 1;
}
/**
* 调用用户所点击的菜单项对应的类,弹出各个模块
* @author 王长春
* @param string(String)类名字,id(String)用户编号, iString(String)用户名字
* @return void
* @exception ClassNotFoundException,InstantiationException,IllegalAccessException
* <span style="white-space:pre"> </span> NoSuchMethodException,SecurityException,InvocationTargetException
* */
public void creatNewWindow(String string, String id ,String name) throws ClassNotFoundException, Instantia<span style="white-space:pre"> </span>tionException, IllegalAccessException
{
try {
Class class1 = Class.forName(string);
//要调用的方法的参数类型
Class[] paramType = {String.class, String.class};
//实际传入的参数
Object[] param = {id , name};
//得到参数类型相同的构造方法
Constructor constructor =class1.getConstructor(paramType);
//传入参数,并执行构造方法
Object object = constructor.newInstance(param);
//调用getJframe的方法(各个模块统一留出一致的方法,),传入的参数为空
Method method = class1.getMethod("getJframe", new Class[] {});
//执行该方法
Object mObject =method.invoke(object, new Object[] {});
jInternalFrame = (JInternalFrame) mObject;
//将反回的窗体加入到我的窗体里面,成为我的子窗口
DesktopPane.add(jInternalFrame);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 调用用户所点击的菜单项中工具对应的类,弹出各个模块
* @author 王长春
* @param string(String) 类名字,id(String)用户编号, iString(String)用户姓名
* @return void
* @exception ClassNotFoundException,InstantiationException,IllegalAccessException
* <span style="white-space:pre"> </span>NoSuchMethodException,SecurityException,InvocationTargetException,NoSuchMethodException
* */
public void creatSpecliWindow(String ClassName,String userIdString, String userNameString )
{
try {
Class clas2 = Class.forName(ClassName);
//要调用的方法的参数类型
Class[] patrem = {JFrame.class, String.class, String.class};
//实际要传入的参数
Object[] valueObjects = {jfrmMain, userIdString, userNameString};
//得到参数类型相同的构造方法
Constructor constructor1 = clas2.getConstructor(patrem);
//传入参数,并执行构造方法
Object pObject = constructor1.newInstance(valueObjects);
} catch (ClassNotFoundException e)
{
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 对添加的JInterNalFream窗口进行事件监听
* @author 王长春
* @param tempString(String) 事件监听时的菜单对象
* @return void
* @exception void
*
* */
public void DealAction(final JMenuItem tempItem)
{
jInternalFrame.addInternalFrameListener
(//对子窗体进行事件监听,子窗体没有关闭,再点击该菜单项不能在弹出该窗体
new InternalFrameListener() {
public void internalFrameOpened(InternalFrameEvent e) {
}
public void internalFrameIconified(InternalFrameEvent e) {}
public void internalFrameDeiconified(InternalFrameEvent e) {}
public void internalFrameDeactivated(InternalFrameEvent e) {
}
public void internalFrameClosing(InternalFrameEvent e) {}
public void internalFrameClosed(InternalFrameEvent e){
arrayListWindowCanSee.remove(tempItem.getName());
}
public void internalFrameActivated(InternalFrameEvent e){}
}
);
}
/**初始化界面
* 构造方法
* @author 王长春
* @param void
* @return void
* @exception void
* */
public myWindowsDate2()
{
creatJFram();
jfrmMain.setJMenuBar(jmenbTopBar);
DateFromDateBase();
jfrmMain.setVisible(true);
}
/**
* 带两个参数的构造方法,登入界面传入参数
* @author 王长春
* @param String userId 登入界面传入的用户编号, String userName 登入界面传入的用户姓名
* @return void
* @exception void
* */
public myWindowsDate2(String userId, String userName)
{
this.userId = userId;
this.userName = userName;
creatJFram();
jfrmMain.setJMenuBar(jmenbTopBar);
DateFromDateBase();
jfrmMain.setVisible(true);
}
/**
* 主函数
* @author 王长春
* @param void
* @return void
* @exception void
* */
public static void main(String[] args)
{
new myWindowsDate2();
}
}
看看最后运行的效果吧:
点击菜单项后出现相应的界面,弹出界面的代码就不再这里展示了: