Dorado 7 : 10. 对象监听器(SEFC)

在前面的学习中我们知道只要我们定义好一个View文件,就可以在浏览器中使用它,我们根据界面的显示需要调整View中的各种属性配置。但在某些场景下,我们可能希望在视图的初始化过程中通过自己的Java代码动态的改变视图的内容,例如:动态的添加控件或动态的调整已有控件中的部分属性。这样便于我们结合服务器端的某些外部条件进行页面显示的控制,例如个性化控制,权限控制等。要实现这些功能就有必要了解Dorado提供的对象监听器。

对象监听器并不是专门为View设计的,事实上Dorado中的所有控件都可以提供了一个listener属性,用以支持对象监听器功能。如前面我们看到的JspTemplate.view.xml,在IDE视图中我们就能看到这个属性:

在listener中也可以定义一到多个对象监听器,下面我们根据一个实例了解对象监听器的基本用法,查看如下视图:

在这个例子中我们是通过循环动态的创建了8个Button对象,我们通过源码分析它的实现过程,首先我们打开com.bstek.dorado.sample.DynaView.view.xml:

我们可以看到在这个View中我们定义了一个空的Panel对象,但是页面上我们却看到8个Button对象。我们再看View的listener属性,在这个属性中我们定义了:

spring:dynaView#onViewInit

这是一个服务定位表达式(参考服务定位表达式)对于对象监听器而言,这表示通过Spring中id为dynaView的onViewInit方法监听这个View的创建过程。我们找到com.bstek.dorado.sample.DynaView.java对应的onViewInit方法:

@Component
public class DynaView {
	public void onViewInit(Panel panelButtons) {
		panelButtons.setCaption("此标题是通过视图拦截器设置的");

		for (int i = 1; i <= 8; i++) {
			Button button = new Button();
			button.setCaption("Button " + i);

			AnchorLayoutConstraint layoutConstraint = new AnchorLayoutConstraint();
			layoutConstraint.setAnchorLeft(AnchorMode.previous);
			layoutConstraint.setLeft("5");
			layoutConstraint.setTop("10");
			button.setLayoutConstraint(layoutConstraint);

			button.addClientEventListener(
					"onClick",
					new DefaultClientEvent("dorado.MessageBox.alert('You clicked ' + self.get('caption'));"));

			panelButtons.addChild(button);
		}
	}
}

这当中的@Component方法的声明在实做Ajax章节中我们已经说明过了。主要的目的是将当前的类注册到Spring的上下文当中。由于此处不是一个Ajax调用,因此就不需要@Expose声明。该处方法名可以自由定义,只要保证与listener中的定位表达式一致就可以,参数也将采用智能方法适配规则,可以根据需要定义。例如本例我们希望向panelButtons对象中动态的添加8个Button,我们就可以直接将onViewInit方法写成这种样子:

public void onViewInit(Panel panelButtons) {
}

智能方法适配规则会自动的将当前View中id为panelButtons且类型为Panel的对象注入进来。好,现在我们再来关注方法内部的代码,其实很简单就是用一个for循环创建8个Button对象,并利用Panel的addChild方法添加到Panel中。

注意

不要试图在对象监听器中定义两个名称一样,但是参数不一样的方法,它会导致对象监听器拒绝服务,直接报错!

 

以上就是对象监听器的一个简单用法,更详细的用法请参考如下的内容:

onInit监听和beforeInit监听

对象监听器有两种监听模式,一种是onInit,另一种是beforeInit.对一个控件而言,这表示我们如果要在控件创建的过程中添加监听,即可以在初始化之前监听它,也可以在这个控件初始化之后去监听它。所谓初始化是指dorado读取View中的XML信息初始化到控件对象上,无论是beforeInit还是都是onInit,其中的控件都是已经被实例化好的,差别是在是否进行过初始化。在前面的范例中我们在View中的listener中定义的服务表达式是:

spring:dynaView#onViewInit

应该注意到这个地方并没有使用onInit方法,这是在Listener处理机制中有一种默认约定:一个监听器究竟使用哪一种监听方式,取决于该方法的方法名中是否使用before前缀,例如beforeInit。因此我们要理解onInit和beforeInit并不是具体的方法名,它是监听模式的含义。

另外注意几点:

  • Dorado中绝大多数的可配置对象都listener属性,可以定义一到多个监听器用以监听对象的创建过程。多个对象监听器之间用逗号隔开配置到listener属性中
  • 定义监听器时可以使用自动方法适配。
了解ViewConfig和View

在View配置文件的基本结构中我们没有说明ViewConfig与View的区别,对于一个View配置文件而言,ViewConfig是最顶层的节点。在Dorado的设计中,View节点主要用于定义最终需要输出到浏览器中的可见和不可见的对象。但是对于一个视图而言(不是指View节点)它的生命周期有几种不同的形式,例如我们访问一个视图的时候和这个视图向后台发出一个AJAX请求的时候,对于视图而言这是不同的物理状态,在Dorado内部处理过程中,View节点只会在前一个生命周期中被创建,在AJAX请求中它没有必要创建,而ViewConfig对象则会在所有的生命周期中创建。知道了这个区别之后,我们在使用对象监听器时,我们就要清楚的知道同样一个监听器把它添加在View的listener属性中和添加到ViewConfig的listener属性中,被激活的频率是不一样的,很显然ViewConfig中的listener中触发的频率更高,因此在实际使用中我们就要注意,如果只是希望对View的可见不可见组件进行属性定制,则把这个对象监听器添加到View或View的控件对象上就可以,而如果希望在AJAX请求过程中也触发的逻辑则应该添加到ViewConfig的listener中。

View监听器中控件的自动注入

注意这儿指的是View的监听器,而不是ViewConfig或控件的监听器

对于View的监听器,它还提供了控件自动注入的功能,在提供控件自动注入功能之前,我们在View的监听器中如果要获取View中的其他控件,可能就会采用如下较为笨拙的方法:

public void onInit(View view) {
	Button buttonOK = (Button) view.getComponent("buttonOK");
	buttonOK.setDisabled(true);
	
	Panel panelMain = (Panel) view.getComponent("panelMain");
	panelMain.setCollapsed(true);
}

但是View监听器提供了我们自动注入功能,我们就可以将上面的代码改写的更为灵巧一些:

public void onInit(Button buttonOK, Panel panelMain) {
	buttonOK.setDisabled(true);
	panelMain.setCollapsed(true);
}

上面代码告诉我们,如果我们希望在代码编程中针对某一个控件进行编码,我们就可以直接通过参数定义,只要保证控件的类型正确和控件的ID与View中的ID保持一致就可以。自动注入功能会帮助你找到这个控件对象并在调用onInit方法的时候自动传入。

例如页面上我们有button1, menu1, dataSet1,且我们希望在onInit的时候对它们编程,则我们就可以将上面的代码修改为:

public void onInit(Button button1, Menu menu1, DataSet dataSet1) {
	//TODO
}

当这个方法被触发的时候,自动注入功能就会自动的注入button1、menu1和dataSet1对象。这样你就可以在内部代码中直接针对button1,menu1和dataSet1进行操作了。当然了你也可以只保留一个View参数,再通过View的getComponent方法获得所有的控件。

全局对象拦截器

前面所讲的对象监听器,它一次只能监听一个具体的View或一个控件对象。并且要在View中配置这个监听器,当在某些场景下我们希望做全局监听,便于做统一的设定,如:统一定制某一种控件的特性或通过这个入口做权限设定等。如果采用前面的监听器技术,则有大量的工作要做,我们需要逐个的打开相关的View,然后配置监听器,显然这并不是我们希望的设计模式。
为此,dorado提供了全局对象监听器的配置,全局对象监听器可以监听所有对象的创建过程。并且不需要在View中做任何配置,我们只要直接在dorado-home的app-context.xml中这么配置:

<bean parent="dorado.genericObjectListenerRegister">
	<property name="listener">
		<bean class="com.xxx.xxx.ViewInterceptor" >
			<property name="order" value="3" />
			<property name="pattern" value="*" />
		</bean>
	</property>
</bean>

注意parent需要设置为"dorado.genericObjectListenerRegister"。listener中的class就是自定义的Java类。pattern属性可以用通配符设定,它主要是告诉Listener的过滤规则,用于监控哪些View或哪些控件。
基本的代码结构:

public class ViewInterceptor extends GenericObjectListener<View> {
	@Override
	public boolean beforeInit(View view) throws Exception {
		// your code
	}
	
	@Override
	public void onInit(View view) throws Exception {
		// your code
	}
}

全局监听器必须继承自GenericObjectListener,与对象监听器一样,它支持两种监听模式:beforeInit和onInit。这样这个类能监听所有的View,除了上面介绍的pattern属性使用通配符进行过滤外,我们还可以在Java中通过泛型进行过滤,如上述范例中使用了View通配符,表示监控所有的View,如果改为Button则表示监控所有的Button:

public class ButtonInterceptor extends GenericObjectListener<Button> {
	@Override
	public boolean beforeInit(Button button) throws Exception {
		// your code
	}
	
	@Override
	public void onInit(Button button) throws Exception {
		// your code
	}
}
更多细节参见:http://wiki.bsdn.org/x/m4A0

Attachments: