1. 程式人生 > >Spring MVC配置介紹

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>

主要配置項:

  1. <context-param><listener>:配置Spring的RootContext,對應配置檔案為root-context.xml
  2. <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>

主要配置項:

  1. <mvc:annotation-driven/>標籤作用是開啟註解
  2. <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其它相關文章: