花了兩個星期學習
Spring WebMVC
,
總體感覺收獲不少,感受比較深的是
Spring
框架的確解決了在
j2EE
開發(fā)中經(jīng)常遇到的問題
,
自己也寫了一個覆蓋框架主要功能的簡單例子,和大家一起交流、分享一下,如有錯誤,歡迎大家多指正。
-、相對于WebWork的特性
1、?
表單處理功能比較強大,將數(shù)據(jù)初始化、展現(xiàn)、封裝、效驗等功能做了分解,代碼功能更加清晰(優(yōu))
舉個例子,我們要更新一個產(chǎn)品的信息,一般實現(xiàn)步驟如下:
a.??????
根據(jù)主鍵查找到該產(chǎn)品信息,作為
Request
屬性對象傳遞到編輯頁面
b.??????
在編輯頁面時往往還需要其他一些數(shù)據(jù),如每個產(chǎn)品都有產(chǎn)品類別,因此也會將相關數(shù)據(jù)作為
Request
屬性對象傳遞到編輯頁面
c.??????
完成對編輯頁面客戶端效驗
d.??????
編輯頁面提交后,將頁面輸入數(shù)據(jù)封裝成產(chǎn)品對象,然后效驗,如果有錯誤,需要將此時的產(chǎn)品對象再返回到編輯頁面,提示用戶錯誤信息。
e.??????
效驗正常后,調用業(yè)務邏輯方法處理后,頁面調轉到查詢頁面,注意避免用戶刷新重復提交,往往我們使用
redirect
方式調整
而這些功能在
Spring
框架時如何實現(xiàn)的,只需要大家繼承于
org.springframework.web.servlet.mvc.SimpleFormController
類,針對上述步驟,我們需要分別重寫如下方法:
說明:
?SimpleException
是自定義的異常,我們在實際開發(fā)中都會定義自己的業(yè)務異常
?Spring
在上述步驟時基本都有其他的實現(xiàn)方法,如
d
步驟也可調用
onBind
帶
BindException
參數(shù)的方法進行效驗,
e
步驟就有更多的同名方法實現(xiàn),我們可以根據(jù)不同的應用場景選擇相對應的方法
?
2、?
容器負責截取異常,根據(jù)配置提供相關異常展現(xiàn)頁面(優(yōu))
目前在
WebWork
中異常直接是由
ServletDispatcher
類捕獲相關異常,發(fā)送
500
錯誤,
WebWork
中實際上還是比較容易實現(xiàn)該功能,定義一個攔截器,注意必須配置時將其配置為第一個攔截器,在調用攔截方法中捕獲系統(tǒng)相關異常,根據(jù)配置調整到異常錯誤處理頁面
我以前在
webwork
實現(xiàn)都是定義在
Action,
攔截器中捕獲異常,將異常綁定在
ActionError
中,然后跳轉到錯誤頁面,
?
3、?
附件上傳功能比較方便(優(yōu))
4、?
url
映射是可以采用通配符匹配的形式,更加靈活(優(yōu))
5、?
值對象數(shù)據(jù)封裝比較麻煩,如果有屬性為整型,頁面值為空串時無法轉換為空指針(缺)
6、?
Spring
將上下文環(huán)境對象作為
Request
屬性對象傳遞,感覺這一塊比較冗余,沒有必要,除了必要的配置信息需要傳遞,其他沒有必要,不夠精確,在方面
webwork
比較好,它是只將
Action
對象作為
Request
屬性對象傳遞(缺)
二、處理請求過程
???
DispatcherServlet??
處理請求過程
1
、綁定上下文對象于
DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE
請求屬性
2
、綁定
locale resolver,
解析本地化信息(沒有忽略該過程)
3
、綁定
theme resolver
,確定使用主題(
沒有忽略該過程)
4
、判斷是否為
multipart,
如果是就解析封裝
5
、查找處理器、執(zhí)行對應的攔截器,獲取視圖模型
6
、根據(jù)
view resolver
選擇相對應的視圖
1
-
6
過程中如有異常,可以被
handlerexception resolver
捕獲
表單處理過程:
1、?
判斷是否是提交表單請求,
2、?
如果不是,調用
showNewForm(request, response, errors)
方法,
2.1
、
如果子類重寫方法調用
formBackingObject
方法
2.2
、
如果有重寫
referenceData
方法,
2.3
、
如果屬性
bindOnNewForm=true
,系統(tǒng)將
request
對應參數(shù)值綁定到對應的對象中
2.4
、
最后根據(jù)屬性
formView
跳轉到相對應的表單錄入頁面
3
、如果是表單提交請求
?
3.1
如果不是會話表單或會話中有對應表單對象,
3.1.1
、
系統(tǒng)從會話中獲取或者調用調用
formBackingObject
方法創(chuàng)建數(shù)據(jù)綁定對象,
根據(jù)頁面輸入值綁定數(shù)據(jù)對象,中間可能會產(chǎn)生綁定異常
?3.1.2
、
如子類重寫了
onBind
方法,會回調對應的方法,對應綁定異常會一直傳遞下去
3.1.3
、
如果子類有相對應的效驗類,并且需要在綁定時效驗,就回調對應的效驗類的效驗方法,對應綁定異常會一直傳遞下去
3.1.4
、
如果子類重寫了
onBindAndValidate
方法,系統(tǒng)會回調對應的綁定效驗方法
3.1.5
、
?
如果有異常,調用
showForm(request, response, errors)
,不是會話表單,重新獲取輔助數(shù)據(jù),執(zhí)行方法
referenceData
返回表單編輯頁面。
3.1.6
、
如果無異常調用
onSubmit(request, response, command, errors),
返回成功提交后的頁面
3
.
2
如果是會話表單并且會話中沒有該表單對象,會根據(jù)
formBackingObject
初始化值對象
,
然后處理過程和
3.1.1
執(zhí)行過程一致。
?
說明:
步驟
3.1.1
會回調一次
formBackingObject
方法,然后再綁定頁面數(shù)據(jù),為何回調該方法的原因是因為我們在頁面中有些屬性不需要顯示,如果只通過頁面封裝數(shù)據(jù),哪些不需要顯示的就無法封裝數(shù)據(jù)。因此在使用該方法的時候特別要注意性能問題,避免在該方法中調用數(shù)據(jù)庫查詢等等影響性能的方法(我還是使用
hidden
控件,雖然安全性低了一點)
?
個人認為步驟
2.3
位置應該
2.1
在前,因為往往我們在實際使用中是先根據(jù)頁面?zhèn)鬟f的值來再構造對應的值對象的。并且這樣的話
頁面?zhèn)鬟f的變量會覆蓋
formBackingObject
值
?
個人建議盡可能的不要使用會話表單
三、開發(fā)注意事項、心得
1、?
自定義攔截器只需實現(xiàn)
org.springframework.web.servlet.HandlerInterceptor
接口
2、?
上傳多個文件時注意事項:建議采用重寫
?protected ModelAndView onSubmit(HttpServletRequest request,HttpServletResponse response, Object command, BindException errors)?? throws Exception
方法,直接通過
MultiHttpServletRequest getFileMap()
方法,對應
map
存儲對象為
MultipartFile
接口實現(xiàn)類,建議通過接口訪問,這樣如果通過其他方式處理上傳的,只需要修改配置文件,通過對應的
transferTo
方法處理上傳的文件。
3、?
在
web
環(huán)境中如何獲取應用上下文對象
ApplicationContext acx=(ApplicationContext)req.getAttribute(
DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
?
4、?
在配置
multipart
解析器
配置時
id="multipartResolver"
<bean id="multipartResolver"? class="
org.springframework.web.multipart.commons.CommonsMultipartResolver">
??
??????? <property name="maxUploadSize"><value>10485760</value>
</property>
</bean>
5、?
在配置資源信息
?
配置時
id=
messageSource
<bean id="messageSource" class="
org.springframework.context.support.ResourceBundleMessageSource">
?
6、?
如果視圖要采用
redirect
方式,配置類似如下
????
??
?<property name="successView">
???
??????????? <value>redirect:/viewpro.action</value>
???? </property>
?
??
7
、
如果要實現(xiàn)一個簡單的頁面數(shù)據(jù)綁定功能
?
只需要繼承
BaseCommandController
類,重寫
handleRequestInternal(
HttpServletRequest request, HttpServletResponse response)
throws Exception
方法
?
在該方法中只要創(chuàng)建數(shù)據(jù)綁定對象,調用綁定方法,然后封裝在對應的視圖中
8
、標簽庫的使用,請參考如下文章:
?http://www-128.ibm.com/developerworks/cn/java/j-jstl0211/index.html
四、簡單例子
?
我的應用環(huán)境
????? AppServer? weblogic814
?????
框架版本
?? Spring ?1.2 RC2
?????
數(shù)據(jù)庫
???? Oracle 9.2.0.1
運行例子注意事項:
1
、修改
build.properties
lib.src
對應目錄改為你本機
spring
目錄
?????? 2
、執(zhí)行數(shù)據(jù)庫腳本
product.sql,
修改對應的
jdbc.properties
文件
3
、如果使用其他的
AppServer
注意在
lib
中加入相關數(shù)據(jù)庫的驅動,并設定
Request
請求中數(shù)據(jù)編碼格式為
GBK
4
、如果使用其他數(shù)據(jù)庫,修改
jdbc.properties
、
hibernate
相關屬性,并且修改
product.hbm.xml
的主鍵生成規(guī)則,本例子是用
sequence
生成。
代碼下載:
http://www.tkk7.com/Files/beauty_beast/springweb_example.rar
?
五、遺留問題
1、?
如何將頁面數(shù)據(jù)和
List,Map
對象綁定?
2、?
使用標準標簽庫時,頁面不能含有中文字符,否則拋出異常、是否標簽庫對字符集有限制?