- Spring MVC 主要流程為 DispatcherServlet 讀取可設定的 hander mapping,將 request 傳給 handler(controller),然後再透過 view resolution 產生 response,並支援 locale 與 theme resolution 以及檔案上傳。
- @Controller 與 @RequestMapping 組成預設的 handler。
- Spring 3.0 以後,新增 @PathVariable等功能用來建立 RESTful 與一般 web app。
- Spring MVC 特色:
- 使用 POJO 作為 form backing object,不用與 Spring 有任何關聯。
- 在 data binding 時,將型別錯誤視為驗證錯誤,而非較嚴重且難處理的系統錯誤。
- 在 business object 的自動型別與 form field 的字串型別之間是無痛轉換的。
- Controller 可以直接寫到 response,也可以透過 ModelAndView 來利用 view resolutaion,並使用 map 來存放 form object 或 reference data。
- 透過 @RequestParam、@RequestHeader、 @PathVariable 等 annotation,讓 controller method 的 signature 不受任何限制。
- Spring 的一個特色就是 Open for extension, close for modification,意思就是「擴充很容易,但不能修改」。
- 在 web.xml 設定 DispatcherServlet:
<servlet> <servlet-name>mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
- DispatcherServlet 預設會去找 /WEB-INF/[servlet-name]-servlet.xml,也就是 mvc-servlet.xml,來設定 WebApplicationContext。
- WebApplicationContext 繼承 ApplicationContext,增加 web 相關的功能。
- WebApplicationContext 放在 ServletContext 裡,可以透過 RequestContextUtils 來取得。
- DispatcherServlet 有三個 init-param 可以設定:
- contextClass - 實做 WebApplicationContext 的 class name,預設為 XmlWebApplicationContext。
- contextConfigLocation - 設定檔的位置,預設為 /WEB-INF/[servlet-name]-servlet.xml,可以用逗號指定多個。
- namespance - WebApplicationContext 的 namespace,預設為 [servlet-name]-servlet。
- DispatcherServlet 需要某些 bean 一起工作:
- controllers - MVC 裡的 C。
- handler mappings -將 url 對應到特定的 controller,以及 pre-processors 或 post-processors。
- view resolvers - 將 view name 轉換成 view 物件。
- locale resolver - 自訂i18n。
- theme resolver -自訂頁面風格。
- multipart file resolver -檔案上傳。
- handler exception resolvers - 將特定的 exception 對應到某些 view,或者自訂的 exception 處理方式。
- DispatcherServlet 處理 request 的流程:
- 先找出 WebApplicationContext 以 DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE 為名放到 request,供後續使用。
- 找出 locale resolver 放到 request 裡,如果有的話。
- 找出 theme resolver 放到 request 裡,如果有的話。
- 找出 multipart file resolver 放到 request 裡,如果有的話;若有檔案上傳,則將 request 包成 MultipartHttpServletRequest,供後續使用。
- 找出 handler,包括 pre-processors、controllers、post-processors,並執行之。
- 得到 model 與 view, 產生 response 後回傳,handler 可能不會回傳 model 或 view,有可能有 exception 發生,或者 handler 本身已經回寫 response。
- Controllers
- 自從引進 @Controller 以後,Spring controllers 不用再繼承 Spring 特定的 class 了。
@Controller public class HelloWorldController { // ... } // 當然要讓 Spring 知道怎麼找到這些 @Controller <context:component-scan base-package="org.springframework.samples.petclinic.web"/>
- @RequestMapping 可以用在 class level 或 method level,用在 class level 表示以前的 SimpleFormController,用在 method level 表示 MultiActionController。
@Controller @RequestMapping(value = "/user") public class UserController { private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } // 表示使用 get 讀取 /user @RequestMapping(method = RequestMethod.GET) public String userForm(Model model) { model.addAttribute(new User()); return "userForm"; } // 表示使用 post 讀取 /user @RequestMapping(method = RequestMethod.POST) public String adduser(User user, BindingResult result) { if (result.hasErrors()) { return "userForm"; } this.userService.addUser(user); return "userList/r"; } // 表示使用 get 讀取 /user/list @RequestMapping("/list") public ModelAndView userList() { ModelAndView mav = new ModelAndView("userList"); List<User> list = this.userService.listUser(0, 10); mav.addObject("list", list); return mav; } // 表示使用 get 讀取 /user/delete @RequestMapping("/delete") public String deleteUser(@RequestParam("id") Long id) { this.userService.deleteUser(id); return "userList/r"; } }
- 如果 @RequestMapping 同時使用在 class level 與 method level,若定義的屬性不同,則表示為聯集,若屬性相同,則表示為覆蓋。
- 但是若同時定義路徑,即 value 屬性,則表示為路徑聯集,以上面的 userList() 為例,其所代表的路徑為 /user/list,也就是說同時定義時,method level 變成相對於 class level 的路徑。
- 若只有定義 method level 的 @RequestMapping,即為 MultiActionController,如下所示:
@Controller public class CommonController { @RequestMapping("/index") public ModelAndView index() { ModelAndView mav = new ModelAndView(); return mav; } }
- 如果因為使用 @Transactional 導致 controller 必須產生 proxy 時,若是使用 interface proxy,則必須將 @Controller 與 @RequestMapping 等 annotation 放在 interface 上,因為 proxy 之後是看不到原始 class 的,除非改用 CGLIB 的 class proxy。
- 好用的 URI Template 與 @PathVariable
- 將 param 參數移到 uri裡,透過 URI Template 與 @PathVariable 自動將 param 參數設定到 method 參數裡。
// 啟用 debugging complie @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable String ownerId, Model model) { // ownerId is available here... } // 未啟用 debugging compile 時,必須在 @PathVariable 指定 param 變數名稱,但也因此 method 參數名稱就不受限制 @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable("ownerId") String owner, Model model) { // ownerId is available here... } // 可以同時使用多個 @PathVariable @RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // ... }
- URI Template 甚至可以用在 class level 的 @RequestMapping 上。
- 至於型別的限制上,只要有定義 data binder 就可以用。
- 一堆 @RequestMapping 進階的功能
- 除了支援 URI Template,也支援 Ant style path pattern,如 /user/*.do,更支援兩種混合,如 /user/*/{userId}。
- 當沒有指定 value 屬性時,會以 method name 替代,替代方式以 MethodNameResolver 決定,預設為 InternalPathMethodNameResolver。
- 也可以使用 params 屬性提高準確度。
@RequestMapping(value = "/pets/{petId}", params="myParam=myValue")
- 也可以使用 header 屬性提高準確度。
@RequestMapping(value = "/pets", method = RequestMethod.POST, headers="content-type=text/*")
- 可以用在 @ReqeustMapping method 裡的參數型別
- ServletRequest, ServletReponse, HttpServletRequest, HttpServletReponse
- HttpSession
- WebRequest and NativeWebRequest
- Locale
- InputStream and Reader - 讀取 request 的內容
- OutputStream and Writer - 供寫出 response 的內容
- Principal
- @PathVariable 註解的參數
- @RequestParam 註解的參數
- @RequestHeader 註解的參數
- @RequestBody 註解的參數
- HttpEntity<?>
- Map, Model, ModelMap - 供 view render 使用
- Command 或 form object
- Errors 或 BindingResult - command 或 form object 的驗證結果,在 method 參數列中一定要緊跟在驗證對象之後。
// 無效的 @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, Model model, BindingResult result) { … } // 有效的 @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, Model model) { … }
- SessionStatus - 2012/10/12 補充,請參考 為什麼要用 SpringMVC 的 SessionStatus。
- @ReqeustMapping method支援的回傳型別
- ModelAndView - 夾帶 command 物件與 @ModelAttribute reference object。
- Model - view name 由 RequestToViewNameTranslator 決定,且夾帶 command 物件與 @ModelAttribute reference object。
- Map - view name 由 RequestToViewNameTranslator 決定,且夾帶 command 物件與 @ModelAttribute reference object。
- View - 且夾帶 command 物件、 @ModelAttribute reference object 與 Model 物件。
- String - 且夾帶 command 物件、 @ModelAttribute reference object 與 Model 物件。
- void - 由 handler 自行寫入 response,或者由 RequestToViewNameTranslator 決定 view name。
- @ResponseBody - 直接將回傳物件寫入 response,由 HttpMessageConverter 轉譯。
- HttpEntity<?> and ResponseEntity<?> - 可以透過這兩個物件直接存取 response 的 header 和 content。
- 其他物件 - 視為一個 model,且夾帶 command 物件與 @ModelAttribute reference object。
- @RequestParm
- 將 request param 塞到 method 參數。
- 預設為 required,可以透過 required 屬性修改。
- @RequestBody
- 不會用。
- @ResponseBody
- 不會用。
- HttpEntity<?> 類似於 @RequestBody 與 @ResponseBody。
- @ModelAttribute
- 用在 method level 表示 reference data。
@ModelAttribute("types") public List<String> findTypes() { List<String> list = new ArrayList<String>(); // ... return list; }
- 用在 method param 表示來自於 form 的 model data。
@RequestMapping(method = RequestMethod.POST) public String adduser(@ModelAttribute("user") User user, BindingResult result) { // ... } <form:form modelAttribute="user" method="post"> <form:input path="email" size="30" maxlength="80"/> <input type="submit"/> </form:form>
- @SessionAttributes
- 用在 type level,作為以前的 sessionForm 使用。
@Controller @RequestMapping("/user") @SessionAttributes("user") public class UserController { // ... }
- @CookieValue
- 用在 method param 來取得 cookie。
public ModelAndView index(@CookieValue("JSESSIONID") String cookie) { // ... }
- @RequestHeader
- 用在 method param 來取得 request header。
- @InitBinder
- 用在 method level,該 method param 可以用除了 command 物件、form 物件與驗證結果物件以外 @RequestMapping 支援所有的 method param,另外可以用 WebDataBinder。
- 不可以有回傳值。
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
沒有留言:
張貼留言