映射處理器Handler Mapping
1.簡析映射處理器
在spring
mvc中,使用映射處理器可以把web請求映射到正確的處理器上,spring內(nèi)置了很多映射處理器,而且我們也可以自定義映射處理器。
下面的實例展示spring中最常用的兩個映射處理器:
BeanNameUrlHandlerMapping和SimpleUrlHandlerMapping。
在正式開始前有必要了解以下相關(guān)要點:
(1)映射處理器都能把請求傳遞到處理器執(zhí)行鏈接(HandlerExecutionChain)上,并且處理器執(zhí)行鏈接必須包含能處理該請求的處理器(實質(zhì)就是處理器鏈上動態(tài)添加了了此處理器,可以結(jié)合filter工作原理理解),而且處理器鏈接也能包含一系列攔截器。
(2)上面列舉的spring最常用的兩種處理器都是繼承自AbstractHandlerMapping類
,因而它們具備父類的屬性。
2.實例:BeanNameUrlHandlerMapping
在我們上面的工程的基礎(chǔ)上進(jìn)行修改
步驟一:建立后端控制器HelloWorldController.java,
步驟二:配置web.xml,
步驟三:配置spmvc-servlet.xml,
步驟四:在WEB-INF/page目錄下hello.jsp
步驟五:啟動服務(wù)器,輸入…/helloController.do訪問測試。
簡析執(zhí)行過程
(1)啟動服務(wù)器后,當(dāng)我們向服務(wù)器發(fā)送message.do請求時,首先被在web.xml中配置的前端控制器DispatcherServlet攔截到。
(2)前端控制器把此請求轉(zhuǎn)交給后端控制器,下面分析轉(zhuǎn)交過程:當(dāng)在spmvc-servlet.xml中查找能執(zhí)行
helloController
.do請求的映射處理器時,發(fā)現(xiàn)沒有能處理此請求的映射處理器,這時便使用默認(rèn)的映射處理器BeanNameUrlHandlerMapping:This is the default implementation used by the DispatcherServlet, along with DefaultAnnotationHandlerMapping (on Java 5 and higher).
我們還需注意:這種后端控制器的bean Name必須以“/”開頭
,并且要結(jié)合DispatcherServlet的映射配置。同時beanName支持通配符配置
。比如如果配置:<bean name="/m*.do
" class="com.wy.controller.HelloWorldController" /> 時,當(dāng)訪問
helloController
.do時也可以成功訪問到
HelloWorldController
類。
(3)BeanNameUrlHandlerMapping處理器,會查找在spring容器中是否在名為“
helloController
.do”的bean實例
。當(dāng)查找到此實例后,則把此bean作為處理此請求的后端控制器。同時把自身加到映射處理器鏈上,并向處理器鏈傳遞此請求。
(4)后端控制器進(jìn)行處理,并返回視圖。
小結(jié)
:
BeanNameUrlHandlerMapping的實現(xiàn)更為簡單,每個Controller的URL與其name屬性對應(yīng),因此,只需要對每個Controller以URL作為name,就可以實現(xiàn)URL映射。
配置示例如下:
- <bean id="beanNameUrlHandlerMapping" class="org.springframework.web.servlet. handler.BeanNameHandlerMapping" />
-
- <bean name="/a.html" class="example.chapter7.ControllerA" />
-
- <bean name="/a.html" class="example.chapter7.ControllerB" />
之所以用Bean的name作為URL而不是id,是因為XML規(guī)范不允許在id標(biāo)識中使用特殊字符“/”。當(dāng)用戶請求一個URL時,Spring將直接查找name為URL的Controller。
使用
SimpleUrlHandlerMapping的麻煩之處在于,添加或刪除Controller時必須要對
SimpleUrlHandlerMapping做相應(yīng)的修改,而BeanNameUrlHandlerMapping則無需手工編寫映射,只需要在每個
Controller中仔細(xì)定義name屬性。
如果使用XDoclet自動生成配置文件,則可以將name在Controller的注釋中定義,維護(hù)起來
更加方便。
因此,我們推薦首先考慮使用BeanNameUrlHandlerMapping。
事實上,如果沒有在XML配置文件中定義任何
UrlHandlerMapping,則Spring MVC默認(rèn)使用BeanNameUrlHandlerMapping。
3.SimpleUrlHandlerMapping
步驟一:建立后端控制器UserContrller.java.代碼如下:
- package com.asm;
- //...省略導(dǎo)入的相關(guān)類
- public class UserController extends SimpleFormController {
- protected ModelAndView processFormSubmission(HttpServletRequest request, HttpServletResponse response,
- Object command, BindException errors) throws Exception {
- System.out.println("調(diào)用邏輯層,處理表單");
- ModelAndView mav = new ModelAndView("loginSuc");
- return mav;
- }
- }
步驟二:在spmvc-servlet.xml中增加如下配置:
- <bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
- <!-- 為映射處理器引入攔截器bean -->
- <property name="interceptors">
- <list>
- <ref bean="workTimeInterceptor" />
- </list>
- </property>
- <property name="mappings">
- <props>
- <prop key="/op/*/login.do">userController</prop>
- </props>
- </property>
- </bean>
-
- <bean id="userController" class="com.asm.UserController">
- <property name="commandClass" value="com.asm.User"/>
- </bean>
-
- <!-- 攔截器bean -->
- <bean id="workTimeInterceptor"
- class="com.asm.LoginTimeInterceptor">
- <property name="startTime" value="6" />
- <property name="endTime" value="18" />
- </bean>
說明
:(1
)通過前面實例我們可以知道,SimpleController
這樣的后端控制器必須綁定一個commandClass
對象,在這里我們通過配置文件
<
property
name
=
"commandClass"
value
=
"com.asm.User"
/>
綁定。
(2
)
<
prop
key
=
"/op/*/login.do"
>
userController
</
prop
>
配置說明只要訪問是以op
開頭,中間*
可以是任意字符,并以login.do
結(jié)尾的請求,便能訪問到
userController
控制器。
(3
)
SimpleUrlHandlerMapping
是一個更強大的映射處理器,它除了支持上面
<
props
>
的這種配置,還支持Ant
風(fēng)格的路徑匹配。另外也可以進(jìn)行如下形式的配置:
<
property
name
=
"mappings"
>
<
value
>
/op/*/login.do=
userController
</
value
>
</
property
>
(4
)攔截器:為了為某些特殊請求提供特殊功能,spring
為映射處理器提供了攔截器支持。它的配置文件很簡單:一是把攔截器類納入spring
容器管理,二是在映射處理器引入配置的攔截器bean
。
步驟三
:編寫攔截器
LoginTimeInterceptor.java
,主要代碼如下:
- package com.asm;
- //...省略導(dǎo)入的相關(guān)類
- public class LoginTimeInterceptor extends HandlerInterceptorAdapter {
- private int startTime;
- private int endTime;
-
- public void setStartTime(int startTime) {
- this.startTime = startTime;
- }
- public void setEndTime(int endTime) {
- this.endTime = endTime;
- }
-
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
- Exception ex) throws Exception {
- System.out.println("執(zhí)行afterCompletion方法-->03");
- super.afterCompletion(request, response, handler, ex);
- }
-
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
- ModelAndView modelAndView) throws Exception {
- System.out.println("執(zhí)行postHandle方法-->02");
- super.postHandle(request, response, handler, modelAndView);
- }
-
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- System.out.println("執(zhí)行preHandle方法-->01");
- Calendar cal = Calendar.getInstance();
- int hour = cal.get(Calendar.HOUR_OF_DAY);
- if (startTime <= hour && hour < endTime) {
- return true;
- } else {
- response.sendRedirect("http://www.");
- return false;
- }
- }
- }
說明:此攔截器作用:如果用戶沒有在6-18點登錄,則重定向到j(luò)avaeye站點
(1)攔截器必須HandlerInterceptorAdapter接口
(2)preHandle方法在后端控制器執(zhí)行前被調(diào)用,postHandle方法在后端控制器執(zhí)行后被調(diào)用;afterCompletion方法在整個請求處理完成后被調(diào)用。
(3)
preHandle方法:返回true,映射處理器執(zhí)行鏈將繼續(xù)執(zhí)行;當(dāng)返回false時,DispatcherServlet處理器認(rèn)為攔截器已經(jīng)處理完了請求,而不繼續(xù)執(zhí)行執(zhí)行鏈中的其它攔截器和處理器。它的API文檔解釋如下:true
if the execution chain should proceed with the next interceptor or the handler
itself. Else, DispatcherServlet assumes that this interceptor has already dealt
with the response itself.
(4)這三個方法都是相同的參數(shù),Object
handler參數(shù)可以轉(zhuǎn)化成一個后端控制器對象,比如這里可以轉(zhuǎn)換成UserController對象。
步驟四:完成其它相關(guān)代碼的編寫
User.java
代碼
- package com.asm;
-
- public class User {
-
- private String username;
-
- private String password;
-
- //省略getter/setter方法
-
- }
WEB-INF/page/loginSuc.jsp
,
主要代碼如下:
- <body>
-
- 登錄成功!歡迎來到后臺管理頁面
-
- </body>
index.jsp
代碼:
- <form action="<%=request.getContextPath()%>/op/luanXie/login.do" method="post">
-
- 用戶名:<input type="text" name="username"><br/>
-
- 密 碼:<input type="password" name="password"><br/>
-
- <input type="submit" value="登錄">
-
- </form>
步驟五
:訪問index.jsp
頁面,完成測試。
分析執(zhí)行過程:為了清晰體會到整個處理器執(zhí)行過程,我們首先在UserController.java
中增加如下代碼:
- protected Object formBackingObject(HttpServletRequest request) throws Exception {
- System.out.println("formBackingObject方法執(zhí)行-->01");
- return super.formBackingObject(request);
- }
- protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
- System.out.println("initBinder方法執(zhí)行-->02");
- super.initBinder(request, binder);
- }
- protected void onBind(HttpServletRequest request, Object command) throws Exception {
- System.out.println("onBind方法執(zhí)行-->03");
- super.onBind(request, command);
- }
- protected void onBindAndValidate(HttpServletRequest request, Object command, BindException errors)
- throws Exception {
- System.out.println("onBindAndValidate方法執(zhí)行-->04");
- super.onBindAndValidate(request, command, errors);
- }
(1
)當(dāng)訪問…/login.do
時,會首先被前端控制器DispatcherServlet
攔截到,前端控制器通過查找spmvc-servlet.xml
配置文件,并交給后端控制器處理。
(2)
執(zhí)行后,得到如下打印結(jié)果,通過打印結(jié)果我們知道它的一個大致執(zhí)行過程。
執(zhí)行
preHandle
方法
-->01
formBackingObject
方法執(zhí)行
-->01
initBinder
方法執(zhí)行
-->02
onBind
方法執(zhí)行
-->03
onBindAndValidate
方法執(zhí)行
-->04
調(diào)用邏輯層,處理表單
Admin----123456
執(zhí)行
postHandle
方法
-->02
執(zhí)行
afterCompletion
方法
-->03
小結(jié):
混合使用多種
UrlHandlerMapping,但是必須為每個UrlHandlerMapping指定order屬性來表示優(yōu)先級
,order值越小優(yōu)先級越
高,Spring會先查詢優(yōu)先級高的UrlHandlerMapping。
若找到了對應(yīng)的Controller,就不再繼續(xù)查詢,否則,按照優(yōu)先級依次查
詢,直到找到為止。
若所有的UrlHandlerMapping都無法返回一個合適的Controller,并且沒有設(shè)置默認(rèn)的Controller時,
就會返回給客戶端一個“404 Not Found”錯誤,表示不存在這個URL。
|