后台采用Struts2+Spring2.5+Hibernate3的经典模式。简约主义,体现在分层简约(三层:Action、service、dao)、结构节约(类的解耦:Action一个类,通过DI调用Service。Service通过配置注入泛型基类dao的实例,无需手工书写dao。添加一个Model,后台就OK了)、配置节约,默认的Struts无需进行任何配置,因为我们已经采用了通配符的方式:
Struts.xml
1 <package name="page" extends="json-default" namespace="/page">
2 <action name="*/*" class="{1}Action" method="{2}">
4 <result name="success">/WEB-INF/pages/{1}/list.jsp</result>
5 <!-- 引入JSON,前台用YUI展示 -->
6 <result name="json" type="json"/>
7 </action>
8 </package>
Spring需要配置如:
1 <!-- Service Begin -->
2 <bean id="userManager" class="com.aostar.service.UserManager">
3 <property name="sessionFactory" ref="sessionFactory" />
4 </bean>
5 <!-- Service End -->
6 <!-- Action Begin -->
7 <bean id="userAction" class="com.aostar.webapp.action.UserAction" scope="prototype">
8 <property name="userManager" ref="userManager" />
9 </bean>
10 <!-- Action End -->
Hibernate需要加一行:
1 <property name="annotatedClasses">
2 <list>
3 <value>com.aostar.model.User</value>
4 </list>
5 </property>
因为我们采用了JPA,避免手工书写xml。
至于前台,与后台的联系是单纯的数据交换,JSON格式的数据很适合在有限的带宽里无限奔跑。基于此,数据交换格式采用JSON,而前台UI使用YUI制作。话说YUI还是不错的,文档一应俱全(虽然是英文),效果也很可赞。
前台,一个页面搞定CURD:
1 <script type="text/javascript">
2 YAHOO.namespace("aostar.container");
3
4 var myDataTable,myDataSource;
5 //启动时载入,获取JSON数据,初始化列表
6 YAHOO.util.Event.addListener(window, "load", function() {
7 YAHOO.aostar.USERJSON = new function() {
8 var myColumnDefs = [
9 {key:"id", sortable:true, resizeable:true},
10 {key:"name",sortable:true, resizeable:true},
11 {key:"module",sortable:true, resizeable:true},
12 {key:"note"},
13 {key:"active"}
14 ];
15
16 myDataSource = new YAHOO.util.DataSource("jsonList.action");
17 myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
18 myDataSource.connXhrMode = "queueRequests";
19 myDataSource.responseSchema = {
20 resultsList: "page.list",
21 fields: ["id","name","module","note","active"]
22 };
25
26 myDataTable = new YAHOO.widget.DataTable("json", myColumnDefs,
27 myDataSource, {initialRequest:""});
28
29 myDataTable.subscribe("checkboxClickEvent", function(oArgs){
30 var elCheckbox = oArgs.target;
31 var oRecord = this.getRecord(elCheckbox);
32 oRecord.setData("check",elCheckbox.checked);
33 });
34
35 //监听row的相关行为
36 myDataTable.subscribe("rowMouseoverEvent", myDataTable.onEventHighlightRow);
37 myDataTable.subscribe("rowMouseoutEvent", myDataTable.onEventUnhighlightRow);
38 myDataTable.subscribe("rowClickEvent", myDataTable.onEventSelectRow);
39 };
40 });
41 //处理Dialog的各种动作
42 function init() {
43 // 处理Dialog层的各种触发动作
44 var handleSubmit = function() {
45 this.submit();
46 };
47 var handleCancel = function() {
48 YAHOO.aostar.container.dlg.form.reset();
49 this.cancel();
50 };
51 var handleSuccess = function(o) {
52 var m,response;
53 try {
54 m = YAHOO.lang.JSON.parse(o.responseText);
55 }
56 catch (x) {
57 alert("解析JSON失败!");
58 return;
59 }
60 YAHOO.aostar.container.dlg.form.reset();
61 if(m.message=="create"){
62 response = "新增表成功,点击“确定”刷新页面"
63 }else if(m.message=="update"){
64 response = "更新表成功,点击“确定”刷新页面"
65 }
66 if (confirm(response)) {
67 window.location.reload();
68 }
69 };
70 var handleFailure = function(o) {
71 alert("提交失败: " + o.status);
72 };
73
74 var batchDeleteMethod = {
75 success : function (o) {
76 var m,response;
77 try {
78 m = YAHOO.lang.JSON.parse(o.responseText);
79 }
80 catch (x) {
81 alert("解析JSON失败!");
82 return;
83 }
84 if(m.message=="delete"){
85 response = "删除表成功,点击“确定”刷新页面"
86 }
87 if (confirm(response)) {
88 window.location.reload();
89 }
90 },
91 failure : function (o) {
92 if (!YAHOO.util.Connect.isCallInProgress(o)) {
93 alert("服务调用失败!");
94 }
95 },
96 timeout : 3000
97 };
98
99 //编辑方法
100 var editRowData = function(e){
101 //var oRecord = myDataTable.getRecord(myDataTable.getFirstTrEl());
102 var oRecord;
103 var selectRows = myDataTable.getSelectedRows();
104 if(selectRows.length!=null && selectRows.length==1){
105 oRecord = myDataTable.getRecordSet().getRecord(selectRows[0]);
106 var formData = YAHOO.aostar.container.dlg.form;
107 formData.reset();
108 formData.id.value = YAHOO.lang.dump(oRecord.getData("id"));
109 formData.name.value = YAHOO.lang.dump(oRecord.getData("name"));
110 formData.module.value = YAHOO.lang.dump(oRecord.getData("module"));
111 formData.active.value = YAHOO.lang.dump(oRecord.getData("active"));
112 formData.note.value = YAHOO.lang.dump(oRecord.getData("note"));
113 YAHOO.aostar.container.dlg.show();
114 }else{
115 alert("请选中一行进行编辑");
116 }
117 }
118 //删除方法
119 var deleteRowData = function(e){
120 //var oRecord = myDataTable.getRecord(myDataTable.getFirstTrEl());
121 var oRecord;
122 var selectRows = myDataTable.getSelectedRows();
123 if(selectRows.length>0){
124 var sId="{id:[";
125 for(var i=0;i<selectRows.length;i++){
126 oRecord = myDataTable.getRecordSet().getRecord(selectRows[i]);
127 if(i>0){
128 sId += ",";
129 }
130 sId += YAHOO.lang.dump(oRecord.getData("id"));
131 }
132 sId += "]}";
133 }else{
134 alert("请选中行进行删除");
135 }
136 YAHOO.util.Connect.asyncRequest('GET',"batchDelete.action?batchId="+sId, batchDeleteMethod);
137 }
138 //定义Dialog的一些属性
139 YAHOO.aostar.container.dlg = new YAHOO.widget.Dialog("dlg",
140 { width : "30em",
141 fixedcenter : true,
142 visible : false,
143 constraintoviewport : true,
144 modal: true,//hideMask
145 buttons : [ { text:"提交", handler:handleSubmit, isDefault:true },
146 { text:"取消", handler:handleCancel } ]
147 });
148
149 // 进行数据验证的方法
150 YAHOO.aostar.container.dlg.validate = function() {
151 var data = this.getData();
152 if (data.name == "" || data.module == "") {
153 alert("请输入名字和所属模块");
154 return false;
155 } else {
156 return true;
157 }
158 };
159
160 // Wire up the success and failure handlers
161 YAHOO.aostar.container.dlg.callback = { success: handleSuccess,
162 failure: handleFailure };
163
164 //装配Dialog层
165 YAHOO.aostar.container.dlg.render();
166
167 YAHOO.util.Event.addListener("addtable", "click", YAHOO.aostar.container.dlg.show, YAHOO.aostar.container.dlg, true);
168 YAHOO.util.Event.addListener("edittable", "click", editRowData);
169 YAHOO.util.Event.addListener("deletetable", "click", deleteRowData);
170 }
171 YAHOO.util.Event.onDOMReady(init);
172 </script>
看到这里,似乎这个架构不过平平,类似的东东网上一抓一把。OK,接下来说到重点。
我们知道,在传统的架构里,前台只是管理业务数据的地方。而程序数据和程序结构则在如迷宫一般的后台里,或者需要到浩如烟海的文档中搜寻。一批接一批的开发人员前仆后继,软件越来越迷宫,文档越来越芜杂,维护也越发困难。
既然hibernate已经把数据表作为对象在后台进行管理了,为何我们不能开发出一个前台管理版的hibernate?在前台可以看到每个表的名字、所属模块、作用。点开以后可以看到每个字段的说明及索引等,何等直观。而且做好这一切后,将数据表直接转化为Web页面的工作也会迅速很多,可以通过程序和模板直接进行转换。这样,我们把后台数据表和前台页面有机的联合起来,而且都作为对象进行管理了。对于后来的维护者也大大的便利了。这个想法,我们可以称之为“数据注册机制”。
软件,是对资源的管理。不管是业务资源还是程序本身的资源,一个架构良好的软件可以对他们进行清晰和方便的管理。既然我们有了“数据注册机制”,那么下一步,我们还可以有“类注册机制”、“包注册机制”……如果能把软件本身相应的资源管理起来。那么所谓DDD、安全、SOA等模式,都可以有一个良好的开始和基础。
附上YUI+JSON版的前台架构样式:
http://www.blogjava.net/Files/shenlei/ssh+yui.rar