Spring MVC配置介紹
一、Spring MVC 縱覽
Spring MVC就是Spring框架對MVC設計模式的實現,通過Spring MVC ,我們可以快速的構建靈活、鬆耦合的web服務。再具體介紹Spring MVC 之前,我們先看一下它的請求處理過程:
1.1 springMVC 的請求過程
1. 請求會首先發送到DispatchServlet,這是spring的前置Servlet,它會接收請求並轉發給spring的MVC controller,也就是業務controller 2. DispatchServlet通過HandlerMapping確定將請求轉發給哪個controller,HandlerMapping主要通過請求中的URL確定對映關係的 3. DispatchServlet將請求轉發給確定的controller之後,controller負責處理這個請求,一般會通過呼叫service層進行業務邏輯處理 4. 當controller處理完請求後,它會把業務處理結果封裝成model,為了使處理結果的model在頁面上更好的展示,controller還會指定展示model對應的view(比如一個JSP頁面),當controller確定了model和view之後,會把它們以請求的形式再轉發給DispatchServlet 5. DispatchServlet通過查詢ViewResolver找到view對應的頁面 6. DispatchServlet最終把model交給頁面進行渲染 7. 頁面對model進行渲染,將結果展示到客戶端,整個請求結束
1.2 配置Spring MVC
其實Spring MVC的核心就是DispatchServlet,配置Spring MVC的過程就是配置DispatchServlet的過程。
1.2.1 配置DispatchServlet
Spring的配置有兩種方式,一種通過web.xml配置,另一種就是通過Java配置,在這裡我們主要講如何在web.xml中配置:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/root-context.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>spring</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
主要配置項:
<context-param>
和<listener>
:配置Spring的RootContext
,對應配置檔案為root-context.xml
<servlet>
和<servlet-mapping>
:配置Spring的WebContext
,對應配置檔案預設為為WEB-INF/{servlet-name}-servlet.xml
可能有些人還不太清楚為什麼要這樣配置,在具體講解之前,先介紹Spring MVC中的兩個context(上下文)。
理解兩個context
我們知道,Spring有一個核心容器也就是ApplicationContext,所有的Spring元件都由這個ApplicationContext進行管理。但是在Web專案中,Spring會維護兩個context:
1. WebContext
第一個Context就是由DispatchServlet建立的WebContext,負責裝載web元件相關的bean,比如controllers、view resolvers、handler mapping等,對應的配置檔案是WEB-INF/{servlet-name}-servlet.xml
,在上面例子中我們配置的Servlet的名字是spring,所以它預設載入spring-servlet.xml配置檔案作為上下文。
但是我們也可以自己指定一個WebContext配置檔案位置,比如指定/WEB-INF/spring/appServlet/servlet-context.xml路徑下的配置檔案:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/appServlet/servlet-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
2. RootContext
RootContext是由ContextListener載入的,它主要裝載除web元件之外的應用程式元件,比如jdbc、mybatis等元件。<context-param>
標籤指定了RootContext配置檔案的位置,並由<listener>
標籤指定的Listener類進行裝載。
1.2.2 啟用Spring MVC
上面的配置其實已經基本ok了,但是一個完整的Spring MVC應用還需要controller、service、view等web元件,所以我們還要在配置中啟用註解及自動包掃描等功能,方便web元件的自動發現,這些應該在上面介紹的WebContext對應的配置檔案中進行配置,也就是在spring-servlet.xml中配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.springmvc.demo" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
主要配置項:
<mvc:annotation-driven/>
標籤作用是開啟註解<context:component-scan/>
標籤目的是啟用自動包掃描,這樣spring框架就會自動掃描被註解的類,納入到WebContext中。
通過上面的配置,Spring MVC的主要配置就已經完成了,我們現在就可以編寫Spring MVC的元件了,我們先從controller開始。
二、寫一個controller
2.1 寫一個簡單的controller
@Controller
public class HomeController {
@RequestMapping(value="/", method= RequestMethod.GET)
public String home() {
return "home";
}
}
宣告controller元件:寫一個controller很簡單,@controller註解聲明當前類是一個controller類,上面配置中我們開啟了ComponentScan,被@controller註解的類會被自動裝載到spring application context中。當然我們使用@component元件效果也是一樣的,只不過@controller更能體現controller角色。
定義請求路徑: HomeController類裡面只有一個home()方法,並且還攜帶一個@RequestMapping註解,註解中的value屬性定義了這個方法的訪問路徑是“/”,method屬性定義了這個方法只處理get請求。
定義渲染view:我們可以看到,home()方法很簡單,僅返回了一個“home”字串。預設情況下,方法返回的字串預設會被解析成view的名稱,DispatcherServlet 會讓ViewResolver解析這個view名稱對應的真是view頁面。我們上面已經配置了一個InternalResourceViewResolver,返回的home會被解析/WEB-INF/views/home.jsp。
寫完controller之後我們可以通過測試類測試一下,寫一個測試controller:
public class HomeControllerTest {
@Test
public void testHomePage() throws Exception {
HomeController controller = new HomeController();
Assert.assertEquals("home", controller.home());
}
@Test
public void testHomePageMVC() throws Exception {
HomeController controller = new HomeController();
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
mockMvc.perform(MockMvcRequestBuilders.get("/")).andExpect(MockMvcResultMatchers.view().name("home"));
}
}
2.2 定義一個類層次的路徑對映
其實@RequestMapping
註解既可以註解方法又可以註解類,當註解類時,訪問類中所有的方法就必須加上類路徑。另外@RequestMapping
中的值可以是一個路徑陣列,當傳入一個數組時,我們可以通過陣列中的任何一個路徑訪問到這個類.
@Controller
@RequestMapping({"/", "/homepage"})
public class HomeController {
...
}
2.3 返回Model資料給view層
我們知道通常情況下,controller處理完業務後會返回結果資料給view層。為此,SpringMVC提供了Model類來封裝結果資料,封裝的過程是自動的,我們只需要在方法中加入Model引數即可:
public String getUsers(Model model) {
model.addAttribute( userService.findSpittles( Long.MAX_VALUE, 20));
return "userView";
}
Model其實就是一個Map,view層會自動解析model中的資料,然後渲染結果給client端。上面的model.addAttribute
方法沒有指定key,key值會被預設設定為物件的型別名,比如上例子中物件型別是List<User>
,則key值預設為:userList
。方法最後返回的String值userView
會直接作為view的名稱。
當然我們也可以明確指定返回資料模型的key值:
public String getUsers(Model model) {
model.addAttribute("userList", userService.findUsers( Long.MAX_VALUE, 20));
return "userView";
}
如果我們不想使用Spring的Model類,我們也可以用java.util.Map類替換Model,這兩個效果是完全一樣的:
public String getUsers(Map map) {
map.addAttribute("userList", userService.findUsers( Long.MAX_VALUE, 20));
return "userView";
}
除了上面兩種方式之外,我們還可以這麼寫:
@RequestMapping(method=RequestMethod.GET)
public List<User> getUsers() {
return userService.findUsers(Long.MAX_VALUE, 20));
}
這種方式比較特殊,我們既沒有設定返回的Model,又沒有指定渲染Model的view,僅僅返回處理的結果物件。遇到這種情況時,Spring MVC會把返回的物件會自動放入Model中,其key就是物件的型別名,即userList
。而對應的view名稱則預設與請求路徑名一致,例如我們的請求路徑是這樣:
@Controller
@RequestMapping("/users")
public class UserController {
... ...
}
那麼對應渲染結果的view就是users
。
三、 處理請求資料
SpringMVC提供了多種資料傳輸方式:
- QueryParameter:查詢引數
- Form Parameters:表單引數
- Path variables:路徑變數
下面我們逐一說明。
3.1 獲取查詢引數
首先什麼是查詢引數呢?比如我們有一個這樣的請求http://localhost:8080/user/queryUsers?pageNo=1&count=5
,在這個請求中,引數值都是通過URL中?後面的引數傳遞過來的,這種方式就是查詢引數傳值。如果要解析查詢引數中的值,我們需要用到@RequestParam註解,它會將請求引數對映到方法引數:
@RequestMapping(value = "/getUsersByPage", method = RequestMethod.GET)
public String getUsersByPage(@RequestParam(value = "pageNo",defaultValue = "1") long pageNo,@RequestParam(value = "count",defaultValue = "5") long count ,Model model) {
model.addAttribute(userService.getAllUsers());
return "userListPageView";
}
需要注意的是,我們在配置請求引數時可以指定引數的預設值,當client端傳過來的引數值不存在或者為空時,就會採用這個預設值,還有一點,因為查詢引數都是String型別,所以這裡的預設值也都是String型別。
除了通過查詢引數傳遞引數值之外,還有一種流行的方式就是通過請求路徑傳遞引數值,特別是在討論構建基於資源的服務時會經常用到這種方式。(注:基於資源的服務可以簡單看做所有請求都是針對資源,所有的返回結果也是資源)
3.2 通過請求路經獲取引數
比如我們有一個根據使用者id查詢使用者資訊的需求,通過上面介紹的方式我們可以這麼做:
@RequestMapping(value = "/queryUser", method = RequestMethod.GET)
public String queryUser(@RequestParam(value = "employeeId") long employeeId,Model model){
model.addAttribute(manager.queryEmployeeVO(employeeId));
return "employee";
}
那麼我們客戶的的請求路徑應該是這樣:/employee/queryEmployee ?employeeId=12345
,儘管也可以滿足需求,但這樣不太符合基於資源的理念。理想的情況是,資源應該是由請求路徑決定的,而不是由請求引數決定。或者說,請求引數不應該被用來描述一個資源。/employee/12345
這種請求方式顯然比/employee/queryEmployee ?employeeId=12345
更能合適,前者定義了要查詢的資源,而後者更強調了通過引數進行操作。
為了達到構建基於資源的controller這個目標,Spring MVC允許請求路徑中包含一個佔位符,佔位符名稱需要用一對{}括起來。在客戶的請求資源時,請求路徑的其它部分還是用來匹配資源路徑,而佔位符部分則直接用來傳輸引數值。下面就是通過佔位符的方式實現路徑中傳遞引數值:
@RequestMapping(value ="/{userId}", method = RequestMethod.GET)
public String queryUser(@PathVariable(value = "userId") long userId, Model model){
model.addAttribute(userService.queryUser(employeeId));
return "userView";
}
上面例子可以看到,方法引數中有一個@PathVariable
的註解,這個註解的意思就是不管請求路徑中佔位符處的值是什麼,它都會被傳遞到@PathVariable
註解的變數中。比如按照上面的配置,如果我們的請求是/employee/12345
,則123456
便會傳入變成userId
的值。需要注意的是,如果方法引數值和佔位符名稱一樣,我們也可以省略@PathVariable
中的屬性值:
@RequestMapping(value ="/{userId}", method = RequestMethod.GET)
public String queryUser(@PathVariable long userId, Model model){
model.addAttribute(userService.queryUser(employeeId));
return "userView";
}
在請求的資料量很小的時候使用查詢引數和路徑引數還是可行的,但是有時候我們請求的資料量會很大,再用上面兩種方式就顯得有點不太合適,這時就需要考慮用第三種方式:表單引數。
四、處理表單資料
一個Web應用不單單只是給使用者展示資料,很多時候它還需要與使用者互動獲取使用者資料,表單就是獲取使用者資料最常見的方式。
比如我們有個登錄檔單:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Spittr</title>
<link rel="stylesheet" type="text/css" >
</head>
<body>
<h1>Register</h1>
<form method="POST">
First Name: <input type="text" name="name" /><br/>
Last Name: <input type="text" name="accountNo" /><br/>
Password: <input type="password" name="password" /><br/>
<input type="submit" value="Register" />
</form>
</body>
</html>
4.1 寫一個接收表單資料的controller
表單都是以post方式提交的,所以我們的controller接受的應該是個post請求:
@Controller
@RequestMapping("/user")
public class RegisterController {
@Autowired
private RegisterService registerService;
@RequestMapping(value="/register", method=RequestMethod.POST)
public String processRegistration(User user) {
registerService.register(user);
return "redirect:/user/" + user.getName();
}
}
在上面例子中,出了接收的請求是post之外,註冊方法還有一個含有User物件的引數,這個User物件中含有name、password、accountNo屬性,SpringMVC會根據名稱從請求中解析相同名稱引數並賦值到物件屬性中。
當註冊方法儲存使用者資訊之後,會返回一個字串redirect:/user/
,這個字串與前面講到的都不同,它返回的並不是一個view的名稱,而是一個redirect重定向請求。因為返回的字串中含有一個redirect:
重定向關鍵字,當InternalResourceViewResolver
類遇到這個關鍵字時,它將會攔截這個字串並把它解析成一個重定向請求而不是view名稱。
InternalResourceViewResolver
除了能夠識別redirect:關鍵字之外,它還能識別forward:
關鍵字,並把包含forward:
關鍵字的字串解析成forward
請求。
4.2 驗證表單
在Spring3.0以後,SpringMVC便支援Java Validation API了,Java Validation API提供了一些註解來約束物件屬性值,這些註解有:
註解 | 解釋 |
---|---|
@AssertFalse | The annotated element must be a Boolean type and be false. |
@AssertTrue | The annotated element must be a Boolean type and be true. |
@DecimalMax | The annotated element must be a number whose value is less than or equal toa given BigDecimalString value. |
@DecimalMin | The annotated element must be a number whose value is greater than orequal to a given BigDecimalString value. |
@Digits | The annotated element must be a number whose value has a specified number of digits. |
@Future | The value of the annotated element must be a date in the future. |
@Max | The annotated element must be a number whose value is less than or equal to a given value. |
@Min | The annotated element must be a number whose value is greater than or equal to a given value. |
@NotNull | The value of the annotated element must not be null. |
@Null | The value of the annotated element must be null. |
@Past | The value of the annotated element must be a date in the past. |
@Pattern | The value of the annotated element must match a given regular expression. |
@Size | The value of the annotated element must be either a String, a collection, or an array whose length fits within the given range. |
我們可以利用這些註解給User物件新增一些驗證,比如非空和字串長度驗證:
public class User {
@NotNull
@Size(min=3,max=20)
private String name;
@NotNull
@Size(min=6,max=20)
private String password;
@NotNull
@Size(min=3,max=20)
private String accountNo;
}
我們還需要通過@Valid
標籤在方法中對User啟用引數驗證:
@RequestMapping(value="/register", method=RequestMethod.POST)
public String processRegistration(@Valid User user, Errors errors) {
if(errors.hasErrors()){
return "register";
}
registerService.register(user);
return "redirect:/user/" + user.getName();
}
在上面例子中,我們在方法中的User引數前面添加了@valid註解,這個註解會告訴Spring框架需要啟用對這個物件的驗證。如果發現任何驗證錯誤,錯誤資訊都將會被封裝到Errors物件中,我們可以通過errors.hasErrors()
方法判斷是否驗證通過。
Spring MVC其它相關文章: