SingletonActionListener.java
作用:用于返回ActionListener的唯一实例,自身实现了ActionListener接口。抽象方法通过Java反射机制,执行事件处理函数(目标函数)。
package com.csufox.listener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Method;
import java.util.HashMap;
import org.w3c.dom.Element;
public class SingletonActionListener implements ActionListener{
private static HashMap<Object,Element> eventMap = new HashMap<Object,Element>();;
private static HashMap<String,Object> classMap = new HashMap<String,Object>();;
private static SingletonActionListener singletonActionListener = new SingletonActionListener(); //单态模式的ActionListener,只有一个实例在内存
public static SingletonActionListener getInstance(Object bean,Object component,Element eventNode){
String classPath = eventNode.getAttribute("class");
if(classPath==""){ //若event节点中class属性缺省,则默认事件执行方法在原容器中
String className = bean.getClass().getName();
eventNode.setAttribute("class", className);
classMap.put(className, bean); //classMap用于保证执行器的单态,节省内存
}
eventMap.put(component, eventNode);
return singletonActionListener;
}
public void actionPerformed(ActionEvent event) {
Object key = event.getSource();
Object execute = null;
if(eventMap.containsKey(key)){
Element eventNode = eventMap.get(key);
String classPath = eventNode.getAttribute("class");
try {
if(classMap.containsKey(classPath)){
execute = classMap.get(classPath); //返回事件执行器
}
else{
execute = Class.forName(classPath).newInstance(); //实例化事件执行器
classMap.put(classPath, execute); //置入classMap,保证单态
}
String targetMethod = eventNode.getAttribute("method"); //反射
Class<?> cla = execute.getClass();
Method m = cla.getMethod(targetMethod,new Class[]{});
m.invoke(execute, new Object[]{}); } catch (Exception e) {
e.printStackTrace();
}
}
}
}
Session.java
作用:模拟B/S模式Web容器中的Session,用于传递数据,使事件触发和事件执行分离成为可能。
package com.csufox.util;
import java.util.HashMap;
public class Session {
private static HashMap<String,Object> hm = new HashMap<String,Object>();
public static void setAttribute(String name,Object obj){ //静态方法,添加属性
hm.put(name, obj);
}
public static Object getAttribute(String name){ //静态方法,得到属性
Object obj = hm.get(name);
return obj;
}
}
至此,框架的代码初步完成。
测试
采用该框架,继续实现输入数字计算平方的简单图形界面,看看会发生什么奇迹?
新代码如下(注意代码中仅有组件(成员变量)的set、get方法,没有任何注册监听器,实现抽象方法等操作。全部在框架底层实现):
package demo;
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.TextField;
import com.csufox.util.Session;
import com.csufox.util.XMLReader;
public class FrameDemo2 extends Frame{
private TextField input = new TextField(10);
private Button calculate = new Button("求平方");
private TextField show = new TextField(10);
public TextField getInput() {
return input;
}
public void setInput(TextField input) {
this.input = input;
}
public Button getCalculate() {
return calculate;
}
public void setCalculate(Button calculate) {
this.calculate = calculate;
}
public TextField getShow() {
return show;
}
public void setShow(TextField show) {
this.show = show;
}
public FrameDemo2(){
show.setEditable(false);
this.setLayout(new FlowLayout());
this.add(input);
this.add(calculate);
this.add(show);
this.setSize(300,80);
this.setTitle("FrameDemo2");
this.setVisible(true);
}
public static void main(String args[]){
XMLReader xr = new XMLReader("event-config.xml");
FrameDemo2 frame = (FrameDemo2)xr.getBean("frame");
Session.setAttribute("frame", frame);
}
}
在 event-config.xml 配置文件中配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="frame" class="demo.FrameDemo2">
<component name="calculate">
<event type="ActionListener" class="event.OnClick" method="onClick"></event>
</component>
</bean>
</beans>
在event包的OnClick类中定义onClick方法(实现了事件执行器的远程调用):
package event;
import java.awt.TextField;
import com.csufox.util.Session;
import demo.FrameDemo2;
public class OnClick {
public void onClick(){
FrameDemo2 frame = (FrameDemo2)Session.getAttribute("frame");
TextField input = frame.getInput();
TextField show = frame.getShow();
String numStr = input.getText();
double num = Double.parseDouble(numStr);
String result = String.valueOf(num * num);
show.setText(result);
}
}
运行后的结果如下:
事件得到监听,说明框架能够正常工作,测试成功!
另:
如果在event-config.xml文件中event节点的class属性空缺,则默认执行器的类路径在原容器FrameDemo2内。
在FrameDemo2中定义方法onClick(), 经过测试,程序同样正常运行。
从运行结果来看,该框架相比传统的Java事件机制有如下的优点:
1. 代码中仅需要写get,set方法,可由IDE(如MyEclipse)自动生成,没有注册监听器,实现接口等代码,减少了代码编写量。
2. 事件监听器和事件执行器均在底层实现了单态模式,仅有一个实例运行,节省内存消耗(如同Struts中的Action)。
3. 实现了真正意义上的事件触发和事件执行的分离,通过Session来传递相关组件/容器的引用,进而改变状态。
通过这种解耦,提高了维护性。
4. event-config.xml来配置事件信息,实现了组件的热可插拔,比如将来需要更换事件执行器或事件监听器类型,只需在配置文件中重新配置即可,无须修改源代码。配置文件呈树结构,结构清晰,易于维护。
注:
框架XMLEventHandler只实现了ActionListener(SingletonActionListener.java),没有其它监听器接口的实现类,并不完善。但是整个实现的结构已经形成。
框架XMLEventHandler本可以省略get,set方法,而采用Java反射机制,用Field来得到组件(如Button),减少代码量。但是由于Field得到组件需将组件定义为公有变量,考虑到一般成员变量均定义为私有的习惯,放弃了这种实现方法。
–EOF–