SpringMVC 處理模型:
1、所有request請求,如果需要交由SpringMVC處理,必須在DispatcherServlet對請求進行處理
2、所有請求根據請求的信息,通過handlerMapping進行獲得相關的Handler映射。
3、根據handler的映射信息,獲得handler然後通過handlerAdapter對handler進行統一封裝代用。
3、然後根據handler的處理獲得相應的ModelAndView,然後通過ModelAndView獲得請求的模型數據和需要進行response的視圖信息
4、根據ModelAndView中的視圖信息調用ViewResolver,獲得相應的視圖(JSP等)
5、根據實際的視圖進行response渲染,並返回給客戶端
其中controller就是我們的handler然後通過handlerAdapter進行適配,至於我們的DefaultAnnotationHandlerMapping是通過@RequestMapping進行標識。可以發現其實HandlerMapping和HandlerAdapter、ViewResolver的實現類都可以進行調整的,當然默認已經幾乎夠用了。
首先對SpringMVC進行配置,我們需要配置web.xml 因爲需要聲明DispatcherServlet 截獲所有請求,第二需要在web.xml中定義SpringMVC的配置文件。
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>Spring Review Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>springMvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>可以見到有contextConfigLocation配置定義了SpringMVC的配置文件位置。當然這不是唯一定義SpringMVC配置文件位置的方式:
1、不寫配置文件的位置,默認爲servlet-name的名字+servlet.xml,以上面爲例:servlet-name爲springMvc,默認配置文件爲:/WEB-INF/springMvc-servlet.xml
2、配置namespace,如果配置namespace的情況下,配置文件的位置和文件名是:/WEB-INF/<namespace>.xml。
3、第三種方式就是上面那種,也是我經常使用的一種,因爲我用maven創建項目,所有配置文件習慣放在resource列表中。
當然也有一些不常用的屬性這裏也列出一下:
1、publishContext: boolean類型的屬性,默認爲true。決定是否將WebApplicationContext 發表到ServletContext的屬性列表中。調用者可以使用servletContext找到WebApplicationContext實例,對應的屬性名爲DispatcherServlet#getServletContextAttributeName()返回值。
2、publishEvents:boolean類型屬性,當DispatcherServlet處理完一個請求後,是否需要向容器發佈一個ServletRequestHandlerEvent事件,默認爲true,如果容器中沒有監聽可以設置爲false來提高運行性能。
SpringMVC配置文件
其實SpringMVC配置文件和Spring的基本配置文件差不多。但是最重要的概念是,Spring容器和SpringMVC容器的關係,SpringMVC的容器是作爲Spring容器的子容器。所以SpringMVC容器中的bean能夠訪問Spring容器中的資源,但是反過來則不可以。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <context:component-scan base-package="com.maxfunner"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/views/" p:suffix=".jsp" p:order="2" ></bean> </beans>
這裏首先定義了關於組建的掃描的包起始位置,當然這裏最重要是controller的掃描。這裏我們定義了viewResolver是InternalResourceViewResolver,這個viewResolver稍後會詳細詳解,這裏定義了InternalResourceViewResolver的作用是,定義從handler獲得的視圖位置解釋。例如:我們從控制器返回一個“login”字符串,則將會解釋視圖爲/WEB-INF/views/login.jsp的視圖,然後將這個視圖進行渲染和響應。
然後我們做一個簡單的控制器
@Controller @RequestMapping("/user") public class UserController { @Autowired UserService userService; public UserService getUserService() { return userService; } public void setUserService(UserService userService) { this.userService = userService; } @RequestMapping("/register.html") public String registerHtml() { return "user/register"; } @RequestMapping("/login.html") public String loginHtml() { return "user/login"; } @RequestMapping("/register.action") public ModelAndView registerAction(User user) { this.userService.saveUser(user); ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("user/main"); modelAndView.addObject("user", user); return modelAndView; } @RequestMapping("/{userId}") public String getUserById(@PathVariable("userId") int userId, ModelMap modelMap) { modelMap.put("user", this.userService.getUserById(userId)); return "user/main"; } @RequestMapping(value = "/list") public String userList(int begin, int end, ModelMap modelMap) { List<User> userList = this.userService.getUserByIdRange(begin, end); modelMap.addAttribute("userList", userList); return "user/list"; } @RequestMapping(value = "/login.action", params = {"username=admin"}) public ModelAndView adminLogin(String username, String password) { ModelAndView modelAndView = new ModelAndView(); User user = this.userService.login(username, password); modelAndView.addObject("user", user); modelAndView.setViewName("user/main"); System.err.println(">>>>>>> adminLogin !!!"); return modelAndView; } @RequestMapping(value = "/login.action", params = {"username!=admin"}) public ModelAndView normalLogin(String username, String password, @CookieValue("JSESSIONID") String jsessionId) { ModelAndView modelAndView = new ModelAndView(); User user = this.userService.login(username, password); modelAndView.addObject("user", user); modelAndView.setViewName("user/main"); System.err.println(">>>>>>> normalLogin !!! " + jsessionId); return modelAndView; } }
這裏可以看見很多種獲http請求信息綁定的方式,首先解釋幾件事:
@RequestMapping:@RequestMapping 爲響應的方位URL,這個是針對於項目而然的,這裏的RequestMapping 在類的頭部也有定義,方法也有定義。在類中定義是非必要的,但是一般我們都會這樣做,原因是爲了對類進行URL分類。如果用戶訪問路徑爲/user/login.html 則這裏會訪問到loginhtml方法當中。如果我們沒有定義類的RequestMapping則 用戶訪問loginHtml則用戶的訪問路徑爲/login.html。然而@RequestMapping最重要的是將請求匹配到相應的處理方法當中,所以匹配方式可以有如下這些:
1、@RequestMapping("/user/*/main") 使用通配符進行多個url匹配到單個處理方法當中,通配符*一個路徑下的任意目錄名稱(不包括子目錄),通配符**則爲一個路徑下的任意目錄名稱(包括子目錄),?代表匹配任意字符。舉個例子:/user/*/main 匹配 /user/ab/main /user/dd/main ... , /user/**/main 匹配 /user/admin /user/ccd/dd/main /user/add/admin ... , /user/??/main 匹配 /user/dd/main /user/da/main
2、對請求參數的訪問限制:我們可以通過@RequestMapping的param的屬性對其進行控制,例子:
@RequestMapping(value = "/login.action", params = {"username=admin"}) 如果username參數值爲admin 則映射到這個處理方法當中。
@RequestMapping(value = "/login.action", params = {"username!=admin"}) 如果username參數值不等於admin 則映射到這個處理方法中。
除此之外,!admin則代表請求不包含admin的參數,admin則代表請求包含admin參數。當然如果多個條件可以使用“,” 分開。
3、對請求方法(method)進行綁定,@RequestMapping(value = "/register.action",method = RequestMethod.POST) 這個處理方法 請求method爲post纔會訪問。
4、@RequestMapping 還可以將我們傳入的URL作爲一個佔位符,然後將這個佔位符相應的實際路徑並定到處理方法的參數中,例子:
@RequestMapping("/{userId}")
public String getUserById(@PathVariable("userId") int userId, ModelMap modelMap) {
modelMap.put("user", this.userService.getUserById(userId));
return "user/main";
}
入參綁定
一般情況下,我們最簡單就是講參數名稱定義爲提交的參數名稱一致,spring就會爲我們進行綁定,當然我們可以使用@RequestParam進行參數綁定,而且這@RequestParam提供了參數的一些綁定方式,例如這個參數是否必須、參數名是什麼等
@RequestParam
value -->參數名稱
required --> 這個參數是否爲必要參數,默認爲true
defaultValue ---> 默認參數,這個設置了required自定設置爲false。而且極少情況會使用到這個參數。
@RequestMapping(value = "/login.action", params = {"username=admin"})
public ModelAndView adminLogin(@RequestParam(value = "username",required = true) String username,@RequestParam(value = "password",required = true) String password) {
ModelAndView modelAndView = new ModelAndView();
User user = this.userService.login(username, password);
modelAndView.addObject("user", user);
modelAndView.setViewName("user/main");
return modelAndView;
}
@CookieValue 將Cookie參數綁定到處理方法的參數上:
@RequestMapping(value = "/login.action", params = {"username!=admin"})
public ModelAndView normalLogin(String username, String password,@CookieValue(value = "JSESSIONID",required = false) String jsessionId)
@RequestHeader 將請求頭信息綁定到參數中:
@RequestMapping("/index.html")
public String index(@RequestHeader("Accept-Encoding") String encoding) {
return "user/index";
}
對form表單的參數封裝對象直接綁定:
@RequestMapping(value = "/register.action", method = RequestMethod.POST)
public ModelAndView registerAction(User user) {
this.userService.saveUser(user);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("user/main");
modelAndView.addObject("user", user);
return modelAndView;
}
則請求參數的名稱和User對象的屬性名稱一致,spring會進行對象的創建和對應屬性的綁定操作。
綁定Servlet API對象作爲處理方法的入參:
@RequestMapping("/index.html") public String index(HttpServletRequest request, HttpServletResponse response, HttpSession session) { return "user/index"; }
Spring會根據參數進行動態綁定,非常方便。然而SpringMVC也提供了一個代理原生ServletAPI對象的接口,WebRequest。
我們甚至可以使用OutputStream作爲處理方法中的入參:
@RequestMapping("/userImage") public void getImage(OutputStream outputStream) throws IOException { Resource resource = new ClassPathResource("/timg.jpeg"); FileCopyUtils.copy(resource.getInputStream(),outputStream); }
訪問相關路徑會獲得一張圖片