SpringMVC入門筆記

SpringMVC 細(xì)節(jié)方面的東西很多高每,所以在這里做一篇簡單的 SpringMVC 的筆記記錄爷怀,方便以后查看吁朦。

Spring MVC是當(dāng)前最優(yōu)秀的MVC框架逗宜,自從Spring 2.5版本發(fā)布后囤屹,由于支持注解配置冲簿,易用性有了大幅度的提高。Spring 3.0更加完善角钩,實(shí)現(xiàn)了對老牌的MVC框架Struts 2的超越吝沫,現(xiàn)在版本已經(jīng)到了Spring5.x了。

一递礼、工程創(chuàng)建

1. 創(chuàng)建Maven的web工程惨险,添加架包

Maven架包添加 spring-context恭朗、spring-webspring-webmvc趋翻、log4j

2. 在web.xml中配置DispatcherServlet

<servlet>
  <servlet-name>dispatcherServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-mvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>dispatcherServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

注意:這里配置的 <url-pattern>/</url-pattern> 攔截資源配置的是 /寡夹,攔截所有除其他 servlet 之外的資源訪問,包括 jsp通熄、靜態(tài)網(wǎng)頁饿幅、圖片等等。與 /* 不一樣膀斋,/* 一般配在攔截器里面,攔截所有資源訪問览芳。

3. 創(chuàng)建SpringMVC的配置文件

上面配置 DispatcherServlet 里面用到了 contextConfigLocation 配置文件的地址,下面來創(chuàng)建配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<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.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">

    <!-- scan the package and the sub package -->
    <context:component-scan base-package="com.ogemray.springmvc"></context:component-scan>

    <!-- don't handle the static resource -->
    <mvc:default-servlet-handler />

    <!-- if you use annotation you must configure following setting -->
    <mvc:annotation-driven />

    <!-- configure the InternalResourceViewResolver -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 配置首頁跳轉(zhuǎn), 省略了在 Controller 里的創(chuàng)建訪問方法 -->
    <mvc:view-controller path="index" view-name="../index"></mvc:view-controller>

</beans>

二邢羔、@RequestMapping 注解

在對 SpringMVC 進(jìn)行的配置的時(shí)候, 需要我們指定請求與處理方法之間的映射關(guān)系宣虾。 指定映射關(guān)系,就需要我們用上 @RequestMapping 注解淑蔚。
@RequestMapping 是 Spring Web 應(yīng)用程序中最常被用到的注解之一,這個(gè)注解會將 HTTP 請求映射到控制器(Controller類)的處理方法上点骑。

1. value 和 method 屬性

簡單例子

@RequestMapping("rm")
@Controller
public class RequestMappingController {

    @RequestMapping(value = {"home", "/", ""}, method = RequestMethod.GET)
    public String goRMHome() {
        System.out.println("訪問了 Test RequestMapping 首頁");
        return "1-rm";
    }
}

最終訪問路徑是 .../rm/home卿泽,通過該方法返回視圖名字和SpringMVC視圖解析器加工,最終會轉(zhuǎn)發(fā)請求到 .../WEB-INF/jsp/1-rm.jsp 頁面滋觉。
如果沒有類名上面的 @RequestMapping("rm")签夭,則訪問路徑為 .../home
method 指定方法請求類型椎侠,取值有 GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE第租。
value 為數(shù)組字符串,指定訪問路徑與方法對應(yīng)我纪,指定的地址可以是URI慎宾。URI值可以是中:普通的具體值、包含某變量浅悉、包含正則表達(dá)式趟据。

下面以包含某一變量舉例:

@RequestMapping(value = "testPathVariable/{username}", method = RequestMethod.GET)
public String testPathVariable(@PathVariable(value = "username") String name) {
    //參數(shù)部分也可以直接寫成 @PathVariable String username, 省略value, 保證形參名與上面 {} 內(nèi)的名字一致
    //不建議省略
    System.out.println("訪問了 Test PathVariable 方法 username: " + name);
    return "success";
}

2. consumes 屬性

指定處理請求的提交內(nèi)容類型(Content-Type)

@RequestMapping(value = "testConsumes", method = RequestMethod.POST, consumes = "application/x-www-form-urlencoded")
public String testConsumes() {
    System.out.println("訪問了 Test Consumes 方法");
    return "success";
}

如果請求里面的 Content-Type 對不上會報(bào)錯

3. produces 屬性

指定返回的內(nèi)容類型,僅當(dāng)request請求頭中的(Accept)類型中包含該指定類型才返回

其中 */*;q=0.8 表明可以接收任何類型的术健,權(quán)重系數(shù)0.8表明如果前面幾種類型不能正常接收則使用該項(xiàng)進(jìn)行自動分析汹碱。

@RequestMapping(value = "testProduces", method = RequestMethod.POST, produces = "text/html")
public String testProduces() {
    return "success";
}

4. params 屬性

指定request中必須包含某些參數(shù)值,才讓該方法處理

JSP頁面請求

<form action="${pageContext.request.contextPath}/rm/testParams" method="post">
    用戶名: <input type="text" name="username" value="Tom"><br>
    密  碼: <input type="text" name="password" value="123"><br>
    <input type="submit" value="測試 Test Params">
</form>

Controller 里面對應(yīng)的請求方法

@RequestMapping(value = "testParams", method = RequestMethod.POST, params = {"username!=Tom", "password"})
public String testParams() {
    return "success";
}

params = {"username!=Tom", "password"} 表示請求參數(shù)里面 username !=Tom 且有包含 password苛坚,二者有一個(gè)不滿足則會報(bào)錯

5. headers 屬性

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

@RequestMapping(value = "testHeaders", method = RequestMethod.GET, headers = "Accept-Language=zh-CN,zh;q=0.9")
public String testHeaders() {
    return "success";
}

如果跟設(shè)定頭里面對不上會報(bào)404錯誤

三、@RequestParam注解

請求

<a href="${pageContext.request.contextPath}/rp/testGetOneParam?username=Tom">單參數(shù) GET 請求方式</a>
1. 表單元素的name名字和控制器里的方法的形參名一致泼舱,此時(shí)可以省略 @RequestParam 注解
@RequestMapping(value = "testGetOneParam", method = RequestMethod.GET)
public String testGetOneParam(String username) {
    System.out.println("訪問了 單參數(shù) Get 請求方法 username: " + username);
    return "success";
}

2. 不省略時(shí)的寫法

示例

@RequestMapping(value = "testPostOneParam", method = RequestMethod.POST)
public String testPostOneParam(@RequestParam String username) {
    System.out.println("username: " + name);
    return "success";
}

參數(shù)名字不一致時(shí)

@RequestMapping(value = "testPostOneParam", method = RequestMethod.POST)
public String testPostOneParam(@RequestParam(value = "username", required = false, defaultValue = "") String name) {
    System.out.println("username: " + name);
    return "success";
}

value 屬性指定傳過來的參數(shù)名等缀,跟方法里的形參名字對應(yīng)上
required 指定該參數(shù)是否是必須攜帶的
defaultValue 沒有或者為 null 時(shí),指定默認(rèn)值

注:省略和不省略 @RequestParam 注解娇昙,最終SpringMVC內(nèi)部都是使用 RequestParamMethodArgumentResolver 參數(shù)解析器進(jìn)行參數(shù)解析的尺迂。如果省略 @RequestParam 注解或省略 @RequestParam 注解的 value 屬性則最終則以形參的名字作為 keyHttpServletRequest 中取值。

四冒掌、@RequestHeader 和 @CookieValue 注解

@RequestHeader 注解:可以把 Request 請求 header 部分的值綁定到方法的參數(shù)上

@RequestMapping(value = "rh")
@Controller
public class RequestHeaderController {

    @RequestMapping(value = "testRHAccept", method = RequestMethod.GET)
    public String testRHAccept(@RequestHeader(value = "Accept") String accept) {
        System.out.println(accept);
        return "success";
    }

    @RequestMapping(value = "testRHAcceptEncoding", method = RequestMethod.GET)
    public String testRHAcceptEncoding(@RequestHeader(value = "Accept-Encoding") String acceptEncoding) {
        System.out.println(acceptEncoding);
        return "success";
    }
}

@CookieValue 注解:可以把Request header中關(guān)于cookie的值綁定到方法的參數(shù)上

@RequestMapping(value = "cv")
@Controller
public class CookieValueController {
    @RequestMapping(value = "testGetCookieValue", method = RequestMethod.GET)
    public String testGetCookieValue(@CookieValue(value = "JSESSIONID") String cookie) {
        System.out.println("獲取到Cookie里面 JSESSIONID 的值 " + cookie);
        return "success";
    }
}

五噪裕、數(shù)據(jù)結(jié)果封裝 ModelAndView & ModelMap & Map & Model

SpringMVC 為了方便數(shù)據(jù)封裝和處理,提供了以下幾種方案股毫,最終會將封裝到模型里面的數(shù)據(jù)全都通過 request.setAttribute(name, value) 添加request請求域中膳音。

1. ModelAndView

使用 ModelAndView 類用來存儲處理完后的結(jié)果數(shù)據(jù),以及顯示該數(shù)據(jù)的視圖铃诬。從名字上看 ModelAndView 中的 Model 代表模型祭陷,View 代表視圖苍凛。modelModelMap 的類型,而 ModelMap 又是 LinkedHashMap 的子類兵志,view 包含了一些視圖信息醇蝴。

@RequestMapping(value = "testReturnModelAndView", method = RequestMethod.GET)
public ModelAndView testReturnModelAndView() {

    Student s1 = new Student(1, "Tom", 13, new Date());
    Student s2 = new Student(2, "Jerry", 14, new Date());

    List<Student> list = new ArrayList<>();
    list.add(s1); list.add(s2);

    HashMap<String, Student> map = new HashMap<>();
    map.put("s1", s1); map.put("s2", s2);

    ModelAndView mv = new ModelAndView();
    mv.addObject("s1", s1);
    mv.addObject("s2", s2);

    mv.addObject("list", list);
    mv.addObject("map", map);
    mv.setViewName("5-m&v-success");
    return mv;
}

2. ModelMap & Map & Model

最終也是將封裝的數(shù)據(jù)和返回視圖名字封裝成 ModelAndView對象

@RequestMapping(value = "testMapParam", method = RequestMethod.GET)
public String testMapParam(Map<String, Object> paramMap) {
    ...
    paramMap.put("s1", s1);
    paramMap.put("s2", s2);

    paramMap.put("list", list);
    paramMap.put("map", map);
    return "5-m&v-success";
}

@RequestMapping(value = "testModelParam", method = RequestMethod.GET)
public String testModelParam(Model model) {
    ...
    model.addAttribute("s1", s1);
    model.addAttribute("s2", s2);

    model.addAttribute("list", list);
    model.addAttribute("map", map);
    return "5-m&v-success";
}

@RequestMapping(value = "testModelMapParam", method = RequestMethod.GET)
public String testModelMapParam(ModelMap modelMap) {
    ...
    modelMap.addAttribute("s1", s1);
    modelMap.addAttribute("s2", s2);

    modelMap.addAttribute("list",list);
    modelMap.addAttribute("map", map);
    return "5-m&v-success";
}

3. JSP 頁面提取數(shù)據(jù)

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>

    <c:if test="${s1 != null && s2 != null}">
        <h3 align="center">單個(gè)數(shù)據(jù)封裝</h3>
        <table border="1px solid black" style="border-collapse: collapse" align="center">
            <tr><td colspan="2" align="center">s1</td></tr>
            <tr><td>姓名</td><td>${s1.name}</td></tr>
            <tr><td>年齡</td><td>${s1.age}</td></tr>
            <tr><td>生日</td><td>${s1.birthday.toString()}</td></tr>

            <tr><td colspan="2" align="center">s2</td></tr>
            <tr><td>姓名</td><td>${s2.name}</td></tr>
            <tr><td>年齡</td><td>${s2.age}</td></tr>
            <tr><td>生日</td><td>${s2.birthday.toString()}</td></tr>
        </table>
    </c:if>

    <c:if test="${list != null}">
        <h3 align="center">List數(shù)據(jù)封裝</h3>
        <table border="1px solid black" style="border-collapse: collapse" align="center">
            <c:forEach items="${list}" var="s" varStatus="status">
                <tr><td colspan="2" align="center">${status.count}</td></tr>
                <tr><td>姓名</td><td>${s.name}</td></tr>
                <tr><td>年齡</td><td>${s.age}</td></tr>
                <tr><td>生日</td><td>${s.birthday.toString()}</td></tr>
            </c:forEach>
        </table>
    </c:if>

    <c:if test="${map != null}">
        <h3 align="center">Map數(shù)據(jù)封裝</h3>
        <table border="1px solid black" style="border-collapse: collapse" align="center">
            <c:forEach items="${map}" var="node">
                <tr><td colspan="2" align="center">${node.key}</td></tr>
                <tr><td>姓名</td><td>${node.value.name}</td></tr>
                <tr><td>年齡</td><td>${node.value.age}</td></tr>
                <tr><td>生日</td><td>${node.value.birthday.toString()}</td></tr>
            </c:forEach>
        </table>
    </c:if>
</body>
</html>

六、@SessionAttributes

如果我們希望在多個(gè)請求之間共用某個(gè)模型屬性數(shù)據(jù)想罕,則可以在控制器類上標(biāo)注一個(gè) @SessionAttributes悠栓,SpringMVC 將把模型中對應(yīng)的屬性暫存到 HttpSession 的域中。

使用方法:
@SessionAttributes(value={"xxx"}, types={xxxx.class})
value:是通過鍵來指定放入HttpSession 的域中的值按价;
types:是通過類型指定放入HttpSession 的域中的值惭适;

@SessionAttributes(types=Student.class)
這個(gè)注解會將類中所有放入 Request 域中的 Student 對象同時(shí)放進(jìn) HttpSession 的域空間中。

可以添加多個(gè)屬性
@SessionAttributes(value={“s1”, “s2”})
@SessionAttributes(types={User.class, Grade.class})

可以混合使用
@SessionAttributes(value={“s1”, “s2”},types={Grade.class})

示例

//@SessionAttributes(value = {"s1", "s2"})
@SessionAttributes(types = Student.class)
@RequestMapping(value = "sa")
@Controller
public class SessionAttributesController {

    @RequestMapping(value = "testSA", method = RequestMethod.GET)
    public String testSessionAttributes(Model model) {
        Student s1 = new Student(1, "Tom", 13, new Date());
        Student s2 = new Student(2, "Jerry", 13, new Date());

        model.addAttribute("s1", s1);
        model.addAttribute("s2", s2);
        return "6-sa-success";
    }
}

JSP 頁面提取數(shù)據(jù)

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<body>
    request s1 : ${requestScope.get("s1")}<br><br>
    request s2 : ${requestScope.get("s2")}<br><br>

    session s1 : ${sessionScope.get("s1")}<br><br>
    session s2 : ${sessionScope.get("s2")}<br><br>
</body>
</html>

七俘枫、@ModelAttribute

該注解平時(shí)使用的比較多腥沽,不僅可以寫在方法上面也可以寫在參數(shù)前面。

1. @ModelAttribute 寫在方法上面

  • 在同一個(gè)控制器中鸠蚪,標(biāo)注了@ModelAttribute 的方法實(shí)際上會在 @RequestMapping 注解方法之前被調(diào)用今阳。
  • 標(biāo)注了@ModelAttribute 的方法能接受與@RequestMapping 標(biāo)注相同的參數(shù)類型,只不過不能直接被映射到具體的請求上茅信。
  • 標(biāo)注在方法上的 @ModelAttribute 說明方法一般是用于添加一個(gè)或多個(gè)屬性到 model 上盾舌。

模擬請求

<a href="${pageContext.request.contextPath}/testModelAttribute">模擬請求</a>
① 省略 value 屬性值手動加入屬性
@ModelAttribute
public void modelAttributeMethod1(ModelMap modelMap) {
    Person person = new Person("超哥哥 1 號", 12);
    modelMap.addAttribute("person1", person);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(ModelMap modelMap) {
    modelMap.forEach((key, value) -> {
        System.out.println(key + " = " + value);
        //person1 = Person{name='超哥哥 1 號', age=12}
    });
    return "success";
}

可以看出手動加入 model 里面屬性成功,key 為自定義的字符串蘸鲸。

② 省略 value 屬性值自動加入屬性
@ModelAttribute
public Person modelAttributeMethod2() {
    return new Person("超哥哥 2 號", 12);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(ModelMap modelMap) {
    modelMap.forEach((key, value) -> {
        System.out.println(key + " = " + value);
        //person = Person{name='超哥哥 2 號', age=12}
    });
    return "success";
}

可以看出 @ModelAttribute 修飾的方法沒有指定 value 屬性時(shí)妖谴,讓其自動加入的 key 是以添加類的類名首字母小寫。

③ 指明 value 屬性值自動加入屬性
@ModelAttribute(value = "person3")
public Person modelAttributeMethod3() {
    return new Person("超哥哥 3 號", 13);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(ModelMap modelMap) {
    modelMap.forEach((key, value) -> {
        System.out.println(key + " = " + value);
        //person3 = Person{name='超哥哥 3 號', age=13}
    });
    return "success";
}

從上面可以看出 @ModelAttribute 修飾的方法有指定 value 屬性時(shí)酌摇,讓其自動加入的 key 就是自定的 value 屬性的值膝舅。

2. @ModelAttribute 寫在參數(shù)前面

標(biāo)注在方法參數(shù)前的 @ModelAttribute 說明了該方法參數(shù)的值將由 model 中取得,如果 model 中找不到窑多,那么該參數(shù)會先被實(shí)例化仍稀,然后被添加到 model 中。在 model 中存在以后埂息,將請求中所有名稱匹配的參數(shù)都填充到該參數(shù)對象上技潘。

模擬請求

<a href="${pageContext.request.contextPath}/testModelAttribute?age=13">模擬請求</a>
① 省略 value 屬性值自動匹配或創(chuàng)建
@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(@ModelAttribute Person person) {
    System.out.println(person);
    //Person{name='null', age=13}
    return "success";
}

注:在執(zhí)行 testModelAttribute(..) 方法時(shí),因?yàn)閰?shù)屬性是一個(gè) Person 類對象千康,那么他先從 model 里面找(沒有指明 value 屬性值享幽,則以該類名首字母小寫為 key),發(fā)現(xiàn)找不到便創(chuàng)建一個(gè)拾弃,把請求里面的參數(shù)賦值到該創(chuàng)建對象上值桩,找到了則用請求里面的參數(shù)更新該對象。

② 指定 value 屬性值匹配或創(chuàng)建
@ModelAttribute(value = "p")
public Person modelAttributeMethod3(@RequestParam Integer age) {
    return new Person("超哥哥 3 號", age);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(@ModelAttribute(value = "p") Person person) {
    System.out.println(person);
    //Person{name='超哥哥 3 號', age=13}
    return "success";
}

注:在執(zhí)行 testModelAttribute(..) 方法時(shí)豪椿,因?yàn)閰?shù)屬性是一個(gè) Person 類對象奔坟,那么他先從 model 里面找(有指明 value 屬性值斯入,則以 value 屬性值為 key),發(fā)現(xiàn)找不到便創(chuàng)建一個(gè)蛀蜜,把請求里面的參數(shù)賦值到該創(chuàng)建對象上,找到了則用請求里面的參數(shù)更新該對象增蹭。

③ 省略 @ModelAttribute 注解的 POJO 參數(shù)
@ModelAttribute
public Person modelAttributeMethod3(@RequestParam Integer age) {
    return new Person("超哥哥 4 號", age);
}

@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(Person person) {
    System.out.println(person);
    //Person{name='超哥哥 4 號', age=13}
    return "success";
}

注:@ModelAttribute 注解修飾的方法滴某,沒有指定 value 屬性,則自動注入到 model 里面的 value 以該對象類名首字母小寫為 key滋迈。在下面 @RequestMapping 修飾的方法 testModelAttribute(..) 參數(shù)時(shí)一個(gè) POJO 對象霎奢,雖前面沒有注解修飾,但默認(rèn)也會去匹配 ModelAttributeMethodProcessor 參數(shù)解析器去解析該參數(shù)饼灿,說白了與上面的第一種情況 @ModelAttribute 注解修飾沒有設(shè)置 value 屬性值是一樣的幕侠。

八、在Controller中使用redirect方式處理請求

forword:表示轉(zhuǎn)發(fā)碍彭!
redirect:表示重定向晤硕!

@RequestMapping(value = "index")
public String index() {
    return "success";
}
@RequestMapping(value = "index")
public String index() {
    return "redirect:success";
}

九、RESTFul 風(fēng)格的 SpringMVC

1. RESTFulController

@RequestMapping(value = "rest")
@Controller
public class RESTFulController {

    @RequestMapping(value = {"home", "/", ""}, method = RequestMethod.GET)
    public String goResetHome() {
        System.out.println("訪問了 Rest 風(fēng)格測試首頁");
        return "8-rest";
    }

    @RequestMapping(value = "student/{id}", method = RequestMethod.GET)
    public String get(@PathVariable(value = "id") Integer id) {
        System.out.println("get " + id);
        return "success";
    }

    @RequestMapping(value = "student/{id}", method = RequestMethod.POST)
    public String post(@PathVariable(value = "id") Integer id) {
        System.out.println("post " + id);
        return "success";
    }

    @RequestMapping(value = "student/{id}", method = RequestMethod.PUT)
    public String put(@PathVariable(value = "id") Integer id) {
        System.out.println("put " + id);
        return "success";
    }

    @RequestMapping(value = "student/{id}", method = RequestMethod.DELETE)
    public String delete(@PathVariable(value = "id") Integer id) {
        System.out.println("delete " + id);
        return "success";
    }
}

2. form表單發(fā)送put和delete請求庇忌,需要在web.xml中進(jìn)行如下配置

<!-- configure the HiddenHttpMethodFilter,convert the post method to put or delete -->
<filter>
  <filter-name>hiddenHttpMethodFilter</filter-name>
  <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>hiddenHttpMethodFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

3. 在前臺可以用以下代碼產(chǎn)生請求

<form action="${pageContext.request.contextPath}/rest/student/1" method="get">
    <input type="submit" value="GET">
</form>

<form action="${pageContext.request.contextPath}/rest/student/1" method="post">
    <input type="submit" value="POST">
</form>

<form action="${pageContext.request.contextPath}/rest/student/1" method="post">
    <input type="hidden" name="_method" value="PUT">
    <input type="submit" value="PUT">
</form>

<form action="${pageContext.request.contextPath}/rest/student/1" method="post">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="DELETE">
</form>

十舞箍、@RequestBody 和 @ResponseBody

在SpringMVC的 Controller 中經(jīng)常會用到 @RequestBody@ResponseBody 這兩個(gè)注解,若想使用這兩個(gè)注解皆疹,前提要寫好 <mvc:annotation-driven /> 標(biāo)簽疏橄,他會幫我們注入接下里解析需要的轉(zhuǎn)換器。

1. @RequestBody

簡介:
@RequestBody 注解用于修飾 Controller 的方法參數(shù)略就,根據(jù) HTTP Request Header 的 content-Type 的內(nèi)容捎迫,通過適當(dāng)?shù)?HttpMessageConverter 轉(zhuǎn)換為 Java 類。

使用時(shí)機(jī):
當(dāng)提交的數(shù)據(jù)不是普通表單的形式(application/x-www-form-urlcoded表牢、multipart/form-data)窄绒,而是 JSON 格式(application/json) 或 XML 格式(application/xml)。

使用示例:XML格式數(shù)據(jù)提交

POJO 模型類

@XmlRootElement(name = "person")
public class Person {
    private String name;
    private Integer age;

    public String getName() { return name; }
    @XmlElement
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    @XmlElement
    public void setAge(Integer age) { this.age = age; }
}

AJAX 請求

<a id="tag" href="${pageContext.request.contextPath}/testRequestBody">點(diǎn)擊事件</a>

<script type="text/javascript">
    $("#tag").click(function () {
        var arg =
            "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" +
                "<person>" +
                    "<name>Tom</name>" +
                    "<age>13</age>" +
                "</person>";
        $.ajax({
            url: this.href,
            type: "POST",
            data: arg,
            contentType: "application/xml;charset=utf-8",
            success: function (data, textStatus) {  },
            error: function (data, textStatus, errorThrown) {  }
        });
        return false;
    });
</script>

Controller 里對應(yīng)的方法

@RequestMapping(value = "testRequestBody", method = RequestMethod.POST)
public String testRequestBody(@RequestBody Person person) {
    System.out.println(person);
    //Person{name='Tom', age=13}
    return "success";
}

注:@RequestBody 注解對于XML請求數(shù)據(jù)的解析初茶,請求方要指定 Content-Type = application/xml;charset=utf-8颗祝,服務(wù)器如果要將接收數(shù)據(jù)封裝成 POJO 類,需要在該 POJO 類里面用 @XmlRootElement@XmlElement 注解指明跟標(biāo)簽和子標(biāo)簽恼布,SpringMVC 內(nèi)部最終用到的是自帶的 Jaxb2RootElementHttpMessageConverter 轉(zhuǎn)換器(其實(shí)現(xiàn)了 HttpMessageConverter 接口)螺戳。

2. @ResponseBody

簡介:
@ResponseBody 注解用于修飾 Controller 的方法蜡励,根據(jù) HTTP Request Header 的 Accept 的內(nèi)容丸氛,通過適當(dāng)?shù)?HttpMessageConverter 轉(zhuǎn)換為客戶端需要格式的數(shù)據(jù)并且寫入到 Responsebody 數(shù)據(jù)區(qū),從而不通過視圖解析器直接將數(shù)據(jù)響應(yīng)給客戶端修壕。

使用時(shí)機(jī):
返回的數(shù)據(jù)不是html標(biāo)簽的頁面爽待,而是其他某種格式的數(shù)據(jù)時(shí)(如json损同、xml等)使用翩腐。

使用示例:XML格式數(shù)據(jù)響應(yīng)

POJO 模型類

@XmlRootElement(name = "person")
public class Person {
    private String name;
    private Integer age;

    public String getName() { return name; }
    @XmlElement
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    @XmlElement
    public void setAge(Integer age) { this.age = age; }
}

Controller 里對應(yīng)的方法

@ResponseBody
@RequestMapping(value = "testRequestBody", method = RequestMethod.POST)
public Person testRequestBody() {
    Person person = new Person("Tom",13);
    return person;
}

AJAX 請求

<a id="tag" href="${pageContext.request.contextPath}/testRequestBody">點(diǎn)擊事件</a>

<script type="text/javascript">
    $("#tag").click(function () {
        $.ajax({
            url: this.href,
            type: "POST",
            data: null,
            headers: { Accept: "application/xml;charset=utf-8" },
            success: function (data, textStatus) {
                console.log(textStatus);
                console.log(data);
            },
            error: function (data, textStatus, errorThrown) {
                console.log(textStatus + "   " + data + "  " + errorThrown);
            }
        });
        return false;
    });
</script>

最終瀏覽器控制臺輸出

注:@ResponseBody 注解對于響應(yīng)XML格式數(shù)據(jù)的解析,請求方要指定 Accept = application/xml;charset=utf-8膏燃,服務(wù)器如果想將 POJO 類轉(zhuǎn)換成XML格式數(shù)據(jù)茂卦,需要在該 POJO 類里面用 @XmlRootElement@XmlElement 注解指明跟標(biāo)簽和子標(biāo)簽,SpringMVC 內(nèi)部最終用到的是自帶的 Jaxb2RootElementHttpMessageConverter 轉(zhuǎn)換器(其實(shí)現(xiàn)了 HttpMessageConverter 接口)组哩。

3. 原理簡介

@RequestBody@ResponseBody 注解最終匹配到的參數(shù)解析器和返回值解析器都是 RequestResponseBodyMethodProcessor 對象等龙,所以該對象分別實(shí)現(xiàn)了 HandlerMethodArgumentResolverHandlerMethodReturnValueHandler 接口。
在該解析器中有一個(gè) messageConverters 屬性伶贰,該屬性是用來記錄轉(zhuǎn)換器的 List蛛砰,這些轉(zhuǎn)換器都是在該解析器初始化的時(shí)候 <mvc:annotation-driven /> 標(biāo)簽幫我們注入的。并且這些解析器都實(shí)現(xiàn)了 HttpMessageConverter 接口黍衙,在 HttpMessageConverter 接口中有四個(gè)最為主要的接口方法泥畅。

public interface HttpMessageConverter<T> {
    boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage);

    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage);
}

read 對應(yīng)請求輸入的轉(zhuǎn)換解析,write 對應(yīng)響應(yīng)輸出的轉(zhuǎn)換解析琅翻。
canRead 根據(jù) Request Header 的 content-Type 的內(nèi)容查看該 HttpMessageConverter 換器是否支持轉(zhuǎn)換位仁,支持則轉(zhuǎn)換為對應(yīng)的 Java 類綁定到修飾的方法入?yún)⑸稀?br> canWrite 根據(jù) Request Headers 里面的 Accept 的內(nèi)容查看該 HttpMessageConverter 換器是否支持轉(zhuǎn)換,支持則轉(zhuǎn)換成指定格式后方椎,寫入到 Response 對象的 body 數(shù)據(jù)區(qū)障癌。

對應(yīng)流程圖如下

十一、解析和返回 Json 數(shù)據(jù)

1. 首先需要導(dǎo)入 JSON 支持架包并且注入轉(zhuǎn)換器

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.6</version>
</dependency>

jackson-databind-2.9.6.jar 架包依賴于 jackson-annotations-2.9.0.jarjackson-core-2.9.6.jar辩尊,所以省略了依賴架包的手動導(dǎo)入涛浙。

同時(shí)要寫好 <mvc:annotation-driven /> 標(biāo)簽,其會幫我們注入對應(yīng)的JSON數(shù)據(jù)轉(zhuǎn)換器摄欲。

2. 代碼示例

需要封裝的 POJO

public class Person {
    private String name;
    private Integer age;
}

Controller中對應(yīng)的請求方法

@ResponseBody
@RequestMapping(value = "testRequestBody", method = RequestMethod.POST)
public Person testRequestBody(@RequestBody Person person) {
    System.out.println(person);
    return person;
}

注:參數(shù)用 @RequestBody 修飾意思是將請求的JSON數(shù)據(jù)用合適的轉(zhuǎn)換器轿亮,轉(zhuǎn)換成 Java 類。@ResponseBody 注解是將返回的數(shù)據(jù)通過合適的轉(zhuǎn)換器轉(zhuǎn)換成客戶端想要的樣子并返回胸墙,在這里是將請求解析的 Person 對象轉(zhuǎn)換成JOSN格式數(shù)據(jù)并返回我注。

AJAX 請求

<a id="tag" href="${pageContext.request.contextPath}/testRequestBody">點(diǎn)擊事件</a>

<script type="text/javascript">
    $("#tag").click(function () {
        var arg = {name : "Tom", age : "10"};
        $.ajax({
            url: this.href,
            type: "POST",
            data: JSON.stringify(arg),
            contentType: "application/json;charset=utf-8",
            headers: { Accept: "application/json;charset=utf-8" },
            success: function (data, textStatus) {
                console.log(textStatus);
                console.log(data);
            },
            error: function (data, textStatus, errorThrown) {
                console.log(textStatus + "   " + data + "  " + errorThrown);
            },
        });
        return false;
    });
</script>

注:① 發(fā)送的數(shù)據(jù)要是JSON格式(也就是 data 屬性的數(shù)據(jù)是JSON格式);② 指明請求數(shù)據(jù)為JSON格式(contentType: "application/json;charset=utf-8")迟隅;③ 指明接收數(shù)據(jù)為JSON格式(headers: { Accept: "application/json;charset=utf-8" })但骨。

3. 原理簡介

最終使用到的轉(zhuǎn)換器是 jackson 提供的 MappingJackson2HttpMessageConverter,也是在解析器初始化的時(shí)候 <mvc:annotation-driven /> 標(biāo)簽幫我們注入的智袭。

十二奔缠、文件上傳

1. 導(dǎo)入文件上傳支持架包

為了實(shí)現(xiàn)文件上傳,需要導(dǎo)入 commons-fileupload 架包吼野,導(dǎo)入如下

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>

2. 配置 MultipartResolver

SpringMVC 上下文中默認(rèn)沒有裝配 MultipartResolver校哎,因此默認(rèn)情況下其不能處理文件上傳工作。如果想使用SpringMVC的文件上傳功能,則需要在上下文中配置 MultipartResolver闷哆。在SpringMVC配置文件中進(jìn)行如下配置

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 請求的編碼格式腰奋,必須和 jsp 的 pageEncoding 屬性一致,默認(rèn)為ISO-8859-1 -->
    <property name="defaultEncoding" value="utf-8"></property>
    <!-- 上傳最大限制 1M = 1M * 1024 * 1024 = 1048576 byte-->
    <property name="maxUploadSize" value="1048576"></property>
</bean>

注:這里一定要設(shè)置 id抱怔,并且值必須是 multipartResolver劣坊,下面的簡單原理會解釋。

3. 代碼示例

Controller 中對應(yīng)的方法

@RequestMapping(value = "upload", method = RequestMethod.POST)
public String testUpload(@RequestParam(value = "file") MultipartFile multipartFile, HttpServletRequest request) throws Exception {
    if (multipartFile.isEmpty() == false) {
        //multipartFile.getName()   標(biāo)簽名字
        //multipartFile.getOriginalFilename()  上傳文件名字
        //multipartFile.getSize()   上傳文件大小
        //multipartFile.getContentType()    上傳文件類型

        //在 webapp 目錄下面(項(xiàng)目目錄下面) 建立一個(gè) resources 資源文件夾, 用來存儲上傳的資源文件
        String parent = request.getServletContext().getRealPath("/resources");
        String filename = UUID.randomUUID() + multipartFile.getOriginalFilename();

        File file = new File(parent, filename);
        multipartFile.transferTo(file);
    }
    return "success";
}

JSP頁面的可變表單請求

<form action="${pageContext.request.contextPath}/upload" 
      enctype="multipart/form-data" 
      method="post">
    <input type="file" name="file" value="請選擇需要上傳的文件" /><br>
    <input type="submit" value="提交">
</form>

4. 原理簡介

DispatcherServlet 初始化的時(shí)候屈留,會從容器中加載 MultipartResolver 可變表單解析器讼稚,從下面源碼中可以看出加載條件就是 idname 為 multipartResolver 的 bean

接著簡單了解下解析绕沈,在 DispatcherServletdoDispatch(..) 方法中檢查該請求是否是可變表單請求,如果是則用加載到緩存的 MultipartResolver 解析器 (這里用到的是注入容器中的 CommonsMultipartResolver 可變表單解析器帮寻,其實(shí)現(xiàn)了 MultipartResolver 接口) 將可變請求解析成 MultipartFile 對象 (這里是 CommonsMultipartFile乍狐,其實(shí)現(xiàn)了MultipartFile 接口),放在 HttpServletRequest 對象中固逗,最終通過合適的參數(shù)解析器綁定到對應(yīng)方法的參數(shù)上浅蚪。

十三、文件下載

SpringMVC提供了一個(gè) ResponseEntity 類型烫罩,使用它可以很方便地定義返回的 HttpHeadersHttpStatus惜傲。
以下代碼演示文件的下載功能

@RequestMapping(value = "download", method = RequestMethod.GET)
public ResponseEntity<byte[]> testDownload(HttpServletRequest request, @RequestParam String filename) throws Exception {

    String parent = request.getServletContext().getRealPath("/resources");
    File file = new File(parent, filename);

    byte[] body = FileUtils.readFileToByteArray(file);

    String downloadFilename = new String(file.getName().getBytes("utf-8"), "iso-8859-1");

    HttpHeaders headers = new HttpHeaders();
    //設(shè)置文件類型
    headers.add("Content-Disposition", "attchement;filename=" + downloadFilename);

    ResponseEntity responseEntity = new ResponseEntity(body, headers, HttpStatus.OK);
    return responseEntity;
}

十四、攔截器

SpringMVC的處理器攔截器贝攒,類似于 Servlet 開發(fā)中的過濾器 Filter盗誊,用于對處理器進(jìn)行預(yù)處理和后處理。

1. 過濾器與攔截器區(qū)別

  • 過濾器:依賴于servlet容器隘弊,在實(shí)現(xiàn)上基于函數(shù)回調(diào)哈踱,可以對幾乎所有請求進(jìn)行過濾,但是缺點(diǎn)是一個(gè)過濾器實(shí)例只能在容器初始化時(shí)調(diào)用一次梨熙。使用過濾器的目的是用來做一些過濾操作开镣,比如:在過濾器中修改字符編碼;在過濾器中修改HttpServletRequest的一些參數(shù)咽扇,包括:過濾低俗文字邪财、危險(xiǎn)字符等。
  • 攔截器:依賴于web框架质欲,在實(shí)現(xiàn)上基于Java的反射機(jī)制树埠,屬于面向切面編程(AOP)的一種運(yùn)用。由于攔截器是基于web框架的調(diào)用嘶伟,因此可以使用Spring的依賴注入(DI)進(jìn)行一些業(yè)務(wù)操作弥奸,同時(shí)一個(gè)攔截器實(shí)例在一個(gè) Controller 生命周期之內(nèi)可以多次調(diào)用。

2. 攔截器接口

攔截器一個(gè)有3個(gè)回調(diào)方法奋早,而一般的過濾器Filter才兩個(gè):

  • preHandle預(yù)處理回調(diào)方法盛霎,實(shí)現(xiàn)處理器的預(yù)處理赠橙。返回值:true表示繼續(xù)流程(如調(diào)用下一個(gè)攔截器或處理器);false表示流程中斷愤炸,不會繼續(xù)調(diào)用其他的攔截器或處理器期揪,此時(shí)我們需要通過 response 來產(chǎn)生響應(yīng);
  • postHandle后處理回調(diào)方法规个,實(shí)現(xiàn)處理器的后處理(但在渲染視圖之前)凤薛,此時(shí)我們可以通過 modelAndView(模型和視圖對象)對模型數(shù)據(jù)進(jìn)行處理或?qū)σ晥D進(jìn)行處理。
  • afterCompletion整個(gè)請求處理完畢回調(diào)方法诞仓,即在視圖渲染完畢時(shí)回調(diào)缤苫,如性能監(jiān)控中我們可以在此記錄結(jié)束時(shí)間并輸出消耗時(shí)間,還可以進(jìn)行一些資源清理墅拭,類似于 try-catch-finally 中的finally活玲。

3. 代碼編寫

有時(shí)候我們可能只需要實(shí)現(xiàn)三個(gè)回調(diào)方法中的某一個(gè),如果實(shí)現(xiàn)HandlerInterceptor 接口的話谍婉,三個(gè)方法必須實(shí)現(xiàn)舒憾,此時(shí) SpringMVC 提供了一個(gè) HandlerInterceptorAdapter 適配器(一種適配器設(shè)計(jì)模式的實(shí)現(xiàn)),允許我們只實(shí)現(xiàn)需要的回調(diào)方法穗熬,該適配器內(nèi)部實(shí)現(xiàn)了 HandlerInterceptor 接口镀迂。

先寫兩個(gè)攔截器

public class HandlerInterceptor1 extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("HandlerInterceptor1 preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("HandlerInterceptor1 postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("HandlerInterceptor1 afterCompletion");
    }
}
public class HandlerInterceptor2 extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("HandlerInterceptor2 preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("HandlerInterceptor2 postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("HandlerInterceptor2 afterCompletion");
    }
}

攔截器的注入

<mvc:interceptors>
    <bean class="com.ogemray.interceptor.HandlerInterceptor1"></bean>
    <bean class="com.ogemray.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptors>

Controller 方法編寫

@RequestMapping(value = "/hello")
public String testHello() {
    System.out.println("HelloController.testHello");
    return "success";
}

最終輸出看下執(zhí)行順序

HandlerInterceptor1 preHandle
HandlerInterceptor2 preHandle
HelloController.testHello
HandlerInterceptor2 postHandle
HandlerInterceptor1 postHandle
HandlerInterceptor2 afterCompletion
HandlerInterceptor1 afterCompletion

4. 運(yùn)行流程圖

SpringMVC攔截器執(zhí)行流程.jpg

5. 選擇性攔截注入

有的時(shí)候我們需要攔截器攔截指定的請求,這樣也是可以配置的

<mvc:interceptors>
    <mvc:interceptor>
        <!-- 攔截對應(yīng) /hello 路徑下的所有請求 -->
        <mvc:mapping path="/hello/*"/>
        <!-- 除去 /hello/test2 這個(gè)請求 -->
        <mvc:exclude-mapping path="/hello/test2"></mvc:exclude-mapping>
        <bean class="com.ogemray.interceptor.HandlerInterceptor1"></bean>
    </mvc:interceptor>

    <mvc:interceptor>
        <!-- /* 是一級目錄下的路徑; /** 不分目錄等級, 即所有請求 -->
        <mvc:mapping path="/**"/>
        <bean class="com.ogemray.interceptor.HandlerInterceptor2"></bean>
    </mvc:interceptor>
</mvc:interceptors>

十五唤蔗、異常處理

在SpringMVC中探遵,所有用于處理在請求映射和請求處理過程中拋出的異常的類,都要實(shí)現(xiàn) HandlerExceptionResolver 接口妓柜。
一個(gè)基于SpringMVC的Web應(yīng)用程序中别凤,可以存在多個(gè)實(shí)現(xiàn)了 HandlerExceptionResolver 的異常處理類,他們的執(zhí)行順序是由其 order 的值從小到大來先后執(zhí)行领虹,直到遇到返回的 ModelAndView 不為空則終斷接下來的異常解析器的執(zhí)行并返回異常的 ModelAndView 對象规哪。

<mvc:annotation-driven />標(biāo)簽會幫我們注入常用的三個(gè)異常解析器:ExceptionHandlerExceptionResolverResponseStatusExceptionResolver塌衰、DefaultHandlerExceptionResolver诉稍。

但是我們接下來重點(diǎn)是了解下常用的兩個(gè)異常解析器,分別是:ExceptionHandlerExceptionResolverSimpleMappingExceptionResolver最疆。

1. ExceptionHandlerExceptionResolver

注意 @ExceptionHandler 注解修飾的方法里面杯巨,只能自己 newModelAndView 對象然后裝入需要的注入的值,對于傳參里面帶的 ModelModelMap 達(dá)不到傳值要求努酸。

① 異常處理方法寫在對應(yīng)的類里面

這樣只能處理該 Controller 里面的異常

處理該 Controller 里面所有的異常服爷,在沒有找到指定的異常類對應(yīng)的處理方法的前提下

@ExceptionHandler
public ModelAndView handlerAllException(Exception e) {
    ModelAndView mv = new ModelAndView();
    mv.addObject("exceptionMsg", e.getMessage());
    mv.setViewName("error");
    System.out.println("HelloController.handlerAllException");
    return mv;
}

處理該 Controller 里面指定類型的異常

@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView handlerArithmeticException(Exception e) {
    ModelAndView mv = new ModelAndView();
    mv.addObject("exceptionMsg", e.getMessage());
    mv.setViewName("error");
    System.out.println("HelloController.handlerArithmeticException");
    return mv;
}
② 異常處理方法寫在單獨(dú)的異常處理類里面

這樣可以處理所有 Controller 的異常,而不是針對單個(gè)的 Controller 類,類上需要用 @ControllerAdvice 注解修飾仍源。

@ControllerAdvice
public class HandlerException {
    @ExceptionHandler
    public ModelAndView handlerAllException(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("exceptionMsg", e.getMessage());
        mv.setViewName("error");
        System.out.println("HelloController.handlerAllException");
        return mv;
    }
    @ExceptionHandler(value = {ArithmeticException.class})
    public ModelAndView handlerArithmeticException(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("exceptionMsg", e.getMessage());
        mv.setViewName("error");
        System.out.println("HelloController.handlerArithmeticException");
        return mv;
    }
}

2. SimpleMappingExceptionResolver

不用自己寫java類處理異常心褐,直接配置就可以了

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!-- 指定注入異常屬性的key, 默認(rèn)為 "exception" -->
    <property name="exceptionAttribute" value="ex"></property>
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
        </props>
    </property>
</bean>

十六、整合SpringIOC和SpringMVC

  1. 在 web.xml 中配置 contextLoaderListener笼踩,并且加入spring的配置文件 applicationContext.xml
    這樣可以把 service逗爹、dao、事務(wù)嚎于、緩存掘而、以及和其它框架的整合放到 spring 的配置文件里面
    web.xml 文件配置如下
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>

    <!-- configure the spring -->
    <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>
    
    <!-- configure the spring mvc -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
  1. 在 web.xml 中配置 SpringMVC 的 Servlet 和加入 springmvc.xml,這時(shí)兩個(gè)配置文件中掃描的包有重合的時(shí)候出現(xiàn)某些bean會被初始化2次的問題于购。
    解決:在掃描包的子節(jié)點(diǎn)下配置exclude-filterinclude-filter

SpringMVC 只掃描 @Controller@ControllerAdvice

<context:component-scan base-package="com.ogemray.springmvc">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>

Spring排除掃描 @Controller@ControllerAdvice

<context:component-scan base-package="com.ogemray.springmvc">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>

注意:Spring 和 SpringMVC 都有一個(gè) IOC 容器袍睡,并且Controller 類的 bean 在 SpringMVC 的 IOC 容器中,但是它可以引用 Spring 的 IOC 容器中的 bean 如 service 和 dao 層的 bean肋僧,反之則不行斑胜,因?yàn)?Spring IOC 容器和 SpringMVC IOC 容器是父子關(guān)系,相當(dāng)于全局變量和局部變量的關(guān)系色瘩!

十七、SpringMVC運(yùn)行流程

SpringMVC運(yùn)行流程.jpg

其他相關(guān)文章

SpringMVC入門筆記
SpringMVC工作原理之處理映射[HandlerMapping]
SpringMVC工作原理之適配器[HandlerAdapter]
SpringMVC工作原理之參數(shù)解析
SpringMVC之自定義參數(shù)解析
SpringMVC工作原理之視圖解析及自定義
SpingMVC之<mvc:annotation-driven/>標(biāo)簽

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逸寓,一起剝皮案震驚了整個(gè)濱河市居兆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌竹伸,老刑警劉巖泥栖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勋篓,居然都是意外死亡吧享,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門譬嚣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钢颂,“玉大人,你說我怎么就攤上這事拜银∈獗蓿” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵尼桶,是天一觀的道長操灿。 經(jīng)常有香客問我,道長泵督,這世上最難降的妖魔是什么趾盐? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上救鲤,老公的妹妹穿的比我還像新娘久窟。我一直安慰自己,他們只是感情好蜒简,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布瘸羡。 她就那樣靜靜地躺著,像睡著了一般搓茬。 火紅的嫁衣襯著肌膚如雪犹赖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天卷仑,我揣著相機(jī)與錄音峻村,去河邊找鬼。 笑死锡凝,一個(gè)胖子當(dāng)著我的面吹牛粘昨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播窜锯,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼张肾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锚扎?” 一聲冷哼從身側(cè)響起吞瞪,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驾孔,沒想到半個(gè)月后芍秆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翠勉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年妖啥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片对碌。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荆虱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出朽们,到底是詐尸還是另有隱情克伊,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布华坦,位于F島的核電站愿吹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏惜姐。R本人自食惡果不足惜犁跪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一椿息、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坷衍,春花似錦寝优、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至迁杨,卻和暖如春钻心,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铅协。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工捷沸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狐史。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓痒给,卻偏偏與公主長得像,于是被迫代替她去往敵國和親骏全。 傳聞我的和親對象是個(gè)殘疾皇子苍柏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容