博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
jmeter系列一(jmeter界面相关无TestBean)
阅读量:5012 次
发布时间:2019-06-12

本文共 11624 字,大约阅读时间需要 38 分钟。

这系列文章是自己学习研究jmeter源码后的结果,这篇文章介绍的是jmeter界面以及事件响应相关的东西,写下来分享也利于回头看,

下图是jmeter源码导入neatbeans

涉及到跟gui相关的内容大体都在core里面的gui包下

涉及到的类主要有

AbstractJMeterGuiComponent

GUIFactory

GuiPackage

MainFrame

MenuFactory 动态加载类路径下继承了AbstractJmeterGuiComponent或者实现了TestBean接口的类,他们就是MainFrame中右边的Frame

实现TestBean接口的类会有个BeanInfo于之相对应,这是javabean规范,重点是用到了Introspector

gui.action包下面

Command和各种实现了Command接口的类

ActionRouter 动态加载实现了Command接口的类,通过ActionEvent的name来匹配是哪个类响应MainFrame中左边树的相关事件

gui.tree包下面

MainFrame左边的树,重点是TreeNode存了各种testelement,比如controller、sampler、preprocess、config etc.当运行测试的时候,程序就读取里面的各种testelement元素来执行测试流程

界面涉及相关流程

实例化树

MainFrame实例化的时候,会通过调用makeTree方法传入JMeterTreeModel和JMeterTreeListener构建一个JTree,初始化有两个节点

1 private void initTree(TestElement tp, TestElement wb) { 2         // Insert the test plan node 3         insertNodeInto(new JMeterTreeNode(tp, this), (JMeterTreeNode) getRoot(), 0); 4         // Insert the workbench node 5         insertNodeInto(new JMeterTreeNode(wb, this), (JMeterTreeNode) getRoot(), 1); 6         // Let others know that the tree content has changed. 7         // This should not be necessary, but without it, nodes are not shown when the user 8         // uses the Close menu item 9         nodeStructureChanged((JMeterTreeNode)getRoot());10     }

这是JMeterTreeModel中的方法,这个方法会被JMeterTreeModel构造方法调用

1 public JMeterTreeModel() {2         this(new TestPlanGui().createTestElement(),new WorkBenchGui().createTestElement());3 4     }

TestPlanGui和WorkBeanchGui都是继承了AbstractJMeterGuiComponent的类,这是MainFrame中右边界面元素都要继承的类

里面有通用发放,界面执行流程会调用到

tree响应鼠标右键事件弹出菜单

看JMeterTreeListener类,实现了TreeSelectionListener, MouseListener, KeyListener接口

因此响应鼠标右键的事件就是在这处理的

1 @Override 2     public void mousePressed(MouseEvent e) { 3         // Get the Main Frame. 4         MainFrame mainFrame = GuiPackage.getInstance().getMainFrame(); 5         // Close any Main Menu that is open 6         mainFrame.closeMenu(); 7         int selRow = tree.getRowForLocation(e.getX(), e.getY()); 8         if (tree.getPathForLocation(e.getX(), e.getY()) != null) { 9             log.debug("mouse pressed, updating currentPath");10             currentPath = tree.getPathForLocation(e.getX(), e.getY());11         }12         if (selRow != -1) {13             // updateMainMenu(((JMeterGUIComponent)14             // getCurrentNode().getUserObject()).createPopupMenu());15             if (isRightClick(e)) {16                 if (tree.getSelectionCount() < 2) {17                     tree.setSelectionPath(currentPath);18                 }19                 log.debug("About to display pop-up");20                 displayPopUp(e);//这里21             }22         }23     }
1 private void displayPopUp(MouseEvent e) {2         JPopupMenu pop = getCurrentNode().createPopupMenu();3         GuiPackage.getInstance().displayPopUp(e, pop);4     }

JMeterTreeNode中的createPopupMenu方法

1 public JPopupMenu createPopupMenu() {2         try {3             return GuiPackage.getInstance().getGui(getTestElement()).createPopupMenu();4         } catch (Exception e) {5             log.error("Can't get popup menu for gui", e);6             return null;7         }8     }

最后shift+鼠标来到了JMeterGUIComponent接口,恩好吧,这是IDE

找一个实现了这个接口的类,AbstractControllerGui....

1 @Override2     public JPopupMenu createPopupMenu() {3         return MenuFactory.getDefaultControllerMenu();4     }

看MenuFactory类

有个static块,调用了静态方法initializeMenus

1 List
guiClasses = ClassFinder.findClassesThatExtend(JMeterUtils.getSearchPaths(), new Class[] { 2 JMeterGUIComponent.class, TestBean.class }); 3 Collections.sort(guiClasses); 4 for (String name : guiClasses) { 5 6 /* 7 * JMeterTreeNode and TestBeanGUI are special GUI classes, and 8 * aren't intended to be added to menus 9 *10 * TODO: find a better way of checking this11 */12 if (name.endsWith("JMeterTreeNode") // $NON-NLS-1$13 || name.endsWith("TestBeanGUI")) {
// $NON-NLS-1$14 continue;// Don't try to instantiate these15 }16 17 if (elementsToSkip.contains(name)) { // No point instantiating class18 log.info("Skipping " + name);19 continue;20 }21 22 boolean hideBean = false; // Should the TestBean be hidden?23 24 JMeterGUIComponent item;25 try {26 Class
c = Class.forName(name);27 if (TestBean.class.isAssignableFrom(c)) {28 TestBeanGUI tbgui = new TestBeanGUI(c);29 hideBean = tbgui.isHidden() || (tbgui.isExpert() && !JMeterUtils.isExpertMode());30 item = tbgui;31 } else {32 item = (JMeterGUIComponent) c.newInstance();33 }34 } catch (NoClassDefFoundError e) {35 log.warn("Missing jar? Could not create " + name + ". " + e);36 continue;37 } catch (Throwable e) {38 log.warn("Could not instantiate " + name, e);39 if (e instanceof Error){40 throw (Error) e;41 }42 if (e instanceof RuntimeException){43 if (!(e instanceof HeadlessException)) { // Allow headless testing44 throw (RuntimeException) e;45 }46 }47 continue;48 }

会扫描实现了JMeterGUIComponent的接口或者实现了TestBean接口(上面提到的利用了JavaBean规范,BeanInfo哦)的类,

首先Class.forName他们

对于实现接口TestBean的类,会用TestBeanGUI进行包装(我能说这是装饰模式吗?)

然后调用getMenuCategories方法(实现了JMeterGUIComponent接口都会实现这个方法),

这个方法就是告诉当在currentNode上右键鼠标,针对于这个node会弹出的菜单即所谓的PopupMenu

1 if (categories == null) { 2                     log.debug(name + " participates in no menus."); 3                     continue; 4                 } 5                 if (categories.contains(THREADS)) { 6                     threads.add(new MenuInfo(item, name)); 7                 } 8                 if (categories.contains(FRAGMENTS)) { 9                     fragments.add(new MenuInfo(item, name));10                 }11                 if (categories.contains(TIMERS)) {12                     timers.add(new MenuInfo(item, name));13                 }14 15                 if (categories.contains(POST_PROCESSORS)) {16                     postProcessors.add(new MenuInfo(item, name));17                 }18 19                 if (categories.contains(PRE_PROCESSORS)) {20                     preProcessors.add(new MenuInfo(item, name));21                 }22 23                 if (categories.contains(CONTROLLERS)) {24                     controllers.add(new MenuInfo(item, name));25                 }26 27                 if (categories.contains(SAMPLERS)) {28                     samplers.add(new MenuInfo(item, name));29                 }30 31                 if (categories.contains(NON_TEST_ELEMENTS)) {32                     nonTestElements.add(new MenuInfo(item, name));33                 }34 35                 if (categories.contains(LISTENERS)) {36                     listeners.add(new MenuInfo(item, name));37                 }38 39                 if (categories.contains(CONFIG_ELEMENTS)) {40                     configElements.add(new MenuInfo(item, name));41                 }42                 if (categories.contains(ASSERTIONS)) {43                     assertions.add(new MenuInfo(item, name));44                 }

然后对应TestElement的gui类中的createPopUp方法就会在这里来取他们对应的菜单项目

弹出菜单项的响应鼠标单击事件

看MenuFactory中的各种makeMenus方法,makeMenuItem是最小项即创建每个弹出菜单最后一项内容

1 public static JMenuItem makeMenuItem(String label, String name, String actionCommand) { 2         JMenuItem newMenuChoice = new JMenuItem(label); 3         newMenuChoice.setName(name); 4         newMenuChoice.addActionListener(ActionRouter.getInstance()); 5         if (actionCommand != null) { 6             newMenuChoice.setActionCommand(actionCommand); 7         } 8  9         return newMenuChoice;10     }

newMenuChoice.addActionListener(ActionRouter.getInstance());

这句话设置了每个菜单项响应事件的监听器,已经actioncommand即该菜单项的command名字

定位到ActionRouter类,他是一个单例类,看他的方法populateCommandMap

1 List
listClasses = ClassFinder.findClassesThatExtend( 2 JMeterUtils.getSearchPaths(), // strPathsOrJars - pathnames or jarfiles to search for classes 3 // classNames - required parent class(es) or annotations 4 new Class[] {Class.forName("org.apache.jmeter.gui.action.Command") }, // $NON-NLS-1$ 5 false, // innerClasses - should we include inner classes? 6 null, // contains - classname should contain this string 7 // Ignore the classes which are specific to the reporting tool 8 "org.apache.jmeter.report.gui", // $NON-NLS-1$ // notContains - classname should not contain this string 9 false); // annotations - true if classnames are annotations10 commands = new HashMap
>(listClasses.size());11 if (listClasses.isEmpty()) {12 log.fatalError("!!!!!Uh-oh, didn't find any action handlers!!!!!");13 throw new JMeterError("No action handlers found - check JMeterHome and libraries");14 }15 for (String strClassName : listClasses) {16 Class
commandClass = Class.forName(strClassName);17 Command command = (Command) commandClass.newInstance();18 for (String commandName : command.getActionNames()) {19 Set
commandObjects = commands.get(commandName);20 if (commandObjects == null) {21 commandObjects = new HashSet
();22 commands.put(commandName, commandObjects);23 }24 commandObjects.add(command);25 }26 }

搜集实现了Command接口的各种类,这些类就是响应菜单单击事件的类,他们都在org.apache.jmeter.gui.action包下,当然我们可以实现我们自己的Command,

简单的看其中一个 AboutCommand

1 static { 2         HashSet
commands = new HashSet
(); 3 commands.add(ActionNames.ABOUT); 4 commandSet = Collections.unmodifiableSet(commands); 5 } 6 7 /** 8 * Handle the "about" action by displaying the "About Apache JMeter..." 9 * dialog box. The Dialog Box is NOT modal, because those should be avoided10 * if at all possible.11 */12 @Override13 public void doAction(ActionEvent e) {14 if (e.getActionCommand().equals(ActionNames.ABOUT)) {15 this.about();16 }17 }18 19 /**20 * Provide the list of Action names that are available in this command.21 */22 @Override23 public Set
getActionNames() {24 return AboutCommand.commandSet;25 }

doAction方法处理单击事件的地方

getActionNames方法是ActionRouter用来调用的,

ActionRouter中收集Command的代码

1 for (String commandName : command.getActionNames()) {2                     Set commandObjects = commands.get(commandName);3                     if (commandObjects == null) {4                         commandObjects = new HashSet();5                         commands.put(commandName, commandObjects);6                     }7                     commandObjects.add(command);8                 }

 

转载于:https://www.cnblogs.com/liliqiang/p/4161124.html

你可能感兴趣的文章
shell学习1shell简介
查看>>
Qt 【无法打开 xxxx头文件】
查看>>
JAVA项目将 Oracle 转 MySQL 数据库转换(Hibernate 持久层)
查看>>
三层架构(我的理解及详细分析)
查看>>
Django模板语言相关内容
查看>>
前端开发工程师如何在2013年里提升自己【转】--2016已更新升级很多何去何从?...
查看>>
markdown语法测试集合
查看>>
running and coding
查看>>
实现QQ第三方登录、网站接入
查看>>
HTML CSS 层叠样式表 三
查看>>
Qt pro pri 文件学习1
查看>>
软件工程概论第六周学习进度条
查看>>
[思路]导入导出功能
查看>>
【iOS】UICollectionView自己定义Layout之蜂窝布局
查看>>
golang——(strings包)常用字符串操作函数
查看>>
发布aar到jcenter
查看>>
跨浏览器问题的五种解决方案
查看>>
XPath定位时,使用文本的方法小技巧。
查看>>
安装pandas报错(AttributeError: 'module' object has no attribute 'main')
查看>>
ch02 fundamental definition 01
查看>>