controller注解和restcontroller,@Controller深入詳解

 2023-11-19 阅读 23 评论 0

摘要:一、簡介 ?????????在SpringMVC?中,控制器Controller?負責處理由DispatcherServlet?分發的請求,它把用戶請求的數據經過業務處理層處理之后封裝成一個Model?,然后再把該Model?返回給對應的View?進行展示。在SpringMVC?中提供了一個非常簡便的定義Control

一、簡介

?????????在SpringMVC?中,控制器Controller?負責處理由DispatcherServlet?分發的請求,它把用戶請求的數據經過業務處理層處理之后封裝成一個Model?,然后再把該Model?返回給對應的View?進行展示。在SpringMVC?中提供了一個非常簡便的定義Controller?的方法,你無需繼承特定的類或實現特定的接口,只需使用@Controller?標記一個類是Controller?,然后使用@RequestMapping?和@RequestParam?等一些注解用以定義URL?請求和Controller?方法之間的映射,這樣的Controller?就能被外界訪問到。此外Controller?不會直接依賴于HttpServletRequest?和HttpServletResponse?等HttpServlet?對象,它們可以通過Controller?的方法參數靈活的獲取到。為了先對Controller?有一個初步的印象,以下先定義一個簡單的Controller?:

例1:

復制代碼
@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?控制器

例1所示@Controller?用于標記在一個類上,使用它標記的類就是一個SpringMVC Controller?對象。分發處理器將會掃描使用了該注解的類的方法,并檢測該方法是否使用了@RequestMapping?注解。@Controller?只是定義了一個控制器類,而使用@RequestMapping?注解的方法才是真正處理請求的處理器,這個接下來就會講到。

controller注解和restcontroller。???單單使用@Controller?標記在一個類上還不能真正意義上的說它就是SpringMVC?的一個控制器類,因為這個時候Spring?還不認識它。那么要如何做Spring?才能認識它呢?這個時候就需要我們把這個控制器類交給Spring?來管理。

這個時候有兩種方式可以把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 > 

注:

java controller????????上面?context:exclude-filter?標注的是不掃描?@Service?標注的類

三、使用?@RequestMapping?來映射?Request?請求與處理器

例1可以使用@RequestMapping?來映射URL?到控制器類,或者是到Controller?控制器的處理方法上。當@RequestMapping?標記在Controller?類上的時候,里面使用@RequestMapping?標記的方法的請求地址都是相對于類上的@RequestMapping?而言的;當Controller?類上沒有標記@RequestMapping?注解時,方法上的@RequestMapping?都是絕對路徑。這種絕對路徑和相對路徑所組合成的最終路徑都是相對于根路徑“/?”而言的。

在這個控制器中,因為MyController?沒有被@RequestMapping?標記,所以當需要訪問到里面使用了@RequestMapping?標記的showView?方法時,就是使用的絕對路徑/showView.do?請求就可以了。

例2

復制代碼
@Controller
@RequestMapping ( "/test" )
public class MyController {@RequestMapping ( "/showView" )public ModelAndView showView() {ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName( "viewName" );modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應的屬性值,它是一個對象 " );return modelAndView;}} 
復制代碼

例2是在控制器上加了@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?例3

復制代碼
@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;}
} 
復制代碼

在例3的代碼中我們定義了兩個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?請求參數到控制器方法參數

例4

    @RequestMapping ( "requestParam" )public String testRequestParam( @RequestParam(required=false) String name, @RequestParam ( "age" ) int age) {return "requestParam" ;} 

mvc controller?在上面代碼中利用@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?是非必須的,不傳遞也可以。

(三)使用?@CookieValue?綁定?cookie?的值到?Controller?方法參數

例5

復制代碼
    @RequestMapping ( "cookieValue" )public String testCookieValue( @CookieValue ( "hello" ) String cookieValue, @CookieValue String hello) {System. out .println(cookieValue + "-----------" + hello);return "cookieValue" ;} 
復制代碼

在上面的代碼中我們使用@CookieValue?綁定了cookie?的值到方法參數上。上面一共綁定了兩個參數,一個是明確指定要綁定的是名稱為hello?的cookie?的值,一個是沒有指定。使用沒有指定的形式的規則和@PathVariable、@RequestParam?的規則是一樣的,即在debug?編譯模式下將自動獲取跟方法參數名同名的cookie?值。

(四)使用?@RequestHeader?注解綁定?HttpServletRequest?頭信息到Controller?方法參數

例6

復制代碼
@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?的方法參數。上面方法的三個參數都將會賦予同一個值,由此我們可以知道在綁定請求頭參數到方法參數的時候規則和?@PathVariable?、?@RequestParam?以及?@CookieValue?是一樣的,即沒有指定綁定哪個參數到方法參數的時候,在?debug?編譯模式下將使用方法參數名作為需要綁定的參數。但是有一點?@RequestHeader?跟另外三種綁定方式是不一樣的,那就是在使用?@RequestHeader?的時候是大小寫不敏感的,即?@RequestHeader(“Host”)?和?@RequestHeader(“host”)?綁定的都是?Host?頭信息。記住在?@PathVariable?、?@RequestParam?和?@CookieValue?中都是大小寫敏感的。

(五)?@RequestMapping?的一些高級應用

controller可以調用controller嗎?在RequestMapping?中除了指定請求路徑value?屬性外,還有其他的屬性可以指定,如params?、method?和headers?。這樣屬性都可以用于縮小請求的映射范圍。

1.params屬性

例7

復制代碼
    @RequestMapping (value= "testParams" , params={ "param1=value1" , "param2" , "!param3" })public String testParams() {System. out .println( "test Params..........." );return "testParams" ;} 
復制代碼

在上面的代碼中我們用@RequestMapping?的params?屬性指定了三個參數,這些參數都是針對請求參數而言的,它們分別表示參數param1?的值必須等于value1?,參數param2?必須存在,值無所謂,參數param3?必須不存在,只有當請求/testParams.do?并且滿足指定的三個參數條件的時候才能訪問到該方法。所以當請求/testParams.do?param1=value1&param2=value2?的時候能夠正確訪問到該testParams?方法,當請求/testParams.do?param1=value1&param2=value2&param3=value3?的時候就不能夠正常的訪問到該方法,因為在@RequestMapping?的params?參數里面指定了參數param3?是不能存在的。

2.method屬性

例8

    @RequestMapping (value= "testMethod" , method={RequestMethod. GET , RequestMethod. DELETE })public String testMethod() {return "method" ;} 

在上面的代碼中就使用method?參數限制了以GET?或DELETE?方法請求/testMethod.do?的時候才能訪問到該Controller?的testMethod?方法。

3.headers屬性

例9

    @RequestMapping (value= "testHeaders" , headers={ "host=localhost" , "Accept" })public String testHeaders() {return "headers" ;} 

java中controller的作用。headers?屬性的用法和功能與params?屬性相似。在上面的代碼中當請求/testHeaders.do?的時候只有當請求頭包含Accept?信息,且請求的host?為localhost?的時候才能正確的訪問到testHeaders?方法。

(六)以?@RequestMapping?標記的處理器方法支持的方法參數和返回類型

1.?支持的方法參數類型

?????????(1?)HttpServlet?對象,主要包括HttpServletRequest?、HttpServletResponse?和HttpSession?對象。?這些參數Spring?在調用處理器方法的時候會自動給它們賦值,所以當在處理器方法中需要使用到這些對象的時候,可以直接在方法上給定一個方法參數的申明,然后在方法體里面直接用就可以了。但是有一點需要注意的是在使用HttpSession?對象的時候,如果此時HttpSession?對象還沒有建立起來的話就會有問題。

???(2?)Spring?自己的WebRequest?對象。?使用該對象可以訪問到存放在HttpServletRequest?和HttpSession?中的屬性值。

???(3?)InputStream?、OutputStream?、Reader?和Writer?。?InputStream?和Reader?是針對HttpServletRequest?而言的,可以從里面取數據;OutputStream?和Writer?是針對HttpServletResponse?而言的,可以往里面寫數據。

???(4?)使用@PathVariable?、@RequestParam?、@CookieValue?和@RequestHeader?標記的參數。

???(5?)使用@ModelAttribute?標記的參數。

controller傳值到頁面。???(6?)java.util.Map?、Spring?封裝的Model?和ModelMap?。?這些都可以用來封裝模型數據,用來給視圖做展示。

???(7?)實體類。?可以用來接收上傳的參數。

???(8?)Spring?封裝的MultipartFile?。?用來接收上傳文件的。

???(9?)Spring?封裝的Errors?和BindingResult?對象。?這兩個對象參數必須緊接在需要驗證的實體對象參數之后,它里面包含了實體對象的驗證結果。

2.?支持的返回類型

?????????(1?)一個包含模型和視圖的ModelAndView?對象。

???(2?)一個模型對象,這主要包括Spring?封裝好的Model?和ModelMap?,以及java.util.Map?,當沒有視圖返回的時候視圖名稱將由RequestToViewNameTranslator?來決定。

CONTROLLER、???(3?)一個View?對象。這個時候如果在渲染視圖的過程中模型的話就可以給處理器方法定義一個模型參數,然后在方法體里面往模型中添加值。

???(4?)一個String?字符串。這往往代表的是一個視圖名稱。這個時候如果需要在渲染視圖的過程中需要模型的話就可以給處理器方法一個模型參數,然后在方法體里面往模型中添加值就可以了。

???(5?)返回值是void?。這種情況一般是我們直接把返回結果寫到HttpServletResponse?中了,如果沒有寫的話,那么Spring?將會利用RequestToViewNameTranslator?來返回一個對應的視圖名稱。如果視圖中需要模型的話,處理方法與返回字符串的情況相同。

???(6?)如果處理器方法被注解@ResponseBody?標記的話,那么處理器方法的任何返回類型都會通過HttpMessageConverters?轉換之后寫到HttpServletResponse?中,而不會像上面的那些情況一樣當做視圖或者模型來處理。

???(7?)除以上幾種情況之外的其他任何返回類型都會被當做模型中的一個屬性來處理,而返回的視圖還是由RequestToViewNameTranslator?來決定,添加到模型中的屬性名稱可以在該方法上用@ModelAttribute(“attributeName”)?來定義,否則將使用返回類型的類名稱的首字母小寫形式來表示。使用@ModelAttribute?標記的方法會在@RequestMapping?標記的方法執行之前執行。

(七)使用?@ModelAttribute?和?@SessionAttributes?傳遞和保存數據

SpringMVC?支持使用?@ModelAttribute?和?@SessionAttributes?在不同的模型和控制器之間共享數據。?@ModelAttribute?主要有兩種使用方式,一種是標注在方法上,一種是標注在?Controller?方法參數上。

當?@ModelAttribute?標記在方法上的時候,該方法將在處理器方法執行之前執行,然后把返回的對象存放在?session?或模型屬性中,屬性名稱可以使用?@ModelAttribute(“attributeName”)?在標記方法的時候指定,若未指定,則使用返回類型的類名稱(首字母小寫)作為屬性名稱。關于?@ModelAttribute?標記在方法上時對應的屬性是存放在?session?中還是存放在模型中,我們來做一個實驗,看下面一段代碼。

復制代碼
@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" );}
} 
復制代碼

當我們請求?/myTest/sayHello.do?的時候使用?@ModelAttribute?標記的方法會先執行,然后把它們返回的對象存放到模型中。最終訪問到?sayHello?方法的時候,使用?@ModelAttribute?標記的方法參數都能被正確的注入值。執行結果如下所示:

?Hello world,Hello user210

???????由執行結果我們可以看出來,此時?session?中沒有包含任何屬性,也就是說上面的那些對象都是存放在模型屬性中,而不是存放在?session?屬性中。那要如何才能存放在?session?屬性中呢?這個時候我們先引入一個新的概念?@SessionAttributes?,它的用法會在講完?@ModelAttribute?之后介紹,這里我們就先拿來用一下。我們在?MyController?類上加上?@SessionAttributes?屬性標記哪些是需要存放到?session?中的。看下面的代碼:

復制代碼
@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中,利用上面的代碼當我們訪問?/myTest/sayHello.do?的時候,結果如下:

?Hello world,Hello user210

仍然沒有打印出任何?session?屬性,這是怎么回事呢?怎么定義了把模型中屬性名為?intValue?的對象和類型為?User?的對象存到?session?中,而實際上沒有加進去呢?難道我們錯啦?我們當然沒有錯,只是在第一次訪問?/myTest/sayHello.do?的時候?@SessionAttributes?定義了需要存放到?session?中的屬性,而且這個模型中也有對應的屬性,但是這個時候還沒有加到?session?中,所以?session?中不會有任何屬性,等處理器方法執行完成后?Spring?才會把模型中對應的屬性添加到?session?中。所以當請求第二次的時候就會出現如下結果:

轉載于:https://www.cnblogs.com/hualishu/p/7398881.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/181546.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息