后臺采用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.tkk7.com/Files/shenlei/ssh+yui.rar