一、簡介
在SpringMVC 中,控制器Controller 負(fù)責(zé)處理由DispatcherServlet 分發(fā)的請求,它把用戶請求的數(shù)據(jù)經(jīng)過業(yè)務(wù)處理層處理之后封裝成一個Model ,然后再把該Model 返回給對應(yīng)的View 進(jìn)行展示。在SpringMVC 中提供了一個非常簡便的定義Controller 的方法,你無需繼承特定的類或?qū)崿F(xiàn)特定的接口,只需使用@Controller 標(biāo)記一個類是Controller ,然后使用@RequestMapping 和@RequestParam 等一些注解用以定義URL 請求和Controller 方法之間的映射,這樣的Controller 就能被外界訪問到。此外Controller 不會直接依賴于HttpServletRequest 和HttpServletResponse 等HttpServlet 對象,它們可以通過Controller 的方法參數(shù)靈活的獲取到。為了先對Controller 有一個初步的印象,以下先定義一個簡單的Controller :
例1:
1
2
3
4
5
6
7
8
9
10
11
12
|
@Controller public class MyController { @RequestMapping ( "/showView" ) public ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應(yīng)的屬性值,它是一個對象 " ); return modelAndView; } } |
在上面的示例中,@Controller 是標(biāo)記在類MyController 上面的,所以類MyController 就是一個SpringMVC Controller 對象了,然后使用@RequestMapping(“/showView”) 標(biāo)記在Controller 方法上,表示當(dāng)請求/showView.do 的時候訪問的是MyController 的showView 方法,該方法返回了一個包括Model 和View 的ModelAndView 對象。這些在后續(xù)都將會詳細(xì)介紹。
二、使用 @Controller 定義一個 Controller 控制器
例1所示@Controller 用于標(biāo)記在一個類上,使用它標(biāo)記的類就是一個SpringMVC Controller 對象。分發(fā)處理器將會掃描使用了該注解的類的方法,并檢測該方法是否使用了@RequestMapping 注解。@Controller 只是定義了一個控制器類,而使用@RequestMapping 注解的方法才是真正處理請求的處理器,這個接下來就會講到。
單單使用@Controller 標(biāo)記在一個類上還不能真正意義上的說它就是SpringMVC 的一個控制器類,因?yàn)檫@個時候Spring 還不認(rèn)識它。那么要如何做Spring 才能認(rèn)識它呢?這個時候就需要我們把這個控制器類交給Spring 來管理。
這個時候有兩種方式可以把MyController 交給Spring 管理,好讓它能夠識別我們標(biāo)記的@Controller 。
第一種方式是在SpringMVC 的配置文件中定義MyController 的bean 對象。
1
|
< bean class = "com.host.app.web.controller.MyController" /> |
第二種方式是在SpringMVC 的配置文件中告訴Spring 該到哪里去找標(biāo)記為@Controller 的Controller 控制器。
1
2
3
4
|
< 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 標(biāo)注的是不掃描 @Service 標(biāo)注的類
三、使用 @RequestMapping 來映射 Request 請求與處理器
例1可以使用@RequestMapping 來映射URL 到控制器類,或者是到Controller 控制器的處理方法上。當(dāng)@RequestMapping 標(biāo)記在Controller 類上的時候,里面使用@RequestMapping 標(biāo)記的方法的請求地址都是相對于類上的@RequestMapping 而言的;當(dāng)Controller 類上沒有標(biāo)記@RequestMapping 注解時,方法上的@RequestMapping 都是絕對路徑。這種絕對路徑和相對路徑所組合成的最終路徑都是相對于根路徑“/ ”而言的。
在這個控制器中,因?yàn)镸yController 沒有被@RequestMapping 標(biāo)記,所以當(dāng)需要訪問到里面使用了@RequestMapping 標(biāo)記的showView 方法時,就是使用的絕對路徑/showView.do 請求就可以了。
例2
1
2
3
4
5
6
7
8
9
10
11
12
|
@Controller @RequestMapping ( "/test" ) public class MyController { @RequestMapping ( "/showView" ) public ModelAndView showView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應(yīng)的屬性值,它是一個對象 " ); return modelAndView; } } |
例2是在控制器上加了@RequestMapping 注解,所以當(dāng)需要訪問到里面使用了@RequestMapping 標(biāo)記的方法showView() 的時候就需要使用showView 方法上@RequestMapping 相對于控制器MyController上@RequestMapping 的地址,即/test/showView.do 。
(一)使用 URI 模板
URI 模板就是在URI 中給定一個變量,然后在映射的時候動態(tài)的給該變量賦值。如URI 模板http://localhost/app/{variable1}/index.html ,這個模板里面包含一個變量variable1 ,那么當(dāng)我們請求http://localhost/app/hello/index.html 的時候,該URL 就跟模板相匹配,只是把模板中的variable1 用hello 來取代。在SpringMVC 中,這種取代模板中定義的變量的值也可以給處理器方法使用,這樣我們就可以非常方便的實(shí)現(xiàn)URL 的RestFul 風(fēng)格。這個變量在SpringMVC 中是使用@PathVariable 來標(biāo)記的。在SpringMVC 中,我們可以使用@PathVariable 來標(biāo)記一個Controller 的處理方法參數(shù),表示該參數(shù)的值將使用URI 模板中對應(yīng)的變量的值來賦值。
例3
1
2
3
4
5
6
7
8
9
10
11
12
|
@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 中的屬性名稱 " , " 對應(yīng)的屬性值,它是一個對象 " ); return modelAndView; } } |
在例3的代碼中我們定義了兩個URI 變量,一個是控制器類上的variable1 ,一個是showView 方法上的variable2 ,然后在showView 方法的參數(shù)里面使用@PathVariable 標(biāo)記使用了這兩個變量。所以當(dāng)我們使用/test/hello/showView/2.do 來請求的時候就可以訪問到MyController 的showView 方法,這個時候variable1 就被賦予值hello ,variable2 就被賦予值2 ,然后我們在showView 方法參數(shù)里面標(biāo)注了參數(shù)variable1 和variable2 是來自訪問路徑的path 變量,這樣方法參數(shù)variable1 和variable2 就被分別賦予hello 和2 。方法參數(shù)variable1 是定義為String 類型,variable2 是定義為int 類型,像這種簡單類型在進(jìn)行賦值的時候Spring 是會幫我們自動轉(zhuǎn)換的,關(guān)于復(fù)雜類型該如何來轉(zhuǎn)換在后續(xù)內(nèi)容中將會講到。
在上面的代碼中我們可以看到在標(biāo)記variable1 為path 變量的時候我們使用的是@PathVariable ,而在標(biāo)記variable2 的時候使用的是@PathVariable(“variable2”) 。這兩者有什么區(qū)別呢?第一種情況就默認(rèn)去URI 模板中找跟參數(shù)名相同的變量,但是這種情況只有在使用debug 模式進(jìn)行編譯的時候才可以,而第二種情況是明確規(guī)定使用的就是URI 模板中的variable2 變量。當(dāng)不是使用debug 模式進(jìn)行編譯,或者是所需要使用的變量名跟參數(shù)名不相同的時候,就要使用第二種方式明確指出使用的是URI 模板中的哪個變量。
除了在請求路徑中使用URI 模板,定義變量之外,@RequestMapping 中還支持通配符“* ”。如下面的代碼我就可以使用/myTest/whatever/wildcard.do 訪問到Controller 的testWildcard 方法。
1
2
3
4
5
6
7
8
9
|
@Controller @RequestMapping ( "/myTest" ) public class MyController { @RequestMapping ( "*/wildcard" ) public String testWildcard() { System. out .println( "wildcard------------" ); return "wildcard" ; } } |
(二)使用 @RequestParam 綁定 HttpServletRequest 請求參數(shù)到控制器方法參數(shù)
例4
1
2
3
4
|
@RequestMapping ( "requestParam" ) public String testRequestParam( @RequestParam (required= false ) String name, @RequestParam ( "age" ) int age) { return "requestParam" ; } |
在上面代碼中利用@RequestParam 從HttpServletRequest 中綁定了參數(shù)name 到控制器方法參數(shù)name ,綁定了參數(shù)age 到控制器方法參數(shù)age 。值得注意的是和@PathVariable 一樣,當(dāng)你沒有明確指定從request 中取哪個參數(shù)時,Spring 在代碼是debug 編譯的情況下會默認(rèn)取更方法參數(shù)同名的參數(shù),如果不是debug 編譯的就會報錯。此外,當(dāng)需要從request 中綁定的參數(shù)和方法的參數(shù)名不相同的時候,也需要在@RequestParam 中明確指出是要綁定哪個參數(shù)。在上面的代碼中如果我訪問/requestParam.do?name=hello&age=1 則Spring 將會把request請求參數(shù)name 的值hello 賦給對應(yīng)的處理方法參數(shù)name ,把參數(shù)age 的值1 賦給對應(yīng)的處理方法參數(shù)age 。
在@RequestParam 中除了指定綁定哪個參數(shù)的屬性value 之外,還有一個屬性required ,它表示所指定的參數(shù)是否必須在request 屬性中存在,默認(rèn)是true ,表示必須存在,當(dāng)不存在時就會報錯。在上面代碼中我們指定了參數(shù)name 的required 的屬性為false ,而沒有指定age 的required 屬性,這時候如果我們訪問/requestParam.do而沒有傳遞參數(shù)的時候,系統(tǒng)就會拋出異常,因?yàn)閍ge 參數(shù)是必須存在的,而我們沒有指定。而如果我們訪問/requestParam.do?age=1 的時候就可以正常訪問,因?yàn)槲覀儌鬟f了必須的參數(shù)age ,而參數(shù)name 是非必須的,不傳遞也可以。
(三)使用 @CookieValue 綁定 cookie 的值到 Controller 方法參數(shù)
例5
1
2
3
4
5
|
@RequestMapping ( "cookieValue" ) public String testCookieValue( @CookieValue ( "hello" ) String cookieValue, @CookieValue String hello) { System. out .println(cookieValue + "-----------" + hello); return "cookieValue" ; } |
在上面的代碼中我們使用@CookieValue 綁定了cookie 的值到方法參數(shù)上。上面一共綁定了兩個參數(shù),一個是明確指定要綁定的是名稱為hello 的cookie 的值,一個是沒有指定。使用沒有指定的形式的規(guī)則和@PathVariable、@RequestParam 的規(guī)則是一樣的,即在debug 編譯模式下將自動獲取跟方法參數(shù)名同名的cookie 值。
(四)使用 @RequestHeader 注解綁定 HttpServletRequest 頭信息到Controller 方法參數(shù)
例6
1
2
3
4
5
|
@RequestMapping ( "testRequestHeader" ) public String testRequestHeader( @RequestHeader ( "Host" ) String hostAddr, @RequestHeader String Host, @RequestHeader String host ) { System. out .println(hostAddr + "-----" + Host + "-----" + host ); return "requestHeader" ; } |
在上面的代碼中我們使用了 @RequestHeader 綁定了 HttpServletRequest 請求頭 host 到Controller 的方法參數(shù)。上面方法的三個參數(shù)都將會賦予同一個值,由此我們可以知道在綁定請求頭參數(shù)到方法參數(shù)的時候規(guī)則和 @PathVariable 、 @RequestParam 以及 @CookieValue 是一樣的,即沒有指定綁定哪個參數(shù)到方法參數(shù)的時候,在 debug 編譯模式下將使用方法參數(shù)名作為需要綁定的參數(shù)。但是有一點(diǎn) @RequestHeader 跟另外三種綁定方式是不一樣的,那就是在使用 @RequestHeader 的時候是大小寫不敏感的,即 @RequestHeader(“Host”) 和 @RequestHeader(“host”) 綁定的都是 Host 頭信息。記住在 @PathVariable 、 @RequestParam 和 @CookieValue 中都是大小寫敏感的。
(五) @RequestMapping 的一些高級應(yīng)用
在RequestMapping 中除了指定請求路徑value 屬性外,還有其他的屬性可以指定,如params 、method 和headers 。這樣屬性都可以用于縮小請求的映射范圍。
1.params屬性
例7
1
2
3
4
5
|
@RequestMapping (value= "testParams" , params={ "param1=value1" , "param2" , "!param3" }) public String testParams() { System. out .println( "test Params..........." ); return "testParams" ; } |
在上面的代碼中我們用@RequestMapping 的params 屬性指定了三個參數(shù),這些參數(shù)都是針對請求參數(shù)而言的,它們分別表示參數(shù)param1 的值必須等于value1 ,參數(shù)param2 必須存在,值無所謂,參數(shù)param3 必須不存在,只有當(dāng)請求/testParams.do 并且滿足指定的三個參數(shù)條件的時候才能訪問到該方法。所以當(dāng)請求/testParams.do?param1=value1¶m2=value2 的時候能夠正確訪問到該testParams 方法,當(dāng)請求/testParams.do?param1=value1¶m2=value2¶m3=value3 的時候就不能夠正常的訪問到該方法,因?yàn)樵贎RequestMapping 的params 參數(shù)里面指定了參數(shù)param3 是不能存在的。
2.method屬性
例8
1
2
3
4
|
@RequestMapping (value= "testMethod" , method={RequestMethod. GET , RequestMethod. DELETE }) public String testMethod() { return "method" ; } |
在上面的代碼中就使用method 參數(shù)限制了以GET 或DELETE 方法請求/testMethod.do 的時候才能訪問到該Controller 的testMethod 方法。
3.headers屬性
例9
1
2
3
4
|
@RequestMapping (value= "testHeaders" , headers={ "host=localhost" , "Accept" }) public String testHeaders() { return "headers" ; } |
headers 屬性的用法和功能與params 屬性相似。在上面的代碼中當(dāng)請求/testHeaders.do 的時候只有當(dāng)請求頭包含Accept 信息,且請求的host 為localhost 的時候才能正確的訪問到testHeaders 方法。
(六)以 @RequestMapping 標(biāo)記的處理器方法支持的方法參數(shù)和返回類型
1. 支持的方法參數(shù)類型
(1 )HttpServlet 對象,主要包括HttpServletRequest 、HttpServletResponse 和HttpSession 對象。 這些參數(shù)Spring 在調(diào)用處理器方法的時候會自動給它們賦值,所以當(dāng)在處理器方法中需要使用到這些對象的時候,可以直接在方法上給定一個方法參數(shù)的申明,然后在方法體里面直接用就可以了。但是有一點(diǎn)需要注意的是在使用HttpSession 對象的時候,如果此時HttpSession 對象還沒有建立起來的話就會有問題。
(2 )Spring 自己的WebRequest 對象。 使用該對象可以訪問到存放在HttpServletRequest 和HttpSession 中的屬性值。
(3 )InputStream 、OutputStream 、Reader 和Writer 。 InputStream 和Reader 是針對HttpServletRequest 而言的,可以從里面取數(shù)據(jù);OutputStream 和Writer 是針對HttpServletResponse 而言的,可以往里面寫數(shù)據(jù)。
(4 )使用@PathVariable 、@RequestParam 、@CookieValue 和@RequestHeader 標(biāo)記的參數(shù)。
(5 )使用@ModelAttribute 標(biāo)記的參數(shù)。
(6 )java.util.Map 、Spring 封裝的Model 和ModelMap 。 這些都可以用來封裝模型數(shù)據(jù),用來給視圖做展示。
(7 )實(shí)體類。 可以用來接收上傳的參數(shù)。
(8 )Spring 封裝的MultipartFile 。 用來接收上傳文件的。
(9 )Spring 封裝的Errors 和BindingResult 對象。 這兩個對象參數(shù)必須緊接在需要驗(yàn)證的實(shí)體對象參數(shù)之后,它里面包含了實(shí)體對象的驗(yàn)證結(jié)果。
2. 支持的返回類型
(1 )一個包含模型和視圖的ModelAndView 對象。
(2 )一個模型對象,這主要包括Spring 封裝好的Model 和ModelMap ,以及java.util.Map ,當(dāng)沒有視圖返回的時候視圖名稱將由RequestToViewNameTranslator 來決定。
(3 )一個View 對象。這個時候如果在渲染視圖的過程中模型的話就可以給處理器方法定義一個模型參數(shù),然后在方法體里面往模型中添加值。
(4 )一個String 字符串。這往往代表的是一個視圖名稱。這個時候如果需要在渲染視圖的過程中需要模型的話就可以給處理器方法一個模型參數(shù),然后在方法體里面往模型中添加值就可以了。
(5 )返回值是void 。這種情況一般是我們直接把返回結(jié)果寫到HttpServletResponse 中了,如果沒有寫的話,那么Spring 將會利用RequestToViewNameTranslator 來返回一個對應(yīng)的視圖名稱。如果視圖中需要模型的話,處理方法與返回字符串的情況相同。
(6 )如果處理器方法被注解@ResponseBody 標(biāo)記的話,那么處理器方法的任何返回類型都會通過HttpMessageConverters 轉(zhuǎn)換之后寫到HttpServletResponse 中,而不會像上面的那些情況一樣當(dāng)做視圖或者模型來處理。
(7 )除以上幾種情況之外的其他任何返回類型都會被當(dāng)做模型中的一個屬性來處理,而返回的視圖還是由RequestToViewNameTranslator 來決定,添加到模型中的屬性名稱可以在該方法上用@ModelAttribute(“attributeName”) 來定義,否則將使用返回類型的類名稱的首字母小寫形式來表示。使用@ModelAttribute 標(biāo)記的方法會在@RequestMapping 標(biāo)記的方法執(zhí)行之前執(zhí)行。
(七)使用 @ModelAttribute 和 @SessionAttributes 傳遞和保存數(shù)據(jù)
SpringMVC 支持使用 @ModelAttribute 和 @SessionAttributes 在不同的模型和控制器之間共享數(shù)據(jù)。 @ModelAttribute 主要有兩種使用方式,一種是標(biāo)注在方法上,一種是標(biāo)注在 Controller 方法參數(shù)上。
當(dāng) @ModelAttribute 標(biāo)記在方法上的時候,該方法將在處理器方法執(zhí)行之前執(zhí)行,然后把返回的對象存放在 session 或模型屬性中,屬性名稱可以使用 @ModelAttribute(“attributeName”) 在標(biāo)記方法的時候指定,若未指定,則使用返回類型的類名稱(首字母小寫)作為屬性名稱。關(guān)于 @ModelAttribute 標(biāo)記在方法上時對應(yīng)的屬性是存放在 session 中還是存放在模型中,我們來做一個實(shí)驗(yàn),看下面一段代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
@Controller @RequestMapping ( "/myTest" ) public class MyController { @ModelAttribute ( "hello" ) public String getModel() { System. out .println( "-------------Hello---------" ); return "world" ; } @ModelAttribute ( "intValue" ) public int getInteger() { System. out .println( "-------------intValue---------------" ); return 10 ; } @RequestMapping ( "sayHello" ) public void sayHello( @ModelAttribute ( "hello" ) String hello, @ModelAttribute ( "intValue" ) int num, @ModelAttribute ( "user2" ) User user, Writer writer, HttpSession session) throws IOException { writer.write( "Hello " + hello + " , Hello " + user.getUsername() + num); writer.write( "\r" ); Enumeration enume = session.getAttributeNames(); while (enume.hasMoreElements()) writer.write(enume.nextElement() + "\r" ); } @ModelAttribute ( "user2" ) public User getUser() { System. out .println( "---------getUser-------------" ); return new User( 3 , "user2" ); } } |
當(dāng)我們請求 /myTest/sayHello.do 的時候使用 @ModelAttribute 標(biāo)記的方法會先執(zhí)行,然后把它們返回的對象存放到模型中。最終訪問到 sayHello 方法的時候,使用 @ModelAttribute 標(biāo)記的方法參數(shù)都能被正確的注入值。執(zhí)行結(jié)果如下所示:
Hello world,Hello user210
由執(zhí)行結(jié)果我們可以看出來,此時 session 中沒有包含任何屬性,也就是說上面的那些對象都是存放在模型屬性中,而不是存放在 session 屬性中。那要如何才能存放在 session 屬性中呢?這個時候我們先引入一個新的概念 @SessionAttributes ,它的用法會在講完 @ModelAttribute 之后介紹,這里我們就先拿來用一下。我們在 MyController 類上加上 @SessionAttributes 屬性標(biāo)記哪些是需要存放到 session 中的。看下面的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
@Controller @RequestMapping ( "/myTest" ) @SessionAttributes (value={ "intValue" , "stringValue" }, types={User. class }) public class MyController { @ModelAttribute ( "hello" ) public String getModel() { System. out .println( "-------------Hello---------" ); return "world" ; } @ModelAttribute ( "intValue" ) public int getInteger() { System. out .println( "-------------intValue---------------" ); return 10 ; } @RequestMapping ( "sayHello" ) public void sayHello(Map<String, Object> map, @ModelAttribute ( "hello" ) String hello, @ModelAttribute ( "intValue" ) int num, @ModelAttribute ( "user2" ) User user, Writer writer, HttpServletRequest request) throws IOException { map.put( "stringValue" , "String" ); writer.write( "Hello " + hello + " , Hello " + user.getUsername() + num); writer.write( "\r" ); HttpSession session = request.getSession(); Enumeration enume = session.getAttributeNames(); while (enume.hasMoreElements()) writer.write(enume.nextElement() + "\r" ); System. out .println(session); } @ModelAttribute ( "user2" ) public User getUser() { System. out .println( "---------getUser-------------" ); return new User( 3 , "user2" ); } } |
在上面代碼中我們指定了屬性為 intValue 或 stringValue 或者類型為 User 的都會放到 Session中,利用上面的代碼當(dāng)我們訪問 /myTest/sayHello.do 的時候,結(jié)果如下:
Hello world,Hello user210
仍然沒有打印出任何 session 屬性,這是怎么回事呢?怎么定義了把模型中屬性名為 intValue 的對象和類型為 User 的對象存到 session 中,而實(shí)際上沒有加進(jìn)去呢?難道我們錯啦?我們當(dāng)然沒有錯,只是在第一次訪問 /myTest/sayHello.do 的時候 @SessionAttributes 定義了需要存放到 session 中的屬性,而且這個模型中也有對應(yīng)的屬性,但是這個時候還沒有加到 session 中,所以 session 中不會有任何屬性,等處理器方法執(zhí)行完成后 Spring 才會把模型中對應(yīng)的屬性添加到 session 中。所以當(dāng)請求第二次的時候就會出現(xiàn)如下結(jié)果:
Hello world,Hello user210
user2
intValue
stringValue
當(dāng) @ModelAttribute 標(biāo)記在處理器方法參數(shù)上的時候,表示該參數(shù)的值將從模型或者 Session 中取對應(yīng)名稱的屬性值,該名稱可以通過 @ModelAttribute(“attributeName”) 來指定,若未指定,則使用參數(shù)類型的類名稱(首字母小寫)作為屬性名稱。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://www.cnblogs.com/xiepeixing/p/4243288.html