20.3. IoVC的4种类型

20.3.1. 页面绑定

使用IoVC的第一步是为页面绑定一个或多个LiteBean。LiteBean是对JSF中ManagedBean的扩展,在OperaMasks中,LiteBean正是通过@ManagedBean注解来定义的。OperaMasks为LiteBean提供了多种便于对视图进行绑定与控制的注解。在IoVC的语境下,LiteBean是控制行为的发起者。

OperaMasks提供了两种绑定形式,通过WEB-INF/operamasks.xml部署配置文件进行绑定和通过<om:useBean>标签进行绑定。

通过配置WEB-INF/operamasks.xml可以将对业务模型的引用完全从展现层页面中消除,OperaMasks支持在绑定映射中使用模式匹配,从而达到一劳永逸,也不失灵活性。事实上,所有通过Apusic Studio创建的支持OperaMasks的Web模块,其WEB-INF中都包含一个默认的operamasks.xml配置文件,其中包含一条默认的绑定映射规则:

<operamasks-config xmlns="http://www.operamasks.org/IoVC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.operamasks.org/IoVC http://www.operamasks.org/schema/operamasks.xsd "> 
    <view-mapping> 
        <url-pattern>*</url-pattern> 
        <model-bean>#{~view}Bean</model-bean> 
    </view-mapping>
</operamasks-config>

这个默认规则允许用户在完全不关心绑定规则的前提下获得IoVC支持,这是OperaMasks中“约定优于配置”思想的一个体现。当然,OperaMasks也允许用户在operamasks.xml中添加更多绑定规则或对默认绑定规则进行修改。

针对 operamasks.xml 的绑定策略如下:

  • Rule 1:可以在配置文件中以通配符的形式进行定义。如:

    <view-mapping> 
        <url-pattern>*</url-pattern> 
        <model-bean>#{~view}Bean</model-bean> 
    </view-mapping>

    上述配置含义为:使用首字母小写的页面文件名(不包括文件后缀)加上“Bean”作为该页面的绑定LiteBean名称。例如myPage.xhtml页面文件,对应的LiteBean类名为myPageBean。

    如果页面中包含"_",如:my_page.xhtml,则对应的LiteBean名称为:my_pageBean。

    如果页面中包含"-",如:my-page.xhtml,由于"-"是非法的Java identifier,会忽略"-",那么,此时的 LiteBean 名称为:myPageBean。

    如果将上述配置中的 view 改成 View,即首字母大写:

    <view-mapping> 
        <url-pattern>*</url-pattern> 
        <model-bean>#{~View}Bean</model-bean> 
    </view-mapping>

    上述配置含义为:使用首字母大写的页面文件名(不包括文件后缀)加上“Bean”作为该页面的绑定 LiteBean名称。例如myPage.xhtml页面文件,对应的LiteBean类名为MyPageBean。

  • Rule 2:可以在通配符中指定页面文件的目录结构,和LiteBean的前缀。如:

    <view-mapping> 
        <url-pattern>/abc/*</url-pattern> 
        <model-bean>def.#{@view}Bean</model-bean> 
    </view-mapping>

    这就意味着:/abc/myPage.xhtml,对应的 ManagedBean名称为:def.myPageBean。

  • Rule 3:精确绑定规则会覆盖通用绑定规则。

    即:使用通配符定义的通用绑定规则具有最低的优先级,如果一个页面符合使用精确指定LiteBean名称进行的绑定定义,将会覆盖使用通配规则的绑定定义。

  • Rule 4:重复定义的url-pattern或model-bean在不违反Rule 3的前提下,采用添加形式,而不是覆盖。

    也就是说,如果在operamasks.xml对同一个url-pattern或同一个model-bean绑定了多次,则所有绑定都生效,导致某个页面会绑定到多个LiteBean,或者某个LiteBean被绑到不同的页面。但精确绑定规则仍然会覆盖通用绑定规则。

  • Rule 5:在同一条<view-mapping>中,允许存在多个<url-pattern>和多个<model-bean>,这时绑定的结果是其中每个<url-pattern>分别绑定了所有的<model-bean>。如:

    <view-mapping> 
        <url-pattern>/test1.xhtml</url-pattern> 
        <url-pattern>/test2.xhtml</url-pattern> 
        <model-bean>test1Bean</model-bean> 
        <model-bean>test2Bean</model-bean> 
    </view-mapping>

    绑定结果是,test1.xhtml绑定了test1Bean和test2Bean,test2.xhtml也绑定了test1Bean和test2Bean

  • Rule 6:<model-bean>标签支持以下匹配模式:

    #{~view} :此模式会被替换为包含路径的页面名称。即为/test/abc.xhtml进行绑定时,以上模式会被替换为test.abc

    #{~View} :此模式会被替换为包含路径的首字大写页面名称。即为/test/abc.xhtml进行绑定时,以上模式会被替换为test.Abc

    #{view} : 此模式会被替换为不包含路径的页面名。即为/test/abc.xhtml 进行绑定时,以上模式会被替换为abc

    #{View} : 此模式会被替换为不包含路径的首字大写页面名。即为/test/abc.xhtml进行绑定时,以上模式会被替换为Abc

同时需要注意的是:一个页面,通过operamasks.xml中配置的规则,和某个LiteBean进行绑定,此页面必须要单独运行这种绑定关系才起作用。如果此页面没有单独运行,而是被一个<ajax:updater>引入到其它的页面中(frame设置为false),那么,与此页面绑定的LiteBean不会正常工作。

为了方便轻量级用户或某些特殊场景,OperaMasks也支持在页面代码中使用<om:useBean>标签来指定页面绑定的LiteBean。例如:

<om:useBean value="EmployeeBean,demo.CompanyBean" />

通过<om:useBean>将页面和LiteBean进行绑定,与通过operamasks.xml进行页面与LiteBean的绑定,其实质是一样的。

一个使用<om:useBean>的典型场景是,当一个页面使用<ajax:updater>引入了另一个页面时,由于被引入的页面没有独立的ViewId,它必须使用<om:useBean>标签来绑定LiteBean

20.3.2. 动作绑定

动作绑定是指IoVC将LiteBean的一个或多个方法绑定到页面构件的一个JSF Action上。

一个典型的对控制行为进行控制反转的场景是在展现层定义了一个按钮

<w:form> 
    <w:button id="ok"/> 
</w:form>

则在页面绑定的LiteBean中可定义方法

@Action 
public void ok() { 
    //单击按键业务逻辑 
}

注意基于“约定优于配置”原则,若方法名称与构件id名称相同,则无需指定。若方法名称与构件id名称不相同,则需要通过@Action注解的id属性进行绑定。例如:

@Action (id="ok") 
public void okey() { 
    //单击按键业务逻辑 
}

20.3.3. 事件绑定

OperaMasks对JSF的Action进行了扩展,将丰富的客户端构件事件映射为服务器端事件,从而允许用户绑定构件事件,对构件行为进行更为精确的控制。例如

@Action 
public void ok_ondblclick() { 
    //双击按钮业务逻辑 
}

基于“约定优于配置原则”,若方法名为“<构件id>_<事件名称>”则无需在@Action中指定。若方法名不符合此规则,则需要通过@Action注解的id属性与event属性进行绑定。例如:

@Action (id="ok", event="ondblclick") 
public void onOkDoubleClick() { 
    //双击按钮业务逻辑 
}

需要注意的是,在@Action中可以设置immediate属性,默认为false。在IoVC中,注解中的属性取值优先于页面构件上设定的属性取值。因此如果使用了@Action而又需要把immediate设为true,必须在@Action注解上设置。

20.3.4. 数据绑定

将业务数据绑定到构件是基于构件编程的一项基本功能。但在IoVC支持下,数据绑定变得更为自然、方便。一个最基本的数据绑定场景是一个Label加一个TextField:

页面代码片段:

<w:form> 
    <h:outputLabel for="number1"></h:outputLabel> 
    <w:textField id="number1"></w:textField> 
    <h:outputLabel id="number1copy"></h:outputLabel> 
</w:form>

LiteBean代码片段:

@Bind (id="number1") 
int PID; 
@Bind (id="number1copy") 
int PIDCopy; 

@Action 
public void number1_onchange() { 
    PIDCopy = PID; 
}

运行以上代码可获得以下效果(其中文本框中数字为用户输入,文本框外数字为LiteBean运算结果):

可以看出IoVC完成了以下动作:

  • 1.自动为文本框前的outputLabel填上内容,基于“约定优于配置”原则,内容为此outputLabel关联的id并首字大写。

  • 2.根据绑定规则向LiteBean的PID域中注入textField构件内容,并根据绑定类型自动进行了类型转换。

  • 3.根据事件绑定规则调用事件业务方法。(注意textField的onchange事件的触发条件是textField失去焦点且内容发生了变化)

  • 4. 根据绑定规则将模型数据PIDCopy注入到id为number1copy的outputLabel中。