一、簡介
在SpringMVC 中,控制器Controller 負責處理由DispatcherServlet 分發的請求,它把用戶請求的數據經過業務處理層處理之后封裝成一個Model ,然后再把該Model 返回給對應的View 進行展示。在SpringMVC 中提供了一個非常簡便的定義Controller 的方法,你無需繼承特定的類或實現特定的接口,只需使用@Controller 標記一個類是Controller ,然后使用@RequestMapping 和@RequestParam 等一些注解用以定義URL 請求和Controller 方法之間的映射,這樣的Controller 就能被外界訪問到。此外Controller 不會直接依賴於HttpServletRequest 和HttpServletResponse 等HttpServlet 對象,它們可以通過Controller 的方法參數靈活的獲取到。為了先對Controller 有一個初步的印象,以下先定義一個簡單的Controller :
@Controller public class MyController { @RequestMapping ( "/showView" ) public ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應的屬性值,它是一個對象 " ); return modelAndView; } }
在上面的示例中,@Controller 是標記在類MyController 上面的,所以類MyController 就是一個SpringMVC Controller 對象了,然后使用@RequestMapping(“/showView”) 標記在Controller 方法上,表示當請求/showView.do 的時候訪問的是MyController 的showView 方法,該方法返回了一個包括Model 和View 的ModelAndView 對象。
二、使用 @Controller 定義一個 Controller 控制器
@Controller 用於標記在一個類上,使用它標記的類就是一個SpringMVC Controller 對象。分發處理器將會掃描使用了該注解的類的方法,並檢測該方法是否使用了@RequestMapping 注解。@Controller 只是定義了一個控制器類,而使用@RequestMapping 注解的方法才是真正處理請求的處理器,這個接下來就會講到。
單單使用@Controller 標記在一個類上還不能真正意義上的說它就是SpringMVC 的一個控制器類,因為這個時候Spring 還不認識它。那么要如何做Spring 才能認識它呢?這個時候就需要我們把這個控制器類交給Spring 來管理。拿MyController 來舉一個例子
@Controller public class MyController { @RequestMapping ( "/showView" ) public ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應的屬性值,它是一個對象 " ); return modelAndView; } }
這個時候有兩種方式可以把MyController 交給Spring 管理,好讓它能夠識別我們標記的@Controller 。
第一種方式是在SpringMVC 的配置文件中定義MyController 的bean 對象。
<bean class="com.host.app.web.controller.MyController"/>
第二種方式是在SpringMVC 的配置文件中告訴Spring 該到哪里去找標記為@Controller 的Controller 控制器。
< context:component-scan base-package = "com.host.app.web.controller" > < context:exclude-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> </ context:component-scan >
注:上面 context:exclude-filter 標注的是不掃描 @Service 標注的類
三、使用 @RequestMapping 來映射 Request 請求與處理器
可以使用@RequestMapping 來映射URL 到控制器類,或者是到Controller 控制器的處理方法上。當@RequestMapping 標記在Controller 類上的時候,里面使用@RequestMapping 標記的方法的請求地址都是相對於類上的@RequestMapping 而言的;當Controller 類上沒有標記@RequestMapping 注解時,方法上的@RequestMapping 都是絕對路徑。這種絕對路徑和相對路徑所組合成的最終路徑都是相對於根路徑“/ ”而言的。
@Controller public class MyController { @RequestMapping ( "/showView" ) public ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應的屬性值,它是一個對象 " ); return modelAndView; } }
在這個控制器中,因為MyController 沒有被@RequestMapping 標記,所以當需要訪問到里面使用了@RequestMapping 標記的showView 方法時,就是使用的絕對路徑/showView.do 請求就可以了。
@Controller @RequestMapping ( "/test" ) public class MyController { @RequestMapping ( "/showView" ) public ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應的屬性值,它是一個對象 " ); return modelAndView; } }
這種情況是在控制器上加了@RequestMapping 注解,所以當需要訪問到里面使用了@RequestMapping 標記的方法showView() 的時候就需要使用showView 方法上@RequestMapping 相對於控制器MyController上@RequestMapping 的地址,即/test/showView.do 。
(一)使用 URI 模板
URI 模板就是在URI 中給定一個變量,然后在映射的時候動態的給該變量賦值。如URI 模板http://localhost/app/{variable1}/index.html ,這個模板里面包含一個變量variable1 ,那么當我們請求http://localhost/app/hello/index.html 的時候,該URL 就跟模板相匹配,只是把模板中的variable1 用hello 來取代。在SpringMVC 中,這種取代模板中定義的變量的值也可以給處理器方法使用,這樣我們就可以非常方便的實現URL 的RestFul 風格。這個變量在SpringMVC 中是使用@PathVariable 來標記的。
在SpringMVC 中,我們可以使用@PathVariable 來標記一個Controller 的處理方法參數,表示該參數的值將使用URI 模板中對應的變量的值來賦值。
@Controller @RequestMapping ( "/test/{variable1}" ) public class MyController { @RequestMapping ( "/showView/{variable2}" ) public ModelAndView showView( @PathVariable String variable1, @PathVariable ( "variable2" ) int variable2) { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應的屬性值,它是一個對象 " ); return modelAndView; } }
在上面的代碼中我們定義了兩個URI 變量,一個是控制器類上的variable1 ,一個是showView 方法上的variable2,然后在showView 方法的參數里面使用@PathVariable 標記使用了這兩個變量。所以當我們使用/test/hello/showView/2.do 來請求的時候就可以訪問到MyController 的showView 方法,這個時候variable1 就被賦予值hello ,variable2 就被賦予值2 ,然后我們在showView 方法參數里面標注了參數variable1 和variable2 是來自訪問路徑的path 變量,這樣方法參數variable1 和variable2 就被分別賦予hello 和2 。方法參數variable1 是定義為String 類型,variable2 是定義為int 類型,像這種簡單類型在進行賦值的時候Spring 是會幫我們自動轉換的
在上面的代碼中我們可以看到在標記variable1 為path 變量的時候我們使用的是@PathVariable ,而在標記variable2 的時候使用的是@PathVariable(“variable2”) 。這兩者有什么區別呢?第一種情況就默認去URI 模板中找跟參數名相同的變量,但是這種情況只有在使用debug 模式進行編譯的時候才可以,而第二種情況是明確規定使用的就是URI 模板中的variable2 變量。當不是使用debug 模式進行編譯,或者是所需要使用的變量名跟參數名不相同的時候,就要使用第二種方式明確指出使用的是URI 模板中的哪個變量。
除了在請求路徑中使用URI 模板,定義變量之外,@RequestMapping 中還支持通配符“* ”。如下面的代碼我就可以使用/myTest/whatever/wildcard.do 訪問到Controller 的testWildcard 方法。
@Controller @RequestMapping ( "/myTest" ) public class MyController { @RequestMapping ( "*/wildcard" ) public String testWildcard() { System. out .println( "wildcard------------" ); return "wildcard" ; } }
(二)使用 @RequestParam 綁定 HttpServletRequest 請求參數到控制器方法參數
@RequestMapping ( "requestParam" ) public String testRequestParam( @RequestParam(required=false) String name, @RequestParam ( "age" ) int age) { return "requestParam" ; }
在上面代碼中利用@RequestParam 從HttpServletRequest 中綁定了參數name 到控制器方法參數name ,綁定了參數age 到控制器方法參數age 。值得注意的是和@PathVariable 一樣,當你沒有明確指定從request 中取哪個參數時,Spring 在代碼是debug 編譯的情況下會默認取更方法參數同名的參數,如果不是debug 編譯的就會報錯。此外,當需要從request 中綁定的參數和方法的參數名不相同的時候,也需要在@RequestParam 中明確指出是要綁定哪個參數。在上面的代碼中如果我訪問/requestParam.do?name=hello&age=1 則Spring 將會把request請求參數name 的值hello 賦給對應的處理方法參數name ,把參數age 的值1 賦給對應的處理方法參數age 。
在@RequestParam 中除了指定綁定哪個參數的屬性value 之外,還有一個屬性required ,它表示所指定的參數是否必須在request 屬性中存在,默認是true ,表示必須存在,當不存在時就會報錯。在上面代碼中我們指定了參數name 的required 的屬性為false ,而沒有指定age 的required 屬性,這時候如果我們訪問/requestParam.do而沒有傳遞參數的時候,系統就會拋出異常,因為age 參數是必須存在的,而我們沒有指定。而如果我們訪問/requestParam.do?age=1 的時候就可以正常訪問,因為我們傳遞了必須的參數age ,而參數name 是非必須的,不傳遞也可以。