springmvc 註解/數據綁定

springmvc入門基礎之註解和參數傳遞

Bean Validation 1.1(JSR-349)到SpringMVC

到目前爲止,請求已經能交給我們的處理器進行處理了,接下來的事情是要進行收集數據啦,接下來我們看看我們能從請求中收集到哪些數據,如圖6-11:



 圖6-11

1、@RequestParam綁定單個請求參數值;

2、@PathVariable綁定URI模板變量值;

3、@CookieValue綁定Cookie數據值

4、@RequestHeader綁定請求頭數據;

5、@ModelValue綁定參數到命令對象;

6、@SessionAttributes綁定命令對象到session;

7、@RequestBody綁定請求的內容區數據並能進行自動類型轉換等。

8、@RequestPart綁定“multipart/data”數據,除了能綁定@RequestParam能做到的請求參數外,還能綁定上傳的文件等。

 

除了上邊提到的註解,我們還可以通過如HttpServletRequest等API得到請求數據,但推薦使用註解方式,因爲使用起來更簡單。

 

接下來先看一下功能處理方法支持的參數類型吧。

6.6.0、功能處理方法支持的參數類型

在繼續學習之前,我們需要首先看看功能處理方法支持哪些類型的形式參數,以及他們的具體含義。

 一、ServletRequest/HttpServletRequest 和 ServletResponse/HttpServletResponse

Java代碼  收藏代碼
  1. public String requestOrResponse (  
  2.         ServletRequest servletRequest, HttpServletRequest httpServletRequest,  
  3.         ServletResponse servletResponse, HttpServletResponse httpServletResponse  
  4.     )  
 Spring Web MVC框架會自動幫助我們把相應的Servlet請求/響應(Servlet API)作爲參數傳遞過來。

 

二、InputStream/OutputStream 和 Reader/Writer

Java代碼  收藏代碼
  1. public void inputOrOutBody(InputStream requestBodyIn, OutputStream responseBodyOut)  
  2.         throws IOException {  
  3. responseBodyOut.write("success".getBytes());  
  4. }  
requestBodyIn獲取請求的內容區字節流,等價於request.getInputStream();

responseBodyOut獲取相應的內容區字節流,等價於response.getOutputStream()。

 

Java代碼  收藏代碼
  1. public void readerOrWriteBody(Reader reader, Writer writer)  
  2.         throws IOException {  
  3.     writer.write("hello");  
  4. }  
 reader獲取請求的內容區字符流,等價於request.getReader();

writer獲取相應的內容區字符流,等價於response.getWriter()。

 

InputStream/OutputStream 和 Reader/Writer兩組不能同時使用,只能使用其中的一組。

 

三、WebRequest/NativeWebRequest

WebRequest是Spring Web MVC提供的統一請求訪問接口,不僅僅可以訪問請求相關數據(如參數區數據、請求頭數據,但訪問不到Cookie區數據),還可以訪問會話和上下文中的數據;NativeWebRequest繼承了WebRequest,並提供訪問本地Servlet API的方法。

Java代碼  收藏代碼
  1. public String webRequest(WebRequest webRequest, NativeWebRequest nativeWebRequest) {  
  2.     System.out.println(webRequest.getParameter("test"));//①得到請求參數test的值  
  3.     webRequest.setAttribute("name""value", WebRequest.SCOPE_REQUEST);//②  
  4.     System.out.println(webRequest.getAttribute("name", WebRequest.SCOPE_REQUEST));  
  5.     HttpServletRequest request =   
  6.         nativeWebRequest.getNativeRequest(HttpServletRequest.class);//③  
  7.     HttpServletResponse response =   
  8.         nativeWebRequest.getNativeResponse(HttpServletResponse.class);  
  9.         return "success";  
  10.     }  
  webRequest.getParameter:訪問請求參數區的數據,可以通過getHeader()訪問請求頭數據;

② webRequest.setAttribute/getAttribute:到指定的作用範圍內取/放屬性數據,Servlet定義的三個作用範圍分別使用如下常量代表:

            SCOPE_REQUEST :代表請求作用範圍;

           SCOPE_SESSION :代表會話作用範圍;

           SCOPE_GLOBAL_SESSION :代表全局會話作用範圍,即ServletContext上下文作用範圍。 

 nativeWebRequest.getNativeRequest/nativeWebRequest.getNativeResponse:得到本地的Servlet API。

 

四、HttpSession

Java代碼  收藏代碼
  1. public String session(HttpSession session) {  
  2.     System.out.println(session);  
  3.     return "success";  
  4. }  
 此處的session永遠不爲null。

 

注意:session訪問不是線程安全的,如果需要線程安全,需要設置AnnotationMethodHandlerAdapter或RequestMappingHandlerAdapter的synchronizeOnSession屬性爲true,即可線程安全的訪問session。

 

五、命令/表單對象

Spring Web MVC能夠自動將請求參數綁定到功能處理方法的命令/表單對象上。

Java代碼  收藏代碼
  1. @RequestMapping(value = "/commandObject", method = RequestMethod.GET)  
  2. public String toCreateUser(HttpServletRequest request, UserModel user) {  
  3.     return "customer/create";  
  4. }  
  5. @RequestMapping(value = "/commandObject", method = RequestMethod.POST)  
  6. public String createUser(HttpServletRequest request, UserModel user) {  
  7.     System.out.println(user);  
  8.     return "success";  
  9. }  
 如果提交的表單(包含username和password文本域),將自動將請求參數綁定到命令對象user中去。

 

六、Model、Map、ModelMap

Spring Web MVC 提供Model、Map或ModelMap讓我們能去暴露渲染視圖需要的模型數據。

Java代碼  收藏代碼
  1. @RequestMapping(value = "/model")  
  2. public String createUser(Model model, Map model2, ModelMap model3) {  
  3.     model.addAttribute("a""a");  
  4.     model2.put("b""b");  
  5.     model3.put("c""c");  
  6.     System.out.println(model == model2);  
  7.     System.out.println(model2 == model3);  
  8.     return "success";}  

 雖然此處注入的是三個不同的類型(Model model, Map model2, ModelMap model3),但三者是同一個對象,如圖6-12所示:



6-11

AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter將使用BindingAwareModelMap作爲模型對象的實現,即此處我們的形參(Model model, Map model2, ModelMap model3)都是同一個BindingAwareModelMap實例。

 

此處還有一點需要我們注意:

Java代碼  收藏代碼
  1. @RequestMapping(value = "/mergeModel")  
  2. public ModelAndView mergeModel(Model model) {  
  3.     model.addAttribute("a""a");//①添加模型數據  
  4.     ModelAndView mv = new ModelAndView("success");  
  5.     mv.addObject("a""update");//②在視圖渲染之前更新③處同名模型數據  
  6.     model.addAttribute("a""new");//③修改①處同名模型數據  
  7.     //視圖頁面的a將顯示爲"update" 而不是"new"  
  8.     return mv;  
  9. }  
 從代碼中我們可以總結出功能處理方法的返回值中的模型數據(如ModelAndView)會 合併 功能處理方法形式參數中的模型數據(如Model),但如果兩者之間有同名的,返回值中的模型數據會覆蓋形式參數中的模型數據。

 

七、Errors/BindingResult

Java代碼  收藏代碼
  1. @RequestMapping(value = "/error1")  
  2. public String error1(UserModel user, BindingResult result)  

 

Java代碼  收藏代碼
  1. @RequestMapping(value = "/error2")  
  2. public String error2(UserModel user, BindingResult result, Model model) {  
  3.       

 

Java代碼  收藏代碼
  1. @RequestMapping(value = "/error3")  
  2. public String error3(UserModel user, Errors errors)   

 

以上代碼都能獲取錯誤對象。

 

Spring3.1之前(使用AnnotationMethodHandlerAdapter)錯誤對象必須緊跟在命令對象/表單對象之後,如下定義是錯誤的:

Java代碼  收藏代碼
  1. @RequestMapping(value = "/error4")  
  2. public String error4(UserModel user, Model model, Errors errors)  
  3.     }  
如上代碼從Spring3.1開始(使用RequestMappingHandlerAdapter)將能正常工作,但還是推薦“錯誤對象緊跟在命令對象/表單對象之後”,這樣是萬無一失的。

 

Errors及BindingResult的詳細使用請參考4.16.2數據驗證。

 

八、HttpHeaders

1.在方法參數中加入@RequestHeader 
2.在類級別注入HttpServletRequest 
建議使用第二種方法,這樣可避免每個方法都加入HttpHeaders參數 

  1. @Controller  
  2. @RequestMapping("/hello")  
  3. public class HelloController {  
  4.      @Autowired  
  5.      private HttpServletRequest request;  
  6.       
  7.      @RequestMapping(value="/printname/{name}", method=RequestMethod.GET)  
  8.      public String printName(@PathVariable String name,  
  9.               @RequestHeader HttpHeaders headers) {  
  10.           System.out.println("from request:" + request.getHeader("code"));  
  11.           System.out.println("from parameter:" + headers.getFirst("code"));  
  12.            
  13.           return "hello";  
  14.      }  
  15. }  

九、其他雜項

Java代碼  收藏代碼
  1. public String other(Locale locale, Principal principal)  

 java.util.Locale:

得到當前請求的本地化信息,默認等價於ServletRequest.getLocale(),如果配置LocaleResolver解析器則由它決定Locale,後續介紹;

java.security.Principal

該主體對象包含了驗證通過的用戶信息,等價於HttpServletRequest.getUserPrincipal()。

 

以上測試在cn.javass.chapter6.web.controller.paramtype.MethodParamTypeController中。

 

其他功能處理方法的形式參數類型(如HttpEntity、UriComponentsBuilder、SessionStatus、RedirectAttributes)將在後續章節詳細講解。

6.6.1、 RequestMapping 用法詳解之地址映射

@RequestMapping

RequestMapping是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作爲父路徑。

RequestMapping註解有六個屬性,下面我們把她分成三類進行說明。

1、 value, method;

value:     指定請求的實際地址,指定的地址可以是URI Template 模式(後面將會說明);

method:  指定請求的method類型, GET、POST、PUT、DELETE等;

2、 consumes,produces;

consumes: 指定處理請求的提交內容類型(Content-Type),例如application/json, text/html;

produces:    指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;

3、 params,headers;

params: 指定request中必須包含某些參數值是,才讓該方法處理。

headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求。


示例:

1、value  / method 示例

默認RequestMapping("....str...")即爲value的值;

[java] view plaincopy
  1. @Controller  
  2. @RequestMapping("/appointments" 
  3. public class AppointmentsController  
  4.   
  5.     private final AppointmentBook appointmentBook;  
  6.       
  7.     @Autowired  
  8.     public AppointmentsController(AppointmentBook appointmentBook)  
  9.         this.appointmentBook appointmentBook;  
  10.      
  11.   
  12.     @RequestMapping(method RequestMethod.GET)  
  13.     public Map get()  
  14.         return appointmentBook.getAppointmentsForToday();  
  15.      
  16.   
  17.     @RequestMapping(value="/{day}"method RequestMethod.GET)  
  18.     public Map getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model)  
  19.         return appointmentBook.getAppointmentsForDay(day);  
  20.      
  21.   
  22.     @RequestMapping(value="/new"method RequestMethod.GET)  
  23.     public AppointmentForm getNewForm()  
  24.         return new AppointmentForm();  
  25.      
  26.   
  27.     @RequestMapping(method RequestMethod.POST)  
  28.     public String add(@Valid AppointmentForm appointment, BindingResult result)  
  29.         if (result.hasErrors())  
  30.             return "appointments/new" 
  31.          
  32.         appointmentBook.addAppointment(appointment);  
  33.         return "redirect:/appointments" 
  34.      
  35.  

value的uri值爲以下三類:

A) 可以指定爲普通的具體值;

B)  可以指定爲含有某變量的一類值(URI Template Patterns with Path Variables);

C) 可以指定爲含正則表達式的一類值( URI Template Patterns with Regular Expressions);


example B)

[java] view plaincopy
  1. @RequestMapping(value="/owners/{ownerId}"method=RequestMethod.GET)  
  2. public String findOwner(@PathVariable String ownerId, Model model)  
  3.   Owner owner ownerService.findOwner(ownerId);    
  4.   model.addAttribute("owner"owner);    
  5.   return "displayOwner"  
  6.  

example C)
[java] view plaincopy
  1. @RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\d\.\d\.\d}.{extension:\.[a-z]}" 
  2.   public void handle(@PathVariable String version, @PathVariable String extension)      
  3.     // ...  
  4.    
  5.  

2 consumes / produces 示例

cousumes的樣例:

[java] view plaincopy
  1. @Controller  
  2. @RequestMapping(value "/pets"method RequestMethod.POST, consumes="application/json" 
  3. public void addPet(@RequestBody Pet pet, Model model)      
  4.     // implementation omitted  
  5.  
方法僅處理request Content-Type爲“application/json”類型的請求。

produces的樣例:

[java] view plaincopy
  1. @Controller  
  2. @RequestMapping(value "/pets/{petId}"method RequestMethod.GET, produces="application/json" 
  3. @ResponseBody  
  4. public Pet getPet(@PathVariable String petId, Model model)      
  5.     // implementation omitted  
  6.  

方法僅處理request請求中Accept頭中包含了"application/json"的請求,同時暗示了返回的內容類型爲application/json;

params / headers 示例

params的樣例:

[java] view plaincopy
  1. @Controller  
  2. @RequestMapping("/owners/{ownerId}" 
  3. public class RelativePathUriTemplateController  
  4.   
  5.   @RequestMapping(value "/pets/{petId}"method RequestMethod.GET, params="myParam=myValue" 
  6.   public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model)      
  7.     // implementation omitted  
  8.    
  9.  

 僅處理請求中包含了名爲“myParam”,值爲“myValue”的請求;


headers的樣例:

[java] view plaincopy
  1. @Controller  
  2. @RequestMapping("/owners/{ownerId}" 
  3. public class RelativePathUriTemplateController  
  4.   
  5. @RequestMapping(value "/pets"method RequestMethod.GET, headers="Referer=http://www.ifeng.com/" 
  6.   public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model)      
  7.     // implementation omitted  
  8.    
  9.  

 僅處理request的header中包含了指定“Refer”請求頭和對應值爲“http://www.ifeng.com/”的請求;

上面僅僅介紹了,RequestMapping指定的方法處理哪些請求,下面一篇將講解怎樣處理request提交的數據(數據綁定)和返回的數據。


6.6.2、@RequestParam綁定單個請求參數值

@RequestParam用於將請求參數區數據映射到功能處理方法的參數上。

Java代碼  收藏代碼
  1. public String requestparam1(@RequestParam String username)  

請求中包含username參數(如/requestparam1?username=zhang),則自動傳入。

 

此處要特別注意:右擊項目,選擇“屬性”,打開“屬性對話框”,選擇“Java Compiler”然後再打開的選項卡將“Add variable attributes to generated class files”取消勾選,意思是不將局部變量信息添加到類文件中,如圖6-12所示:



 圖6-12

當你在瀏覽器輸入URL,如“requestparam1?username=123”時會報如下錯誤

Name for argument type [java.lang.String] not available, and parameter name information not found in class file either,表示得不到功能處理方法的參數名,此時我們需要如下方法進行入參:

 

Java代碼  收藏代碼
  1. public String requestparam2(@RequestParam("username") String username)  

 即通過@RequestParam("username")明確告訴Spring Web MVC使用username進行入參。

 

 

接下來我們看一下@RequestParam註解主要有哪些參數:

value:參數名字,即入參的請求參數名字,如username表示請求的參數區中的名字爲username的參數的值將傳入;

required:是否必須,默認是true,表示請求中一定要有相應的參數,否則將報404錯誤碼;

defaultValue:默認值,表示如果請求中沒有同名參數時的默認值,默認值可以是SpEL表達式,如“#{systemProperties['java.vm.version']}”。

 

Java代碼  收藏代碼
  1. public String requestparam4(@RequestParam(value="username",required=false) String username)   

 表示請求中可以沒有名字爲username的參數,如果沒有默認爲null,此處需要注意如下幾點:

 

     原子類型:必須有值,否則拋出異常,如果允許空值請使用包裝類代替。

     Boolean包裝類型類型:默認Boolean.FALSE,其他引用類型默認爲null。

 

Java代碼  收藏代碼
  1. public String requestparam5(  
  2. @RequestParam(value="username", required=true, defaultValue="zhang") String username)   
  3.           

表示如果請求中沒有名字爲username的參數,默認值爲“zhang”。

 

 

如果請求中有多個同名的應該如何接收呢?如給用戶授權時,可能授予多個權限,首先看下如下代碼:

Java代碼  收藏代碼
  1. public String requestparam7(@RequestParam(value="role") String roleList)  

如果請求參數類似於url?role=admin&rule=user,則實際roleList參數入參的數據爲“admin,user”,即多個數據之間使用“,”分割;我們應該使用如下方式來接收多個請求參數:

Java代碼  收藏代碼
  1. public String requestparam7(@RequestParam(value="role") String[] roleList)     

 

Java代碼  收藏代碼
  1. public String requestparam8(@RequestParam(value="list") List<String> list)      

 到此@RequestParam我們就介紹完了,以上測試代碼在cn.javass.chapter6.web.controller. paramtype.RequestParamTypeController中。

 

6.6.3、@PathVariable綁定URI模板變量值

@PathVariable用於將請求URL中的模板變量映射到功能處理方法的參數上。

 

Java代碼  收藏代碼
  1. @RequestMapping(value="/users/{userId}/topics/{topicId}")  
  2. public String test(  
  3.        @PathVariable(value="userId"int userId,   
  4.        @PathVariable(value="topicId"int topicId)        

 如請求的URL爲“控制器URL/users/123/topics/456”,則自動將URL中模板變量{userId}和{topicId}綁定到通過@PathVariable註解的同名參數上,即入參後userId=123、topicId=456。代碼在PathVariableTypeController中。

 留意到spring mvc 3.1中 @PathVariable的兩個增強,其中: 

1)

Java代碼  收藏代碼
  1. @RequestMapping("/people/{firstName}/{lastName}/SSN")  
  2. public String find(Person person,  
  3.                    @PathVariable String firstName,  
  4.                    @PathVariable String lastName) {  
  5.     person.setFirstName(firstName);  
  6.     person.setLastName(lastName);  
  7.     
  8. }  


  這個在舊的版本中,要強制進行set,在新版本中,不用這樣set了,可以這樣: 
Java代碼  收藏代碼
  1. @RequestMapping("/people/{firstName}/{lastName}/SSN")  
  2. public String search(Person person) {  
  3.    // person.getFirstName() and person.getLastName() are populated  
  4.      
  5. }  


   

2) redirect中也可以和@PathVariable中一致了,原來的 
 
Java代碼  收藏代碼
  1. @RequestMapping(  
  2.     value="/groups/{group}/events/{year}/{month}/{slug}/rooms",  
  3.     method=RequestMethod.POST)  
  4. public String createRoom(  
  5.     @PathVariable String group, @PathVariable Integer year,  
  6.     @PathVariable Integer month, @PathVariable String slug) {  
  7.     // ...  
  8.     return "redirect:/groups/" + group + "/events/" + year + "/" + month + "/" + slug;  
  9. }  


  現在: 
 
Java代碼  收藏代碼
  1. @RequestMapping(  
  2.     value="/groups/{group}/events/{year}/{month}/{slug}/rooms",  
  3.     method=RequestMethod.POST)  
  4. public String createRoom(  
  5.     @PathVariable String group, @PathVariable Integer year,  
  6.     @PathVariable Integer month, @PathVariable String slug) {  
  7.     // ...  
  8.     return "redirect:/groups/{group}/events/{year}/{month}/{slug}";  
  9. }  

6.6.4、@CookieValue綁定Cookie數據值

@CookieValue用於將請求的Cookie數據映射到功能處理方法的參數上。

 

Java代碼  收藏代碼
  1. public String test(@CookieValue(value="JSESSIONID", defaultValue="") String sessionId)   

 

如上配置將自動將JSESSIONID值入參到sessionId參數上,defaultValue表示Cookie中沒有JSESSIONID時默認爲空。

Java代碼  收藏代碼
  1. public String test2(@CookieValue(value="JSESSIONID", defaultValue="") Cookie sessionId)         

傳入參數類型也可以是javax.servlet.http.Cookie類型。

 

測試代碼在CookieValueTypeController中。@CookieValue也擁有和@RequestParam相同的三個參數,含義一樣。

6.6.5、@RequestHeader綁定請求頭數據

@RequestHeader用於將請求的頭信息區數據映射到功能處理方法的參數上。

Java代碼  收藏代碼
  1. @RequestMapping(value="/header")  
  2. public String test(  
  3.        @RequestHeader("User-Agent") String userAgent,  
  4.        @RequestHeader(value="Accept") String[] accepts)  
  5.           

如上配置將自動將請求頭“User-Agent”值入參到userAgent參數上,並將“Accept”請求頭值入參到accepts參數上。測試代碼在HeaderValueTypeController中。

 

@RequestHeader也擁有和@RequestParam相同的三個參數,含義一樣。

6.6.6、@ModelAttribute綁定請求參數到命令對象

@ModelAttribute一個具有如下三個作用:

①綁定請求參數到命令對象:放在功能處理方法的入參上時,用於將多個請求參數綁定到一個命令對象,從而簡化綁定流程,而且自動暴露爲模型數據用於視圖頁面展示時使用;

②暴露表單引用對象爲模型數據:放在處理器的一般方法(非功能處理方法)上時,是爲表單準備要展示的表單引用對象,如註冊時需要選擇的所在城市等,而且在執行功能處理方法(@RequestMapping註解的方法)之前,自動添加到模型對象中,用於視圖頁面展示時使用;

③暴露@RequestMapping方法返回值爲模型數據:放在功能處理方法的返回值上時,是暴露功能處理方法的返回值爲模型數據,用於視圖頁面展示時使用。

 

一、綁定請求參數到命令對象

如用戶登錄,我們需要捕獲用戶登錄的請求參數(用戶名、密碼)並封裝爲用戶對象,此時我們可以使用@ModelAttribute綁定多個請求參數到我們的命令對象。

 

Java代碼  收藏代碼
  1. public String test1(@ModelAttribute("user") UserModel user)  

和6.6.1一節中的五、命令/表單對象功能一樣。只是此處多了一個註解@ModelAttribute("user"),它的作用是將該綁定的命令對象以“user”爲名稱添加到模型對象中供視圖頁面展示使用。我們此時可以在視圖頁面使用${user.username}來獲取綁定的命令對象的屬性。

 

綁定請求參數到命令對象支持對象圖導航式的綁定,如請求參數包含“?username=zhang&password=123&workInfo.city=bj”自動綁定到user中的workInfo屬性的city屬性中。

Java代碼  收藏代碼
  1. @RequestMapping(value="/model2/{username}")  
  2. public String test2(@ModelAttribute("model") DataBinderTestModel model) {   

DataBinderTestModel相關模型請從第三章拷貝過來,請求參數到命令對象的綁定規則詳見【4.16.1、數據綁定】一節,URI模板變量也能自動綁定到命令對象中,當你請求的URL中包含“bool=yes&schooInfo.specialty=computer&hobbyList[0]=program&hobbyList[1]=music&map[key1]=value1&map[key2]=value2&state=blocked”會自動綁定到命令對象上。

 

URI模板變量和請求參數同名時,URI模板變量具有高優先權。

 

二、暴露表單引用對象爲模型數據

Java代碼  收藏代碼
  1. @ModelAttribute("cityList")  
  2. public List<String> cityList() {  
  3.     return Arrays.asList("北京""山東");  
  4. }   

如上代碼會在執行功能處理方法之前執行,並將其自動添加到模型對象中,在功能處理方法中調用Model 入參的containsAttribute("cityList")將會返回true。

Java代碼  收藏代碼
  1. @ModelAttribute("user")  //①  
  2. public UserModel getUser(@RequestParam(value="username", defaultValue="") String username) {  
  3. //TODO 去數據庫根據用戶名查找用戶對象  
  4. UserModel user = new UserModel();  
  5. user.setRealname("zhang");  
  6.      return user;  
  7. }   

如你要修改用戶資料時一般需要根據用戶的編號/用戶名查找用戶來進行編輯,此時可以通過如上代碼查找要編輯的用戶。

也可以進行一些默認值的處理。

Java代碼  收藏代碼
  1. @RequestMapping(value="/model1"//②  
  2. public String test1(@ModelAttribute("user") UserModel user, Model model)   

此處我們看到①和②有同名的命令對象,那Spring Web MVC內部如何處理的呢:

(1首先執行@ModelAttribute註解的方法,準備視圖展示時所需要的模型數據;@ModelAttribute註解方法形式參數規則和@RequestMapping規則一樣,如可以有@RequestParam等;

2執行@RequestMapping註解方法,進行模型綁定時首先查找模型數據中是否含有同名對象,如果有直接使用,如果沒有通過反射創建一個,因此②處的user將使用①處返回的命令對象。即②處的user等於①處的user。

 

三、暴露@RequestMapping方法返回值爲模型數據

Java代碼  收藏代碼
  1. public @ModelAttribute("user2") UserModel test3(@ModelAttribute("user2") UserModel user)  

大家可以看到返回值類型是命令對象類型,而且通過@ModelAttribute("user2")註解,此時會暴露返回值到模型數據(名字爲user2)中供視圖展示使用。那哪個視圖應該展示呢?此時Spring Web MVC會根據RequestToViewNameTranslator進行邏輯視圖名的翻譯,詳見【4.15.5、RequestToViewNameTranslator】一節。

 

此時又有問題了,@RequestMapping註解方法的入參user暴露到模型數據中的名字也是user2,其實我們能猜到:

3@ModelAttribute註解的返回值會覆蓋@RequestMapping註解方法中的@ModelAttribute註解的同名命令對象。

 

四、匿名綁定命令參數

Java代碼  收藏代碼
  1. public String test4(@ModelAttribute UserModel user, Model model)  
  2. 或  
  3. public String test5(UserModel user, Model model)   

此時我們沒有爲命令對象提供暴露到模型數據中的名字,此時的名字是什麼呢?Spring Web MVC自動將簡單類名(首字母小寫)作爲名字暴露,如“cn.javass.chapter6.model.UserModel”暴露的名字爲“userModel”。

Java代碼  收藏代碼
  1. public @ModelAttribute List<String> test6()  
  2. 或  
  3. public @ModelAttribute List<UserModel> test7()   

對於集合類型(Collection接口的實現者們,包括數組),生成的模型對象屬性名爲“簡單類名(首字母小寫)”+“List”,如List<String>生成的模型對象屬性名爲“stringList”,List<UserModel>生成的模型對象屬性名爲“userModelList”。

 

其他情況一律都是使用簡單類名(首字母小寫)作爲模型對象屬性名,如Map<String, UserModel>類型的模型對象屬性名爲“map”。

6.6.7、@SessionAttributes綁定命令對象到session

有時候我們需要在多次請求之間保持數據,一般情況需要我們明確的調用HttpSession的API來存取會話數據,如多步驟提交的表單。Spring Web MVC提供了@SessionAttributes進行請求間透明的存取會話數據。

Java代碼  收藏代碼
  1. //1、在控制器類頭上添加@SessionAttributes註解  
  2. @SessionAttributes(value = {"user"})    //①  
  3. public class SessionAttributeController   
  4.   
  5. //2、@ModelAttribute註解的方法進行表單引用對象的創建  
  6. @ModelAttribute("user")    //②  
  7. public UserModel initUser()   
  8.   
  9. //3、@RequestMapping註解方法的@ModelAttribute註解的參數進行命令對象的綁定  
  10. @RequestMapping("/session1")   //③  
  11. public String session1(@ModelAttribute("user") UserModel user)  
  12.   
  13. //4、通過SessionStatus的setComplete()方法清除@SessionAttributes指定的會話數據  
  14. @RequestMapping("/session2")   //③  
  15. public String session(@ModelAttribute("user") UserModel user, SessionStatus status) {  
  16.     if(true) { //④  
  17.         status.setComplete();  
  18.     }  
  19.     return "success";  
  20. }   

@SessionAttributes(value = {"user"})含義:

@SessionAttributes(value = {"user"}) 標識將模型數據中的名字爲“user” 的對象存儲到會話中(默認HttpSession),此處value指定將模型數據中的哪些數據(名字進行匹配)存儲到會話中,此外還有一個types屬性表示模型數據中的哪些類型的對象存儲到會話範圍內,如果同時指定value和types屬性則那些名字和類型都匹配的對象才能存儲到會話範圍內。

 

包含@SessionAttributes執行流程如下所示:

 首先根據@SessionAttributes註解信息查找會話內的對象放入到模型數據中;

 執行@ModelAttribute註解的方法:如果模型數據中包含同名的數據,則不執行@ModelAttribute註解方法進行準備表單引用數據,而是使用①步驟中的會話數據;如果模型數據中不包含同名的數據,執行@ModelAttribute註解的方法並將返回值添加到模型數據中;

③ 執行@RequestMapping方法,綁定@ModelAttribute註解的參數:查找模型數據中是否有@ModelAttribute註解的同名對象,如果有直接使用,否則通過反射創建一個;並將請求參數綁定到該命令對象;

此處需要注意:如果使用@SessionAttributes註解控制器類之後,③步驟一定是從模型對象中取得同名的命令對象,如果模型數據中不存在將拋出HttpSessionRequiredException Expected session attribute ‘user’(Spring3.1)

或HttpSessionRequiredException Session attribute ‘user’ required - not found in session(Spring3.0)異常。

 如果會話可以銷燬了,如多步驟提交表單的最後一步,此時可以調用SessionStatus對象的setComplete()標識當前會話的@SessionAttributes指定的數據可以清理了,此時當@RequestMapping功能處理方法執行完畢會進行清理會話數據。

 

我們通過Spring Web MVC的源代碼驗證一下吧,此處我們分析的是Spring3.1的RequestMappingHandlerAdapter,讀者可以自行驗證Spring3.0的AnnotationMethodHandlerAdapter,流程一樣:

1、RequestMappingHandlerAdapter.invokeHandlerMethod

Java代碼  收藏代碼
  1. //1、RequestMappingHandlerAdapter首先調用ModelFactory的initModel方法準備模型數據:  
  2. modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);  
  3. //2、調用@RequestMapping註解的功能處理方法  
  4. requestMappingMethod.invokeAndHandle(webRequest, mavContainer);  
  5. //3、更新/合併模型數據  
  6. modelFactory.updateModel(webRequest, mavContainer);   

2、ModelFactory.initModel

Java代碼  收藏代碼
  1. Map<String, ?> attributesInSession = this.sessionAttributesHandler.retrieveAttributes(request);  
  2. //1.1、將與@SessionAttributes註解相關的會話對象放入模型數據中  
  3. mavContainer.mergeAttributes(attributesInSession);  
  4. //1.2、調用@ModelAttribute方法添加表單引用對象  
  5. invokeModelAttributeMethods(request, mavContainer);  
  6. //1.3、驗證模型數據中是否包含@SessionAttributes註解相關的會話對象,不包含拋出異常  
  7. for (String name : findSessionAttributeArguments(handlerMethod)) {  
  8.     if (!mavContainer.containsAttribute(name)) {  
  9.         //1.4、此處防止在@ModelAttribute註解方法又添加了會話對象  
  10.         //如在@ModelAttribute註解方法調用session.setAttribute("user", new UserModel());  
  11.         Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);  
  12.         if (value == null) {  
  13.             throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");  
  14.         }  
  15.         mavContainer.addAttribute(name, value);  
  16. }   

3、ModelFactory.invokeModelAttributeMethods

Java代碼  收藏代碼
  1. for (InvocableHandlerMethod attrMethod : this.attributeMethods) {  
  2.     String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();   
  3.     //1.2.1、如果模型數據中包含同名數據則不再添加  
  4.     if (mavContainer.containsAttribute(modelName)) {  
  5.         continue;  
  6.     }  
  7.     //1.2.2、調用@ModelAttribute註解方法並將返回值添加到模型數據中,此處省略實現代碼  
  8. }   

(4、requestMappingMethod.invokeAndHandle 調用功能處理方法,此處省略

5、ModelFactory.updateMode 更新模型數據

Java代碼  收藏代碼
  1. //3.1、如果會話被標識爲完成,此時從會話中清除@SessionAttributes註解相關的會話對象  
  2. if (mavContainer.getSessionStatus().isComplete()){   
  3.     this.sessionAttributesHandler.cleanupAttributes(request);  
  4. }  
  5. //3.2、如果會話沒有完成,將模型數據中的@SessionAttributes註解相關的對象添加到會話中  
  6. else {  
  7.     this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());  
  8. }  
  9. //省略部分代碼   

到此@SessionAtrribute介紹完畢,測試代碼在cn.javass.chapter6.web.controller.paramtype.SessionAttributeController中。

 

另外cn.javass.chapter6.web.controller.paramtype.WizardFormController是一個類似於【4.11、AbstractWizardFormController】中介紹的多步驟表單實現,此處不再貼代碼,多步驟提交表單需要考慮會話超時問題,這種方式可能對用戶不太友好,我們可以採取隱藏表單(即當前步驟將其他步驟的表單隱藏)或表單數據存數據庫(每步驟更新下數據庫數據)等方案解決。

6.6.8、@Value綁定SpEL表示式

@Value用於將一個SpEL表達式結果映射到到功能處理方法的參數上。

Java代碼  收藏代碼
  1. public String test(@Value("#{systemProperties['java.vm.version']}") String jvmVersion)  

到此數據綁定我們就介紹完了,對於沒有介紹的方法參數和註解(包括自定義註解)在後續章節進行介紹。接下來我們學習下數據類型轉換吧。


AJAX與spring mvc交互

技術關鍵點是:

1、返回字符串,並且是ResponseBody

2、設置MIME type是 text/html

代碼如下:

  1. @ResponseBody  
  2. @RequestMapping(value="/callJs",produces = "text/html; charset=UTF-8")  
  3. public String callJs()throws Exception{  
  4.     return "<script>parent.alert('Hello Js');</script>";  
  5.       
  6. }  
AJAX與spring mvc交互

(1)簡單交互:

<table style="width: 100%" class="table" cellspacing="1" cellpadding="1" border="0">
  <tr><td  class="ti1"  colSpan="2">請選擇審訊室</td></tr> 
 <tr><td  class="ti2hui">審訊室名稱</td><td class="ti1cu">
    <select id="roomid" name="roomid" >
  <c:forEach items="${roomlist}" var="room">  
        <option value ="${room.id}">${room.name}</option>   
     </c:forEach>
    </select>
    </td></tr>
<tr><td   class="ti2hui" colSpan="2" align="center"><input type="button" οnclick="setshow()"  value="確定"/>  </td></tr>
</table>

------------------------------ajax-----提交的參數可以通過url提交,也可以用data:{}方式提交---------------------------------------------- 
function setshow(){ 

$.ajax( {   
    type : "POST",   
    url : "<%=request.getContextPath()%>/initroom.do?method=set", 
    data : {
      'room' : $("#roomid").find('option:selected').text(),
      'roomid' :$("#roomid").val()
     },  
    dataType: "json",   
    success : function(data) {   
        if(data.success){   
            alert("設置成功!");   
             
        }   
        else{   
            alert("設置失敗!");   
        }   
    },   
    error :function(){   
        alert("網絡連接出錯!");   
    }   
});   
}  

------------------------spring mvc-------------------------------------------------

 @RequestMapping(params = "method=set")
 public void jump(HttpSession session,HttpServletRequest request, HttpServletResponse response) throws Exception{
  String roomid= request.getParameter("roomid");
  String room= request.getParameter("room");
  session.setAttribute("ROOMID", roomid);
  session.setAttribute("ROOMNAME", room);
  System.out.println("session set:"+room+"=="+roomid);
  response.setCharacterEncoding("utf-8");
  response.getWriter().write("{\"success\":true }");
  response.getWriter().flush();
 }

 

(2)springmvc 返回信息到ajax:

import com.googlecode.jsonplugin.JSONUtil;

List<Records> recordList = new ArrayList<Records>();

//獲取recordlist操作省略

  response.setCharacterEncoding("utf-8");
   response.getWriter().write("{\"success\":true, \"data\":" + JSONUtil.serialize(recordList) + "}");
   response.getWriter().flush();

-------------------------------ajax處理序列化對象--------------------------------------------

var text = '';
       $(data.data).each(function() {

        text = text + '<li οnclick="selectRecord(' + this.id + ')" style="cursor: pointer; height:20px; list-style:none; valign: center;line-height:23px;"><div style="float:left; width:15px; height:20px; background:url(Images/record_icon.png) no-repeat center;"></div>' + this.name + '</li>';
       });

       $('#recordDiv').html(text);

 

(3)最先進的做法:

在 Spring mvc3中,響應、接受 JSON都十分方便。 
使用註解@ResponseBody可以將結果(一個包含字符串和JavaBean的Map),轉換成JSON。 
使用 @RequestBody 註解前臺只需要向 Controller 提交一段符合格式的 JSON,Spring 會自動將其拼裝成 bean。 
Spring這個轉換是靠org.codehaus.jackson這個組件來實現的,所有需要引入jackson-core-asl和org.codehaus.jackson兩個jar包 

  1. <title>Spring MVC</title>  
  2. <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>  
  3. <script type="text/javascript" src="http://jquery-json.googlecode.com/files/jquery.json-2.2.min.js"></script>  
  4. <script type="text/javascript" src="<%=request.getContextPath()%>/scripts/user/index.js"></script>  
  5. </head>  
  6. <body>  
  7. <div id="info"></div>  
  8. <form action="add" method="post" id="form">  
  9. 編號:<input type="text" name="id"/>  
  10. 姓名:<input type="text" name="username"/>  
  11. 年齡:<input type="text" name="age"/>  
  12.   
  13. <input type="button" value="提交" id="submit"/>  
  14. </form>  
  15. </body>  
  16. </html>  


  1. //將一個表單的數據返回成JSON對象  
  2. $.fn.serializeObject = function() {  
  3.   var o = {};  
  4.   var a = this.serializeArray();  
  5.   $.each(a, function() {  
  6.     if (o[this.name]) {  
  7.       if (!o[this.name].push) {  
  8.         o[this.name] = [ o[this.name] ];  
  9.       }  
  10.       o[this.name].push(this.value || '');  
  11.     } else {  
  12.       o[this.name] = this.value || '';  
  13.     }  
  14.   });  
  15.   return o;  
  16. };  
  17.   
  18. $(document).ready(  
  19.     function() {  
  20.       jQuery.ajax( {  
  21.         type : 'GET',  
  22.         contentType : 'application/json',  
  23.         url : 'user/list',  
  24.         dataType : 'json',  
  25.         success : function(data) {  
  26.           if (data && data.success == "true") {  
  27.             $('#info').html("共" + data.total + "條數據。<br/>");  
  28.             $.each(data.data, function(i, item) {  
  29.               $('#info').append(  
  30.                   "編號:" + item.id + ",姓名:" + item.username  
  31.                       + ",年齡:" + item.age);  
  32.             });  
  33.           }  
  34.         },  
  35.         error : function() {  
  36.           alert("error")  
  37.         }  
  38.       });  
  39.       $("#submit").click(function() {  
  40.         var jsonuserinfo = $.toJSON($('#form').serializeObject());  
  41.         alert(jsonuserinfo);  
  42.         jQuery.ajax( {  
  43.           type : 'POST',  
  44.           contentType : 'application/json',  
  45.           url : 'user/add',  
  46.           data : jsonuserinfo,  
  47.           dataType : 'json',  
  48.           success : function(data) {  
  49.             alert("新增成功!");  
  50.           },  
  51.           error : function(data) {  
  52.             alert("error")  
  53.           }  
  54.         });  
  55.       });  
  56.     });  


  1. @Controller  
  2. @RequestMapping("/user")  
  3. public class DemoController {  
  4.   private Logger logger = LoggerFactory.getLogger(DemoController.class);  
  5.   
  6.   @RequestMapping(value = "/list", method = RequestMethod.GET)  
  7.   @ResponseBody  
  8.   public Map<String, Object> getUserList() {  
  9.     logger.info("列表");  
  10.     List<UserModel> list = new ArrayList<UserModel>();  
  11.     UserModel um = new UserModel();  
  12.     um.setId("1");  
  13.     um.setUsername("sss");  
  14.     um.setAge(222);  
  15.     list.add(um);  
  16.     Map<String, Object> modelMap = new HashMap<String, Object>(3);  
  17.     modelMap.put("total""1");  
  18.     modelMap.put("data", list);  
  19.     modelMap.put("success""true");  
  20.     return modelMap;  
  21.   }  
  22.   
  23.   @RequestMapping(value = "/add", method = RequestMethod.POST)  
  24.   @ResponseBody  
  25.   public Map<String, String> addUser(@RequestBody UserModel model) {  
  26.     logger.info("新增");  
  27.     logger.info("捕獲到前臺傳遞過來的Model,名稱爲:" + model.getUsername());  
  28.     Map<String, String> map = new HashMap<String, String>(1);  
  29.     map.put("success""true");  
  30.     return map;  
  31.   }  
  32. }  

重定向視圖

  1. “redirect:index”
  2. view.setViewName("redirect:index");
  3. view.setView(new RedirectView("/index", false));
  4. RedirectView redirectView = new RedirectView("/index{id}");
    •         redirectView.setExpandUriTemplateVariables(false);
    •         redirectView.setExposeModelAttributes(false);
    •         view.setView(redirectView);

自定義攔截器詳解

  1. package org.springframework.web.servlet;    
  2. import Javax.servlet.http.HttpServletRequest;   
  3. import Javax.servlet.http.HttpServletResponse;   
  4. public interface HandlerInterceptor {    
  5.      // preHandle()方法在業務處理器處理請求之前被調用   
  6.      boolean preHandle(HttpServletRequest request,   
  7. HttpServletResponse response,    
  8.      Object handler)   
  9.          throws Exception;    
  10.      // postHandle()方法在業務處理器處理請求之後被調用   
  11.      void postHandle(    
  12.              HttpServletRequest request, HttpServletResponse   
  13. response, Object    
  14.              handler, ModelAndView modelAndView)   
  15.              throws Exception;    
  16.      // afterCompletion()方法在DispatcherServlet完全處理完請求後被調用   
  17.      void afterCompletion(    
  18.              HttpServletRequest request, HttpServletResponse  
  19. response, Object    
  20.              handler, Exception ex)   
  21.              throws Exception;    
  22.    
  23.  }  

下面對代碼中的三個方法進行解釋。

preHandle():這個方法在業務處理器處理請求之前被調用,在該方法中對用戶請求request進行處理。如果程序員決定該攔截器對請求進行攔截處理後還要調用其他的攔截器,或者是業務處理器去進行處理,則返回true;如果程序員決定不需要再調用其他的組件去處理請求,則返回false。

postHandle():這個方法在業務處理器處理完請求後,但是DispatcherServlet向客戶端返回請求前被調用,在該方法中對用戶請求request進行處理。

afterCompletion():這個方法在DispatcherServlet完全處理完請求後被調用,可以在該方法中進行一些資源清理的操作。

下面通過一個例子來說明如何使用SpringMVC框架的攔截器。

現在要編寫一個攔截器,攔截所有不在工作時間的請求,把這些請求轉發到一個特定的靜態頁面,而不對它們的請求進行處理。

首先編寫TimeInterceptor.Java,代碼如下:

  1. package com.yjde.web.interceptor;  
  2.   
  3. import java.util.Calendar;  
  4.   
  5. import javax.servlet.http.HttpServletRequest;  
  6. import javax.servlet.http.HttpServletResponse;  
  7.   
  8. import org.springframework.web.servlet.ModelAndView;  
  9. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
  10.   
  11. public class TimeInterceptor extends HandlerInterceptorAdapter {  
  12.     // 繼承HandlerInterceptorAdapter類  
  13.   
  14.     private int openingTime; // openingTime 屬性指定上班時間  
  15.     private int closingTime; // closingTime屬性指定下班時間  
  16.     private String outsideOfficeHoursPage;// outsideOfficeHoursPage屬性指定錯誤提示頁面的URL  
  17.   
  18.     // 重寫 preHandle()方法,在業務處理器處理請求之前對該請求進行攔截處理  
  19.     public boolean preHandle(HttpServletRequest request,  
  20.             HttpServletResponse response, Object handler) throws Exception {  
  21.         Calendar cal = Calendar.getInstance();  
  22.         int hour = cal.get(Calendar.HOUR_OF_DAY); // 獲取當前時間  
  23.         if (openingTime <= hour && hour < closingTime) { // 判斷當前是否處於工作時間段內  
  24.             return true;  
  25.         } else {  
  26.             response.sendRedirect(outsideOfficeHoursPage); // 返回提示頁面  
  27.             return false;  
  28.         }  
  29.     }  
  30.   
  31.     public void postHandle(HttpServletRequest request,  
  32.             HttpServletResponse response, Object o, ModelAndView mav)  
  33.             throws Exception {  
  34.         System.out.println("postHandle");  
  35.     }  
  36.   
  37.     public void afterCompletion(HttpServletRequest request,  
  38.             HttpServletResponse response, Object o, Exception excptn)  
  39.             throws Exception {  
  40.         System.out.println("afterCompletion");  
  41.     }  
  42.   
  43.     public int getOpeningTime() {  
  44.         return openingTime;  
  45.     }  
  46.   
  47.     public void setOpeningTime(int openingTime) {  
  48.         this.openingTime = openingTime;  
  49.     }  
  50.   
  51.     public int getClosingTime() {  
  52.         return closingTime;  
  53.     }  
  54.   
  55.     public void setClosingTime(int closingTime) {  
  56.         this.closingTime = closingTime;  
  57.     }  
  58.   
  59.     public String getOutsideOfficeHoursPage() {  
  60.         return outsideOfficeHoursPage;  
  61.     }  
  62.   
  63.     public void setOutsideOfficeHoursPage(String outsideOfficeHoursPage) {  
  64.         this.outsideOfficeHoursPage = outsideOfficeHoursPage;  
  65.     }  
  66.   
  67. }  
  68. // 可以看出,上面的代碼重載了preHandle()方法,該方法在業務處理器處理請求之前被調用。在該方法中,首先獲得當前的時間,判斷其是否在  
  69. // openingTime和closingTime之間,如果在,返回true,這樣纔會調用業務控制器去處理該請求;否則直接轉向一個頁面,返回false,這樣該請求就不會被處理。  

可以看出,上面的代碼重載了preHandle()方法,該方法在業務處理器處理請求之前被調用。在該方法中,首先獲得當前的時間,判斷其是否在 openingTime和closingTime之間,如果在,返回true,這樣纔會調用業務控制器去處理該請求;否則直接轉向一個頁面,返回 false,這樣該請求就不會被處理。

下面是在dispatcher-servlet.xml中對攔截器進行的配置,代碼如下:

  1. <pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"  
  4.     xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"  
  5.     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"  
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  7.             http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  8.             http://www.springframework.org/schema/context   
  9.             http://www.springframework.org/schema/context/spring-context-3.0.xsd  
  10.             http://www.springframework.org/schema/aop   
  11.             http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
  12.             http://www.springframework.org/schema/tx   
  13.             http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
  14.             http://www.springframework.org/schema/mvc   
  15.             http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd  
  16.             http://www.springframework.org/schema/context   
  17.             http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  18.     <!--  
  19.         使Spring支持自動檢測組件,如註解的Controller  
  20.     -->  
  21.     <context:component-scan base-package="com.yjde.web.controller" />  
  22.   
  23.     <bean id="viewResolver"  
  24.         class="org.springframework.web.servlet.view.InternalResourceViewResolver"  
  25.         p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />  
  26.     <mvc:interceptors>  
  27.         <mvc:interceptor>  
  28.             <!--設置攔截的路徑-->  
  29.             <mvc:mapping path="/login1.htm" />  
  30.             <mvc:mapping path="/login2.htm" />  
  31.             <bean class="com.yjde.web.interceptor.TimeInterceptor">  
  32.                 <!--openingTime 屬性指定上班時間-->  
  33.                 <property name="openingTime">  
  34.                     <value>12</value>  
  35.                 </property>  
  36.                 <!--closingTime屬性指定下班時間-->  
  37.                 <property name="closingTime">  
  38.                     <value>14</value>  
  39.                 </property>  
  40.                 <!--outsideOfficeHoursPage屬性指定提示頁面的URL-->  
  41.                 <property name="outsideOfficeHoursPage">  
  42.                     <value>http://localhost:8080/SpringMVCInterceptor/jsp/outsideOfficeHours.jsp  
  43.                     </value>  
  44.                 </property>  
  45.             </bean>  
  46.         </mvc:interceptor>  
  47.     </mvc:interceptors>  
  48.     <bean id="messageSource"  
  49.         class="org.springframework.context.support.ResourceBundleMessageSource"  
  50.         p:basename="message">  
  51.     </bean>  
  52. </beans>

國際化

一.基於瀏覽器請求的國際化實現:

首先配置我們項目的springservlet-config.xml文件添加的內容如下:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <!-- 國際化信息所在的文件名 -->                     
    <property name="basename" value="messages" />   
    <!-- 如果在國際化資源文件中找不到對應代碼的信息,就用這個代碼作爲名稱  -->               
    <property name="useCodeAsDefaultMessage" value="true" />           
</bean>

在com.demo.web.controllers包中添加GlobalController.java內容如下:

複製代碼
package com.demo.web.controllers;

import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.support.RequestContext;
import com.demo.web.models.FormatModel;

@Controller
@RequestMapping(value = "/global")
public class GlobalController {
    
    @RequestMapping(value="/test", method = {RequestMethod.GET})
    public String test(HttpServletRequest request,Model model){
        if(!model.containsAttribute("contentModel")){
            
            //從後臺代碼獲取國際化信息
            RequestContext requestContext = new RequestContext(request);
            model.addAttribute("money", requestContext.getMessage("money"));
            model.addAttribute("date", requestContext.getMessage("date"));

            
            FormatModel formatModel=new FormatModel();

            formatModel.setMoney(12345.678);
            formatModel.setDate(new Date());
            
            model.addAttribute("contentModel", formatModel);
        }
        return "globaltest";
    }
    
}
複製代碼

這裏展示模型還用系列(7)中的作爲演示。

在項目中的源文件夾resources中添加messages.properties、messages_zh_CN.properties、messages_en_US.properties三個文件,其中messages.properties、messages_zh_CN.properties裏面的"money", "date",爲中文,messages_en_US.properties裏面的爲英文。

在views文件夾中添加globaltest.jsp視圖,內容如下:

複製代碼
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

    下面展示的是後臺獲取的國際化信息:<br/>
    ${money}<br/>
    ${date}<br/>

    下面展示的是視圖中直接綁定的國際化信息:<br/>
    <spring:message code="money"/>:<br/>
    <spring:eval expression="contentModel.money"></spring:eval><br/>
    <spring:message code="date"/>:<br/>
    <spring:eval expression="contentModel.date"></spring:eval><br/>
    
</body>
</html>
複製代碼

運行測試:

1

更改瀏覽器語言順序,刷新頁面:

2


二.基於Session的國際化實現:


在項目的springservlet-config.xml文件添加的內容如下(第一種時添加的內容要保留):

<mvc:interceptors>  
    <!-- 國際化操作攔截器 如果採用基於(請求/Session/Cookie)則必需配置 --> 
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />  
</mvc:interceptors>  

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />

更改globaltest.jsp視圖爲如下內容:

複製代碼
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <a href="test?langType=zh">中文</a> | <a href="test?langType=en">英文</a><br/>

    下面展示的是後臺獲取的國際化信息:<br/>
    ${money}<br/>
    ${date}<br/>

    下面展示的是視圖中直接綁定的國際化信息:<br/>
    <spring:message code="money"/>:<br/>
    <spring:eval expression="contentModel.money"></spring:eval><br/>
    <spring:message code="date"/>:<br/>
    <spring:eval expression="contentModel.date"></spring:eval><br/>
    
</body>
</html>
複製代碼

更改GlobalController.java爲如下內容:

複製代碼
package com.demo.web.controllers;

import java.util.Date;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.support.RequestContext;
import com.demo.web.models.FormatModel;

@Controller
@RequestMapping(value = "/global")
public class GlobalController {
    
    @RequestMapping(value="/test", method = {RequestMethod.GET})
    public String test(HttpServletRequest request,Model model, @RequestParam(value="langType", defaultValue="zh") String langType){
        if(!model.containsAttribute("contentModel")){
            
            if(langType.equals("zh")){
                Locale locale = new Locale("zh", "CN"); 
                request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,locale); 
            }
            else if(langType.equals("en")){
                Locale locale = new Locale("en", "US"); 
                request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,locale);
            }
            else 
                request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,LocaleContextHolder.getLocale());
            
            //從後臺代碼獲取國際化信息
            RequestContext requestContext = new RequestContext(request);
            model.addAttribute("money", requestContext.getMessage("money"));
            model.addAttribute("date", requestContext.getMessage("date"));

            
            FormatModel formatModel=new FormatModel();

            formatModel.setMoney(12345.678);
            formatModel.setDate(new Date());
            
            model.addAttribute("contentModel", formatModel);
        }
        return "globaltest";
    }
    
}
複製代碼

運行測試:

3

4

 

三.基於Cookie的國際化實現:

把實現第二種方法時在項目的springservlet-config.xml文件中添加的

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />

註釋掉,並添加以下內容:

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />

更改GlobalController.java爲如下內容:

複製代碼
package com.demo.web.controllers;

import java.util.Date;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
//import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.support.RequestContext;

import com.demo.web.models.FormatModel;

@Controller
@RequestMapping(value = "/global")
public class GlobalController {
    
    @RequestMapping(value="/test", method = {RequestMethod.GET})
    public String test(HttpServletRequest request, HttpServletResponse response, Model model, @RequestParam(value="langType", defaultValue="zh") String langType){
        if(!model.containsAttribute("contentModel")){
            
            /*if(langType.equals("zh")){
                Locale locale = new Locale("zh", "CN"); 
                request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,locale); 
            }
            else if(langType.equals("en")){
                Locale locale = new Locale("en", "US"); 
                request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,locale);
            }
            else 
                request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,LocaleContextHolder.getLocale());*/
            
            if(langType.equals("zh")){
                Locale locale = new Locale("zh", "CN"); 
                //request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,locale);
                (new CookieLocaleResolver()).setLocale (request, response, locale);
            }
            else if(langType.equals("en")){
                Locale locale = new Locale("en", "US"); 
                //request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,locale);
                (new CookieLocaleResolver()).setLocale (request, response, locale);
            }
            else 
                //request.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME,LocaleContextHolder.getLocale());
                (new CookieLocaleResolver()).setLocale (request, response, LocaleContextHolder.getLocale());
            
            //從後臺代碼獲取國際化信息
            RequestContext requestContext = new RequestContext(request);
            model.addAttribute("money", requestContext.getMessage("money"));
            model.addAttribute("date", requestContext.getMessage("date"));

            
            FormatModel formatModel=new FormatModel();

            formatModel.setMoney(12345.678);
            formatModel.setDate(new Date());
            
            model.addAttribute("contentModel", formatModel);
        }
        return "globaltest";
    }
    
}
複製代碼

運行測試:

5

6

關於<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />3個屬性的說明(可以都不設置而用其默認值):

複製代碼
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
    <!-- 設置cookieName名稱,可以根據名稱通過js來修改設置,也可以像上面演示的那樣修改設置,默認的名稱爲 類名+LOCALE(即:org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE-->
    <property name="cookieName" value="lang"/>
    <!-- 設置最大有效時間,如果是-1,則不存儲,瀏覽器關閉後即失效,默認爲Integer.MAX_INT-->
    <property name="cookieMaxAge" value="100000">
    <!-- 設置cookie可見的地址,默認是“/”即對網站所有地址都是可見的,如果設爲其它地址,則只有該地址或其後的地址纔可見-->
    <property name="cookiePath" value="/">
</bean>
複製代碼

 

四.基於URL請求的國際化的實現:

首先添加一個類,內容如下:

複製代碼
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.LocaleResolver;

public class MyAcceptHeaderLocaleResolver extends AcceptHeaderLocaleResolver {

    private Locale myLocal;

    public Locale resolveLocale(HttpServletRequest request) {
        return myLocal;
    } 

    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        myLocal = locale;
    }
  
}
複製代碼

然後把實現第二種方法時在項目的springservlet-config.xml文件中添加的

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />

註釋掉,並添加以下內容:

<bean id="localeResolver" class="xx.xxx.xxx.MyAcceptHeaderLocaleResolver"/>

“xx.xxx.xxx”是剛纔添加的MyAcceptHeaderLocaleResolver 類所在的包名。

保存之後就可以在請求的URL後附上 locale=zh_CN 或 locale=en_US 如 http://xxxxxxxx?locale=zh_CN 來改變語言了,具體這裏不再做演示了。

 

國際化部分的內容到此結束。


發佈了36 篇原創文章 · 獲贊 3 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章