Expresso控制器的深入使用研究
2005-11-28 09:02
507 查看
Expresso控制器的深入使用研究
修订历史记录
1 前言... 1
2 控制器的输入... 2
3 控制器的输出对象... 3
3.1 Input 3
3.1.1 Input常用表达用标签... 4
3.2 Output 4
3.2.1 Output常用表达用标签... 5
3.3 Transition. 5
3.3.1 Transition常用表达用标签... 5
3.4 Block. 6
3.4.1 Block常用表达用标签... 7
4 描述控制器的有力武器……控制器活动图... 10
5 控制器的状态... 11
5.1 内部式... 11
5.2 外部式... 12
5.3 控制状态的跳转... 13
5.4 选择合适的视图... 13
6 控制器的错误处理... 14
7 总结... 14
8 参考资料... 14
try { setFormCache(); myDBObj.clear();myDBObj =DefaultAutoElement.getAutoControllerElement().parseDBObject(req, myDBObj, ee); clearFormCache(); //...
} catch (DBException de) {}(引自com.jcorporate.expresso.services.controller.dbmaint. AddUpdate.java)
parseDBObject()方法实现核心思想如下:for (Iterator i = myDBObj.getMetaData().getFieldListArray().iterator(); i.hasNext(); ) { String oneFieldName = (String) i.next(); String oneParameterValue = request.getParameter(oneFieldName); myDBObj.set(oneFieldName, oneParameterValue);}具体细节在com.jcorporate.expresso.services.controller.ui. DefaultAutoElement。
可以看出,数据的传入本质上都是使用request.getParameter()方法。
<logic:present name="oneInput" property="@multiValued"> <html:select styleClass="jc-formfield" name="oneInput"/> </logic:present> <logic:notPresent name="oneInput" property="@multiValued"> <html:text styleClass="jc-formfield" name="oneInput"/> </logic:notPresent> </logic:iterate>(引自expresso/jsp/dbmaint/add.jsp)
以上3种对象位于Response的根时,这些标签可直接使用;若位于Block对象中,则需要按照Block组织规则来定位再使用。
Output out1 = new Output(); out1.setName("GOODS_ID"); out1.setAlignment("left"); out1.setContent(one.getField("GOODS_ID")); record.add(out1);
Output out2 = new Output(); out2.setName("GOODS_NAME"); out2.setAlignment("left"); out2.setContent(one.getField("GOODS_NAME")); record.add(out2);
// Add an transition Transition infoTransition = new Transition("Get detail", getClass().getName()); infoTransition.setName("detail"); infoTransition.addParam("state", "showDetail"); infoTransition.addParam("goods_id", one.getField("GOODS_NAME")); record.add(infoTransition); }
myResponse.setStyle("list");
} catch (Exception ex) { throw new ControllerException(ex.getMessage()); }}
上例我们使用了2种意思的Block对象:表示表概念的名字为table的Block对象和表示记录概念的名字为recordX的Block对象,这样Input、Output、Transition对象便扮演了字段概念。
<expresso:ElementCollection type="block"> <expresso:ElementIterator>
<tr> <td bgcolor="#E0FFE0" width="15%">***</td>
<expresso:ElementCollection type="output"> <expresso:ElementIterator> <expresso:OutputTag name="xxx"> <td><expresso:ContentTag /> </td> </expresso:OutputTag> </expresso:ElementIterator> </expresso:ElementCollection>
<expresso:ElementCollection type="transition"> <expresso:ElementIterator> <form action="<%= contextPath %>/Demo.do?cmd=button" method="GET"> <td><expresso:TransitionTag name=" detail " /></td> </form> </expresso:ElementIterator> </expresso:ElementCollection> </tr>
</expresso:ElementIterator> </expresso:ElementCollection>
</expresso:Block></table>
若采用Expresso扩展Struts的标签库,在Expresso DBMaint里可以看到如下JSP代码(摘自expresso/jsp/dbmaint/list.jsp,为只描述结构,删除了隔行颜色显示等功能):<table border="1" cellspacing="0" cellpadding="1"> <bean:define id="head" type="java.lang.String" property="recordList.@header-row"/> <expresso:TableHead value="<%= head %>"/> <logic:present property="recordList/1"> <logic:iterate id="oneRow" property="recordList" indexId="rowCount"> <tr> <struts_logic:iterate id="oneCol" name="oneRow" property="nested" type="com.jcorporate.expresso.core.controller.ControllerElement"> <logic:present name="oneCol" property="/edit"> <bean:define id="url" name="oneCol" property="/edit.url" type="java.lang.String"/> <td> <html:link page="<%= url %>"> <bean:write name="oneCol"/> </html:link> </td> </logic:present> <logic:notPresent name="oneCol" property="/edit"> <td> <bean:write name="oneCol"/> </td> </logic:notPresent> </struts_logic:iterate> </tr> </logic:iterate> </logic:present></table><logic:notPresent property="recordList/1"> <h3 align="center" > No Records Found </h3></logic:notPresent>应该注意到,以上两种标签库描述的表对象的差别,前者的Transition逻辑上是和Output对象平行的;而后者的Transition对象是嵌套在一系列Output对象里的其中一个Output对象里。Expresso扩展Struts标签库的嵌套是根据id和name来嵌套的,而property则是一个重要的条件判断控制点。事实上,对于表字段对象类型不单一(同时有Output、Transition等)的情况,不管是Expresso标签库还是Expresso扩展Struts标签库,均只有通过组织特殊的字段对象结构(并列或嵌套)来处理。由于不能直接使用字段对象的名称来访问对象,感觉起来不是很方便,此功能大概需要我们编写自己的标签库得以实现吧(这成为我后面工作的一个研究点)。对于上述问题,是否一定得开发标签库解决呢?那倒不一定,下面将描述一种方法解决此问题,只是该方法有点不符合MVC标准而已。有些人会说:“那么多的标签库,学起来都需要时间,且都是应对某一类特殊情况的,我现在马上就要用,一下子又在标签库里找不到我想要的标签,那我怎么办?是否可以压根就不用标签库呢?”现实就是这样,不可能规定所有人的思想方式成一个模型。针对上述情况,本人的建议是:事情紧迫,先就不用标签处理;日后有时间了,再研究是否可用已有标签表达,若不行,则将你的处理方法抽象成标签库。不用标签又怎么处理呢?以下的方法告诉你采用传统的方式(纯JSP)处理控制器传回来的那些对象。首先,你应该知道控制器传回来的结果结构和内容。若控制器是你本人开发的,这便不是问题;若不是你本人开发的,则只有让项目运行,在你访问该控制器的URL后加上&style=xml&xsl=none参数,你就可以看到传回结果的XML格式的数据了(看XML的内容时,请把注意点放在block、input、output、transition等节点内容)。接下来就是采用代码访问了,代码内容大致如下:<%@ page import="com.jcorporate.expresso.core.controller.*" %><% ControllerResponse myResponse = (ControllerResponse)request.getAttribute( "myResponse" ); Input oneInput= myResponse.getInput("FirstName"); //... Block table= myResponse.getInput("Table"); Block record= table.getBlock("Record"); Output field= table.getInput("FieldName"); //... %>
状态的输出:带名字的箭头;(更多情况指跳转Transition)
下图就是一个简单的例子,描述的是学生网上选课的过程。开始状态是“Prompt”,该状态输出三个对象:一个Input(学生编号)、两个Transition(操作),也就是说,负责该状态表示层(View)的.jsp应该对应有这三个对象的呈现,只是图中并未描述View的形式而已。还值得说明的是,开始状态产生的Input(学生编号)的值,它将随着动作的进行作为参数传往下一个状态。在“SELECT COURSE”状态的输出Input对象则是一个下拉列表方式的,选择的值同样作为参数传给了后续状态。
若使用Expresso框架进行设计,我们又多了一种设计的表达方法……状态活动图,大家学会它,我们又多了一种沟通的工具。
public class Demo extends Controller { public Demo() { super();
State index = new State("index", "my index"); addState(index);
setInitialState("index"); }
public String getTitle() { return "Demo Expresso"; }
protected void runIndexState(ControllerRequest myRequest, ControllerResponse myResponse ) throws ControllerException { myResponse.setStyle("index");
}
}
在这种开发方式里面,可以看到控制器用一个类来描述,而它的所有状态则对应为它的一个个受保护方法。这些受保护方法有着规定的名字:run+状态名(首字母大写)+State,且返回为void。当然,这些状态还是要通过控制器的addState()方法注册到控制器才能使用。
public class PromptBrowser extends State { public PromptBrowser(String stateName, String descrip) { super(stateName, descrip); }
public void run() throws ControllerException { Controller myController = getController(); String currentNumber = StringUtil.notNull(myController.getParameter("number")); }}
建议:通常情况下,我们均使用“内部式”进行开发。当出现业务复杂的情况时,为避免所有代码在一个类里造成程序可读性降低,请另外创建一个没有继承的类来描述业务逻辑,而不是直接将方法“挂”在控制器里。
修订历史记录
日期 | 版本 | 说明 | 作者 |
2005-08-15 | 1.0 | 初建 | 唐家平 |
| | | |
| | | |
1 前言... 1
2 控制器的输入... 2
3 控制器的输出对象... 3
3.1 Input 3
3.1.1 Input常用表达用标签... 4
3.2 Output 4
3.2.1 Output常用表达用标签... 5
3.3 Transition. 5
3.3.1 Transition常用表达用标签... 5
3.4 Block. 6
3.4.1 Block常用表达用标签... 7
4 描述控制器的有力武器……控制器活动图... 10
5 控制器的状态... 11
5.1 内部式... 11
5.2 外部式... 12
5.3 控制状态的跳转... 13
5.4 选择合适的视图... 13
6 控制器的错误处理... 14
7 总结... 14
8 参考资料... 14
1 前言
控制器是业务逻辑层的象征,是所有用户动作的入口点。控制器可以看作是有限状态的机器,它由许多的状态组成,其中肯定有一个初始化状态和一个最后状态,初始化状态通常是输入状态,最后状态则通常是成功操作状态。控制层和表示层结合较紧密,在描述控制层时,不得不带入表示层的一些常用知识。这种对应描述目的是反映一种通常用法,基本能应付处理程序设计中的一般问题。更具体的表示层使用说明可直接参考Expresso Developer's Guide,或参考本人将编写的“表示层使用研究”的文章。文章里的例子代码均可以拷贝到你的项目中使用,只是具体的使用环境需要你稍作修改而已。2 控制器的输入
控制器的数据输入通常是URL参数或Html的Form对象,在Expresso控制器里,URL参数读取如下:req.getParameter("dbobj")req是控制器对象ControllerRequest的实例。 对Form对象的读取则通常可采用Expresso DBMaint提供的方法,将数据读入DBObject对象里,这里把DBObject对象当作Model来使用。调用方法通常如下: DataObject myDBObj = this.getDataObject(); ErrorCollection ee = new ErrorCollection();try { setFormCache(); myDBObj.clear();myDBObj =DefaultAutoElement.getAutoControllerElement().parseDBObject(req, myDBObj, ee); clearFormCache(); //...
} catch (DBException de) {}(引自com.jcorporate.expresso.services.controller.dbmaint. AddUpdate.java)
parseDBObject()方法实现核心思想如下:for (Iterator i = myDBObj.getMetaData().getFieldListArray().iterator(); i.hasNext(); ) { String oneFieldName = (String) i.next(); String oneParameterValue = request.getParameter(oneFieldName); myDBObj.set(oneFieldName, oneParameterValue);}具体细节在com.jcorporate.expresso.services.controller.ui. DefaultAutoElement。
可以看出,数据的传入本质上都是使用request.getParameter()方法。
3 控制器的输出对象
我们处理的结果最终是要传递给UI表示层的,通常情况下,正如一些“框架”一样,我们采用Hashmap。在Expresso里,采用组织好一个ControllerResponse对象,所用到的容器对象主要有Input、Output、Transition、Block。3.1 Input
一个Input对象通常产生一个Form的一项内容,我们很容易将之对应于表里的一个字段。通常一个带有Input对象的状态输出是下一个状态的输入参数。于此同时,Input对象还提供“Hints”和下拉列表方式的值校验。典型使用如下: Input favcolour = new Input(); favcolour.setName("FavColour"); favcolour.setLabel("Favourite Colour?"); favcolour.setType(Input.ATTRIBUTE_LISTBOX); favcolour.setDefaultValue("Red"); favcolour. setDescription ("select Colour"); Vector vec = new Vector(); vec.addElement( new ValidValue( "Red", "Red as the Devil") ); vec.addElement( new ValidValue( "Green", "Green as an olive") ); vec.addElement( new ValidValue( "Blue", "Blue as the azure") ); vec.addElement( new ValidValue( "Yellow", "Yellow as a lemon") ); vec.addElement( new ValidValue( "Brown", "Brown like milk chocolate") ); vec.addElement( new ValidValue( "White", "White as snow") ); vec.addElement( new ValidValue( "Black", "Black as the farside of the moon") ); favcolour.setValidValues( vec ); myResponse.addInput(favcolour);以上是创建了一个下拉列表输入框;一般情况下,我们若要创建一个输入文本框时,代码如下: Input firstname = new Input(); firstname.setName("FirstName"); firstname.setLabel("First Name"); firstname.setDisplayLength(30); firstname.setMaxLength(30); firstname.setType("text"); firstname.setDescription(""); myResponse.addInput(firstname);(引自com.xenonsoft.orangetrader.ot2.controller. InteractiveController.java) 在Expresso DBMaint里, Input对象是根据数据库表字段属性来创建的。可以看到形如下语句(增加记录):Input i = DefaultAutoElement.getAutoControllerElement().renderDBObjectField(getControllerResponse(), myDBObj, oneFieldName, cachedValue, readOnly);(引自com.jcorporate.expresso.services.controller.dbmaint.Add.java)对于下拉列表字段的处理,由于在数据对象已经建立关联,这里仅需要简单的调用即可,DBMaint里可以看到如下代码: oneField.setAttribute(Input.ATTRIBUTE_MULTIVALUED, "Y"); oneField.setAttribute(Input.ATTRIBUTE_DROPDOWN, "Y"); oneField.setType(Input.ATTRIBUTE_DROPDOWN); java.util.List values = dbobj.getValidValuesList(fieldName); oneField.setValidValues(new Vector(values));若我们编码,由于数据对象已经非常明确,甚至可以更简单地调用: oneField.setType(Input.ATTRIBUTE_DROPDOWN); oneField.setValidValues(myDBObj.getValidValues(fieldName));3.1.1 Input常用表达用标签
在Expresso对应的tag为<expresso:InputTag name="" />,通常情况下,我们就在表示层界面简单地这样使用Input对象的标签,标签库会自动地根据控制器传来的具体内容而进行相对的显示。 在Expresso DBMaint里,使用的是Expresso扩展Struts的标签库,形式如下,以遍历所有Input对象:<logic:iterate id="oneInput" property="inputs"> <bean:define id="inputName" type="java.lang.String" name="oneInput" property="name"/> <label for="<%=inputName%>"><bean:write name="oneInput" property="label"/>:</label><logic:present name="oneInput" property="@multiValued"> <html:select styleClass="jc-formfield" name="oneInput"/> </logic:present> <logic:notPresent name="oneInput" property="@multiValued"> <html:text styleClass="jc-formfield" name="oneInput"/> </logic:notPresent> </logic:iterate>(引自expresso/jsp/dbmaint/add.jsp)
3.2 Output
Output对象用来传递一个或一组简单的字符串(String)信息,也可以传递一个由多个Output对象嵌套的树。通常使用如下: Output outJimi = new Output(); outJimi.setName("Hendrix"); outJimi.setAttribute("fullname", "James Marshall Hendricks"); outJimi.setAttribute("guitar", "White Fender 1967"); outJimi.setStyle("Electric Blues Rock"); outJimi.setContent("The most influential guitarist in the twentieth century."); outJimi.setAlignment("apolitical"); myResponse.addOutput(outJimi);以上的例子是传递了一组信息,即一个人的基本信息,通过重复调用setAttribute()方法可以加入更多的信息。这样看起来,一个Output对象很容易作为表里的一个字段的载体:名字为字段名,内容为字段值,属性为字段的多个属性。不要将之对应为记录,这样将丢失字段属性信息。更一般情况,我们仅使用Name和Content两个属性来传递字段内容。Input和Output都可以用来传递字段信息,只是它们一个代表入一个代表出,所表达的具体逻辑意义不同而已。有人会问,那我是否可以将它们倒过来用?答案是肯定的,只是使用的感觉就象开车逆道行使而已。 可以使用addNested()方法来嵌套Output对象,多重嵌套便产生了一颗树,树的操作较为复杂,此处就不展开描述了。3.2.1 Output常用表达用标签
在Expresso标签库里对应的tag为如下形式:<expresso:OutputTag name="Hendrix" > <expresso:ContentTag/> <expresso:AttributeTag name="fullname" /></expresso:OutputTag>可以看出以上三类标签的嵌套结构,这种结构是对应Output对象的,也就是说嵌套关系是不可变的。只是嵌套内部的的元素顺序是可变的,对于一个Output对象包含一组信息的情况,我们很容易使用Html里的<Table>标签和上述标签来展现。 在Expresso DBMaint里,则通常简单地采用<bean:write property="untable/pageHeader"/>标签表达。3.3 Transition
Transition对象提供用户转入下一状态的一个路径,当然这个路径是当前状态允许的,且当前用户拥有所要转入状态的权限。值得说明的是,它不提供跳到其它网站URL的功能(没有setUrl()方法),即仅限于本网站控制器间、状态间的跳转。通常使用如下: Transition fireTransition = new Transition(); fireTransition.setLabel("Fire Button"); fireTransition.setName("fireButton"); fireTransition.setControllerObject(getClass().getName()); fireTransition.addParam("state", "showResults"); if (myRequest.getParameter("next") != null) fireTransition.addParam("next", myRequest.getParameter("next")); myResponse.addTransition(fireTransition);3.3.1 Transition常用表达用标签
在Expresso标签库里对应的tag为如下形式:<expresso:TransitionTag name="fireButton" />值得注意的是,该标签将一个Transition输出为4个<Input>的Html标签,这些标签需要和<Form>连用,且这些标签将使<Form>的Action属性失效。输出结果如下:<input type="HIDDEN" name="fireButton_params" value= "controller=com.xenonsoft.orangetrader.ot2.controller.InteractiveController ?state=showResults"><input type="HIDDEN" name="fireButton_encoding" value="u"><INPUT TYPE="submit" NAME="button_fireButton" VALUE= "Fire Button" CLASS="null" ><INPUT TYPE="hidden" NAME="cmd" VALUE="button" > 在Expresso DBMaint里,则使用如下方式的标签,以遍历显示存在于容器里的所有Transition:<logic:iterate id="oneTransition" property="transitions"> <html:submit name="oneTransition"/></logic:iterate>或<bean:define id="url" name="oneCol" property="/edit.url" type="java.lang.String"/><html:link page="<%= url %>"><bean:write name="oneCol"/></html:link>以上3种对象位于Response的根时,这些标签可直接使用;若位于Block对象中,则需要按照Block组织规则来定位再使用。
3.4 Block
多个Input、Output、Transition对象组成一个控制器的输出成分,如果我们对这些元素不加以分类组织,势必造成混乱。最终导致我们的表示层设计人员将不知道怎样去读取这些信息。Block对象正是这样一个组织它们的容器,它使得前3种对象按逻辑组织得井井有条。如果说将以上3种对象看作是一颗树(或森林)上的节点,Block描述的正是那颗树(或森林)的结构。 通常情况下,我们用Block加上前3种对象来传递一张数据库表的信息,当然这个表的记录条数是可知而有限的,否则这些数据将耗尽你的内存。下面就是一个例子,实现显示一个商品列表的代码。 protected void runListState(ControllerRequest myRequest, ControllerResponse myResponse ) throws ControllerException { try { Goods lst = new Goods(); Goods one = null; Block table = new Block("table"); myResponse.addBlock(table); int q = 0; for (Iterator e = lst.searchAndRetrieveList() .iterator(); e.hasNext(); ) { one = (Goods) e.next(); Block record = new Block("record" + (q + 1)); table.add(record);Output out1 = new Output(); out1.setName("GOODS_ID"); out1.setAlignment("left"); out1.setContent(one.getField("GOODS_ID")); record.add(out1);
Output out2 = new Output(); out2.setName("GOODS_NAME"); out2.setAlignment("left"); out2.setContent(one.getField("GOODS_NAME")); record.add(out2);
// Add an transition Transition infoTransition = new Transition("Get detail", getClass().getName()); infoTransition.setName("detail"); infoTransition.addParam("state", "showDetail"); infoTransition.addParam("goods_id", one.getField("GOODS_NAME")); record.add(infoTransition); }
myResponse.setStyle("list");
} catch (Exception ex) { throw new ControllerException(ex.getMessage()); }}
上例我们使用了2种意思的Block对象:表示表概念的名字为table的Block对象和表示记录概念的名字为recordX的Block对象,这样Input、Output、Transition对象便扮演了字段概念。
3.4.1 Block常用表达用标签
采用Expresso标签库进行表示层(View)的JSP代码如下:<table border="1" cellspacing="0" cellpadding="1"><expresso:Block name="table"> <!-- <expresso:TableHead value="Block|Content|Action"/>--> <tr bgcolor="#000099"> <td width="15%"><font color="#FFFFFF"> Block </font></td> <td><font color="#FFFFFF"> Content </font></td> <td><font color="#FFFFFF"> Action </font></td> </tr><expresso:ElementCollection type="block"> <expresso:ElementIterator>
<tr> <td bgcolor="#E0FFE0" width="15%">***</td>
<expresso:ElementCollection type="output"> <expresso:ElementIterator> <expresso:OutputTag name="xxx"> <td><expresso:ContentTag /> </td> </expresso:OutputTag> </expresso:ElementIterator> </expresso:ElementCollection>
<expresso:ElementCollection type="transition"> <expresso:ElementIterator> <form action="<%= contextPath %>/Demo.do?cmd=button" method="GET"> <td><expresso:TransitionTag name=" detail " /></td> </form> </expresso:ElementIterator> </expresso:ElementCollection> </tr>
</expresso:ElementIterator> </expresso:ElementCollection>
</expresso:Block></table>
若采用Expresso扩展Struts的标签库,在Expresso DBMaint里可以看到如下JSP代码(摘自expresso/jsp/dbmaint/list.jsp,为只描述结构,删除了隔行颜色显示等功能):<table border="1" cellspacing="0" cellpadding="1"> <bean:define id="head" type="java.lang.String" property="recordList.@header-row"/> <expresso:TableHead value="<%= head %>"/> <logic:present property="recordList/1"> <logic:iterate id="oneRow" property="recordList" indexId="rowCount"> <tr> <struts_logic:iterate id="oneCol" name="oneRow" property="nested" type="com.jcorporate.expresso.core.controller.ControllerElement"> <logic:present name="oneCol" property="/edit"> <bean:define id="url" name="oneCol" property="/edit.url" type="java.lang.String"/> <td> <html:link page="<%= url %>"> <bean:write name="oneCol"/> </html:link> </td> </logic:present> <logic:notPresent name="oneCol" property="/edit"> <td> <bean:write name="oneCol"/> </td> </logic:notPresent> </struts_logic:iterate> </tr> </logic:iterate> </logic:present></table><logic:notPresent property="recordList/1"> <h3 align="center" > No Records Found </h3></logic:notPresent>应该注意到,以上两种标签库描述的表对象的差别,前者的Transition逻辑上是和Output对象平行的;而后者的Transition对象是嵌套在一系列Output对象里的其中一个Output对象里。Expresso扩展Struts标签库的嵌套是根据id和name来嵌套的,而property则是一个重要的条件判断控制点。事实上,对于表字段对象类型不单一(同时有Output、Transition等)的情况,不管是Expresso标签库还是Expresso扩展Struts标签库,均只有通过组织特殊的字段对象结构(并列或嵌套)来处理。由于不能直接使用字段对象的名称来访问对象,感觉起来不是很方便,此功能大概需要我们编写自己的标签库得以实现吧(这成为我后面工作的一个研究点)。对于上述问题,是否一定得开发标签库解决呢?那倒不一定,下面将描述一种方法解决此问题,只是该方法有点不符合MVC标准而已。有些人会说:“那么多的标签库,学起来都需要时间,且都是应对某一类特殊情况的,我现在马上就要用,一下子又在标签库里找不到我想要的标签,那我怎么办?是否可以压根就不用标签库呢?”现实就是这样,不可能规定所有人的思想方式成一个模型。针对上述情况,本人的建议是:事情紧迫,先就不用标签处理;日后有时间了,再研究是否可用已有标签表达,若不行,则将你的处理方法抽象成标签库。不用标签又怎么处理呢?以下的方法告诉你采用传统的方式(纯JSP)处理控制器传回来的那些对象。首先,你应该知道控制器传回来的结果结构和内容。若控制器是你本人开发的,这便不是问题;若不是你本人开发的,则只有让项目运行,在你访问该控制器的URL后加上&style=xml&xsl=none参数,你就可以看到传回结果的XML格式的数据了(看XML的内容时,请把注意点放在block、input、output、transition等节点内容)。接下来就是采用代码访问了,代码内容大致如下:<%@ page import="com.jcorporate.expresso.core.controller.*" %><% ControllerResponse myResponse = (ControllerResponse)request.getAttribute( "myResponse" ); Input oneInput= myResponse.getInput("FirstName"); //... Block table= myResponse.getInput("Table"); Block record= table.getBlock("Record"); Output field= table.getInput("FieldName"); //... %>
4 描述控制器的有力武器……控制器状态活动图
象设计类的人员使用类图一样,我们在设计控制器时,可以使用控制器状态活动图来描述控制器各状态间的关系。我们规定出基本图形符号如下:状态:椭圆;状态的输出:带名字的箭头;(更多情况指跳转Transition)
下图就是一个简单的例子,描述的是学生网上选课的过程。开始状态是“Prompt”,该状态输出三个对象:一个Input(学生编号)、两个Transition(操作),也就是说,负责该状态表示层(View)的.jsp应该对应有这三个对象的呈现,只是图中并未描述View的形式而已。还值得说明的是,开始状态产生的Input(学生编号)的值,它将随着动作的进行作为参数传往下一个状态。在“SELECT COURSE”状态的输出Input对象则是一个下拉列表方式的,选择的值同样作为参数传给了后续状态。
若使用Expresso框架进行设计,我们又多了一种设计的表达方法……状态活动图,大家学会它,我们又多了一种沟通的工具。
5 控制器的状态
控制器的每一种状态就是一个动作的入口,我们可以将之看作是一个类的一个方法,也可以将这个方法独立出来成一个单独的类。这样,控制器状态便存在2种开发形式:内部式和外部式。5.1 内部式
一般情况下,这是我们常用的一种开发方式,这种方式的典型结构如下:package com.zoomtech.controller; import com.jcorporate.expresso.core.controller.*;import com.zoomtech.obj.*;import java.util.Iterator;public class Demo extends Controller { public Demo() { super();
State index = new State("index", "my index"); addState(index);
setInitialState("index"); }
public String getTitle() { return "Demo Expresso"; }
protected void runIndexState(ControllerRequest myRequest, ControllerResponse myResponse ) throws ControllerException { myResponse.setStyle("index");
}
}
在这种开发方式里面,可以看到控制器用一个类来描述,而它的所有状态则对应为它的一个个受保护方法。这些受保护方法有着规定的名字:run+状态名(首字母大写)+State,且返回为void。当然,这些状态还是要通过控制器的addState()方法注册到控制器才能使用。
5.2 外部式
这种方式通常被使用在一个比较复杂的控制器中,控制器使用一个类描述,其余每个状态对应一个类描述。他们之间通过addState()、addParameter()、getController()等方法进行通讯。addParameter()方法相当于在请求URL后面增加了一个参数,只是这个参数是在控制器编码时使用而已。下例为上传文件控制器的部分代码:public Upload() { PromptBrowser browser = new PromptBrowser("browser", "Prompt for Upload from Browser"); browser.addParameter("resource", false); addState(browser); DoBrowser dobrowser = new DoBrowser("dobrowser", "Process Upload from Browser"); dobrowser.addParameter("action"); addState(dobrowser); }public class PromptBrowser extends State { public PromptBrowser(String stateName, String descrip) { super(stateName, descrip); }
public void run() throws ControllerException { Controller myController = getController(); String currentNumber = StringUtil.notNull(myController.getParameter("number")); }}
建议:通常情况下,我们均使用“内部式”进行开发。当出现业务复杂的情况时,为避免所有代码在一个类里造成程序可读性降低,请另外创建一个没有继承的类来描述业务逻辑,而不是直接将方法“挂”在控制器里。
5.3 控制状态的跳转
有时候,我们的程序逻辑需要从一个状态跳到另外一个状态……这种跳转并不需要用户动作。这主要分为一下2种情况:在一个控制器中的状态间跳转,不管状态是内部式和外部式,跳转的方法均一样,即调用控制器的transition()方法,具体形式如下:this.transition("newstate", req, res);return; 在不同控制器中的状态间跳转,这种情况就稍微复杂些,必须通过Transition来实现。例子如下:Transition t = new Transition(); t.setName("goSomewhere"); t.addParameter("controller", "com.something.SomeOtherController");t.addParameter("state", "someState"); t.addParameter("otherParameter", "etc"); t.transition(req, res); return;其中“goSomewhere”可以是任意的,但不可不调用setName()方法。5.4 选择合适的视图
控制器的状态最终会将操作结构输出到一定的视图,这些视图均为在xxx-config.xml配置文件的forward别名。然而,某一状态将调用哪个视图呢?默认情况下,状态调用的是和状态名称一样的forward别名。若我们想在写程序时动态调用forward别名时,则可以通过setStyle()方法得以实现,具体如下:myResponse.setStyle("employeeForm");建议所有的程序员这样显式调用该方法完成视图的选择,以增强代码的可读性。6 控制器的错误处理
在控制器状态的方法里抛出ControllerException类型的错误均可由Expresso框架自动捕捉,错误信息由Struts-config.xml里配置的全局forward别名交由具体的JSP页面,设置通常如下:<global-forwards> <forward name="error" path="/expresso/jsp/showerror.jsp"/></global-forwards> 在编写程序时,我们并不想这样简单地抛出错误,而是对于可预测性错误进行处理。Expresso提供一个ErrorCollection对象容器让我们收集错误信息,然后通过调用控制器的saveErrors()传出去。在页面通过如下标签显示即可:<expresso:IfErrorExists> <expresso:ErrorTag/> </expresso:IfErrorExists>7 总结
本人在编写此文时,曾多次尝试将DBMaint作为例子来说,可是结果是花了本人不少时间寻找典型代码段落不说,找到某功能的实现点其实复杂而晦涩难懂,因而只得作罢。事实上,DBMaint为了增强功能的通用性、自动性,而不得不牺牲了代码的可读性。比如针对表字段的不同类型,往往是很长一段的If结构。条件的增多带来代码行急剧增多,又不得不采用一层层的函数调用。可以说,DBMaint为达到它作为通用数据库工具的目的,设计和编码都是非常成功的,若作为别的方面使用,是值得商榷的。将控制器和视图分离,也就是将逻辑层和表示层分离,其核心思想就是分离,这正是大师的Observer设计模式。进一步利用这种思想,我们完全可以在开发控制器时,将更纯的业务逻辑分离出来,这时控制器就简化为一个动作的入口点和出口结果的组织点。Expresso封装了Struts,提出了一些新观点,告诉我们不可以简单地把Expresso和Struts进行类比。可以肯定的是,它们的MVC思想是相通的,是一脉相承的。8 参考资料
Orange Trader Example Web ApplicationExpresso 5.5 Developer's Guide相关文章推荐
- Expresso表示层的深入使用研究
- Expresso持久层的深入使用研究
- oracle AWR深入研究分析,如何使用
- Annotation深入研究——@Documented注释使用
- Fiddler调式使用知多少(一)深入研究
- centos使用yum时提示Segmentation fault错误的深入研究
- 抓包工具Fiddler的使用教程(二十):深入研究AutoResponder
- Fiddler调式使用(一)深入研究
- 深入研究socket编程(3)——使用select函数编写客户端和服务器
- Fiddler调式使用(一)深入研究[转载]
- 深入研究jQuery图片懒加载 lazyload.js使用方法
- 【Java 线程的深入研究3】最简单实例说明wait、notify、notifyAll的使用方法
- unity深入研究--开发之C#使用Socket与HTTP连接服务器传输数据包
- three.js自学之旅(8)—— 天空盒研究、完善nodejs服务器、使用TrackballControls控制器
- 【Java NIO的深入研究2】RandomAccessFile的使用
- 8 -- 深入使用Spring -- 7...3 让Spring管理控制器
- oracle AWR深入研究分析,如何使用
- 深入解读Spring Framework Web MVC(第三弹:使用@Controller定义控制器)
- 深入研究socket编程(3)——使用select函数编写客户端和服务器
- java 容器深入研究之使用Abstract类定制