SpringMVC入門(mén)-創(chuàng)新互聯(lián)

Spring MVC 概述

Spring MVC 介紹

Spring 為表現(xiàn)層提供的基于 MVC 設(shè)計(jì)理念的 Web 框架。

創(chuàng)新互聯(lián)公司專注于古田企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,成都商城網(wǎng)站開(kāi)發(fā)。古田網(wǎng)站建設(shè)公司,為古田等地區(qū)提供建站服務(wù)。全流程定制設(shè)計(jì),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)

Spring MVC 通過(guò)一套 MVC 注解,讓 POJO 成為處理器請(qǐng)求的控制器,而無(wú)需實(shí)現(xiàn)任何接口。

支持 REST 風(fēng)格的 url 請(qǐng)求。

采用松散耦合可插拔組件結(jié)構(gòu),比其他 MVC 框架更具有擴(kuò)展性和靈活性。
Spring MVC 入門(mén)

永遠(yuǎn)的 Hello World

首先,是要導(dǎo)入 jar 包

  • servlet-api-x.y.z.jar
  • commons-logging-x.y.z.jar
  • spring-aop-x.y.z.jar
  • spring-beans-x.y.z.jar
  • spring-context-x.y.z.jar
  • spring-core-x.y.z.jar
  • spring-expression-x.y.z.jar
  • spring-webmvc-x.y.z.jar
  • spring-web-x.y.z.jar

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<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_4_0.xsd"
         version="4.0">
    <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:application.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>

application.xml

<?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"
       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">
    <context:component-scan base-package="com.kernel.spring.web"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

HelloController.java

package com.kernel.spring.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        return "/success";
    }
}

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h2>SUCCESS!</h2>
</body>
</html>

HelloWorld 深度解析

  1. 當(dāng)用戶請(qǐng)求 /hello 這個(gè)路徑時(shí),首先經(jīng)過(guò) DispatcherServlet 的攔截。
  2. 然后通過(guò) /hello 找到對(duì)應(yīng)的 Controller。
  3. DispatcherServlet 將請(qǐng)求提交給 Controller。
  4. Controller 調(diào)用業(yè)務(wù)處理邏輯后,返回 ModelAndView。
  5. DispatcherServlet 查詢一個(gè)或多個(gè) ViewResoler 視圖解析器,找到 ModelAndView 指定的視圖。
  6. 將結(jié)果返回給客戶端。

Spring MVC 入門(mén)

@RequestMapping

@RequestMapping 映射請(qǐng)求注解

在控制器的類定義及方法定義上都可以標(biāo)注 @RequestMapping。

標(biāo)記在類上:提供初步的映射信息,相當(dāng)于 Web 應(yīng)用的根目錄。

標(biāo)記在方法上:提供細(xì)分的映射信息,如果類上未標(biāo)注該注解,那么方法處標(biāo)記的 URL 相對(duì)于 Web 應(yīng)用的根目錄。

作用:DispatcherServlet 截獲請(qǐng)求后,就根據(jù) @RequestMapping 提供的映射信息確定請(qǐng)求所對(duì)應(yīng)的處理方法。

源碼參考

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String[] value() default {};

    RequestMethod[] method() default {};

    String[] params() default {};

    String[] headers() default {};

    String[] consumes() default {};

    String[] produces() default {};
}

上例代碼分析

package com.kernel.spring.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

// 標(biāo)注該類是一個(gè)控制器組件
@Controller
public class HelloController {
    // 映射請(qǐng)求信息
    @RequestMapping("/hello")
    public String hello() {
        // 返回值會(huì)通過(guò)視圖解析器解析為實(shí)際的物理視圖,然后做轉(zhuǎn)發(fā)操作
        // 對(duì)于 InternalResourceViewResolver,會(huì)通過(guò) prefix + returnValue + suffix 這樣的方式得到實(shí)際的物理視圖
        return "/success";
    }
}

@RequestMapping 映射請(qǐng)求方式

該注解除了可以使用請(qǐng)求 URL 映射請(qǐng)求外,還可以使用請(qǐng)求方法、請(qǐng)求參數(shù)和請(qǐng)求頭映射請(qǐng)求。

結(jié)果使用可以讓請(qǐng)求更加精確化

@Controller
public class TestController {
    @RequestMapping(value = "/testMethod", method = RequestMethod.POST)
    public String testMethod(){
        System.out.println("testMethod...");
        return "/success";
    }
}

如果以 get 方式請(qǐng)求,會(huì)報(bào) 405 請(qǐng)求不支持錯(cuò)誤。

@RequestMapping 映射請(qǐng)求參數(shù)、請(qǐng)求頭

@Controller
public class TestController {
    @RequestMapping(value="/testParamsAndHeaders", 
                    params= {"username","age!=10"}, 
                    headers = { "Accept-Language=en-US,zh;q=0.9" })
    public String testParamsAndHeaders(){
        System.out.println("testParamsAndHeaders...");
        return "/success";
    }
}

測(cè)試

http://localhost:8080/testParamsAndHeaders 不可以訪問(wèn)

http://localhost:8080/testParamsAndHeaders?username 可以訪問(wèn)

http://localhost:8080/testParamsAndHeaders?username&age 可以訪問(wèn)

http://localhost:8080/testParamsAndHeaders?username=kernel&age=10 不可以訪問(wèn)

結(jié)論:必須至少攜帶一個(gè)參數(shù),參數(shù)可以傳空,但是 age 不能等于10。

@RequestMapping 映射請(qǐng)求占位符

@PathVariable 可以將請(qǐng)求中的參數(shù),傳遞給處理請(qǐng)求方法的入?yún)⒅小?/p>

@Controller
public class TestController {
    @RequestMapping("/testPathVariable/{id}")
    public String testPathVariable(@PathVariable("id") Integer id){
        System.out.println("testPathVariable...id" + id);
        return "/success";
    }
}

必須攜帶一個(gè)整數(shù)類型的參數(shù)才可以訪問(wèn)。

HiddenHttpMethodFilter

什么是 REST 風(fēng)格

REST:

即 Representational State Transfer。(資源)表現(xiàn)層狀態(tài)轉(zhuǎn)化。是目前最流行的一種互聯(lián)網(wǎng)軟件架構(gòu)。它結(jié)構(gòu)清晰、符合標(biāo)準(zhǔn)、易于理解、擴(kuò)展方便,所以正得到越來(lái)越多網(wǎng)站的采用。

URL 風(fēng)格:

HTTP 方法 說(shuō)明
POST 新增資源
DELETE 刪除資源
PUT 修改資源
GET 獲取資源

HiddenHttpMethodFilter:

總所周知,瀏覽器 from 表單只支持兩種請(qǐng)求,即 GET 和 POST,而 PUT 和 DELETE 不支持,Spring 3.0 提供了一個(gè)過(guò)濾器,將這些請(qǐng)求轉(zhuǎn)換為 PUT 和 DELETE 請(qǐng)求。

配置 HiddenHttpMethodFilter

web.xml

<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>

delete.jsp

<form action="/testRESTDelete/1" method="POST">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="testRESTDelete">
</form>

源碼參考

public class HiddenHttpMethodFilter extends OncePerRequestFilter {
    public static final String DEFAULT_METHOD_PARAM = "_method";
    private String methodParam = "_method";

    public HiddenHttpMethodFilter() {
    }

    public void setMethodParam(String methodParam) {
        Assert.hasText(methodParam, "'methodParam' must not be empty");
        this.methodParam = methodParam;
    }

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String paramValue = request.getParameter(this.methodParam);
        if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
            String method = paramValue.toUpperCase(Locale.ENGLISH);
            HttpServletRequest wrapper = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
            filterChain.doFilter(wrapper, response);
        } else {
            filterChain.doFilter(request, response);
        }

    }

    private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
        private final String method;

        public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
            super(request);
            this.method = method;
        }

        public String getMethod() {
            return this.method;
        }
    }
}

從源碼看出來(lái),Spring 根據(jù)請(qǐng)求中的 _method 參數(shù)進(jìn)行轉(zhuǎn)換,并且只對(duì) POST 進(jìn)行處理, GET 請(qǐng)求進(jìn)行處理,所有如果想轉(zhuǎn)換請(qǐng)求,必須要將表單提交方式設(shè)置成 POST,必須將 _method 的值設(shè)置為 DELETE 或 PUT。

請(qǐng)求數(shù)據(jù)傳入

請(qǐng)求處理方法簽名

Spring MVC 通過(guò)分析處理方法的簽名,HTTP 請(qǐng)求信息綁定到處理方法的入?yún)⒅小?/p>

Spring MVC 對(duì)控制器處理方法簽名的限制是很寬松的,幾乎可以按照喜歡的方式對(duì)方法簽名。

必要時(shí)可以對(duì)方法及方法入?yún)?biāo)注響應(yīng)的注解(@PathVariable 、@RequestParam、@RequestHeader 等)。

Spring MVC 會(huì)將 HTTP 請(qǐng)求的信息綁定到響應(yīng)的方法入?yún)⒅校⒏鶕?jù)方法的返回值做出相應(yīng)的后續(xù)處理。

@RequestParam注解

在處理方法入?yún)r(shí)使用該注解可以將請(qǐng)求參數(shù)傳遞給方法入?yún)ⅰ?/p>

value:參數(shù)名

required:是否必須,默認(rèn)為 true,若不存在,拋出異常。

defaultValue:默認(rèn)值,沒(méi)有傳遞參數(shù)的時(shí)候使用。

@Controller
public class TestController {
    @RequestMapping("/testRequestParam")
    public String testRequestParam(@RequestParam(value = "id",
                                                 required = false, 
                                                 defaultValue = "0") Integer id) {
        System.out.println("testRequestParam...id" + id);
        return "/success";
    }
}

@RequestHeader 注解

在處理方法入?yún)r(shí)使用該注解可以將請(qǐng)求頭傳遞給方法入?yún)ⅰ?/p>

value:參數(shù)名

required:是否必須,默認(rèn)為 true,若不存在,拋出異常。

defaultValue:默認(rèn)值,沒(méi)有傳遞參數(shù)的時(shí)候使用。

@Controller
public class TestController {
    @RequestMapping("/testRequestHeader")
    public String testHeader(@RequestHeader("Accept-Encoding") String encoding,                                          @RequestHeader("Connection") String connection) {
        System.out.println("testRequestHeader...Accept-Encoding" + encoding);
        System.out.println("testRequestHeader...Connection" + connection);
        return "/success";
    }
}

@CookieValue 注解

使用該注解可以綁定請(qǐng)求中的 Cookie 值。

value:參數(shù)名

required:是否必須,默認(rèn)為 true,若不存在,拋出異常。

defaultValue:默認(rèn)值,沒(méi)有傳遞參數(shù)的時(shí)候使用。

@Controller
public class TestController {
    @RequestMapping("/testCookieValue")
    public String testCookieValue(@CookieValue("JSESSIONID") String jsessionid) {
        System.out.println("testCookieValue...JSESSIONID" + jsessionid);
        return "/success";
    }
}

使用POJO作為參數(shù)

使用 POJO 對(duì)象綁定請(qǐng)求參數(shù)值。

Spring MVC 會(huì)按照請(qǐng)求參數(shù)名和 POJO 屬性進(jìn)行自動(dòng)匹配,還支持級(jí)聯(lián)屬性。

TestController.java

@Controller
public class TestController {
    @RequestMapping("/testPOJO")
    public String testPOJO(User user) {
        System.out.println("testPOJO...user" + user);
        return "/success";
    }
}

.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <form action="/testPOJO" method="post">
      ID:<input type="text" name="id"><br>
      賬戶:<input type="text" name="username"><br>
      密碼:<input type="text" name="password"><br>
      郵箱:<input type="text" name="email"><br>
      年齡:<input type="text" name="age"><br>
      省份:<input type="text" name="address.province"><br>
      城市:<input type="text" name="address.city"><br>
      <input type="submit" value="提交">
  </form>
  </body>
</html>

使用原生 ServletAPI 作為參數(shù)

Spring MVC 的 Handler 方法可以接受哪些 ServletAPI 類型的參數(shù)?

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • java.security.Principal
  • Locale
  • InputStream
  • OutputStream
  • Reader
  • Writer

響應(yīng)數(shù)據(jù)傳出

Spring MVC 提供了以下幾種途徑輸出模型數(shù)據(jù):

ModelAndView:處理方法返回值為 ModelAndView 時(shí),即可以通過(guò)該對(duì)象添加模型數(shù)據(jù)。

Map、Model:入?yún)?org.springframework.ui.Model、org.springframework.ui.ModelMap 或 java.uti.Map 時(shí),處理方法返回時(shí),Map 中的數(shù)據(jù)會(huì)自動(dòng)添加到模型中。

@SessionAttributes:將模型中的數(shù)據(jù)暫存到 HttpSession中,以便在多個(gè)請(qǐng)求中共享這個(gè)屬性。

@ModelAttribute:方法入?yún)?biāo)注該注解后,入?yún)⒌膶?duì)象會(huì)放入到模型數(shù)據(jù)中。

ModelAndView

控制器如果返回類型是 ModelAndView,則其既可以包含視圖信息,又可以包含模型數(shù)據(jù)信息。

添加模型:

MoelAndView addObject(String attributeName, Object attributeValue)

ModelAndView addAllObject(Map<String, ?> modelMap)

設(shè)置視圖:

void setView(View view)

void setViewName(String viewName)

@Controller
public class TestController {
    @RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView(){
        System.out.println("testModelAndView...");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("/success");
        modelAndView.addObject("time",new Date());
        return modelAndView;
    }
}

事實(shí)上,返回的類型可以是很多種,可以是 ModelAndView 類型,也可以是 String 類型,還可以是 View 類型,還有很多。但是他們最終會(huì)被解析為 ModelAndView 類型的對(duì)象。

Map

Spring MVC 在內(nèi)部使用了一個(gè) org.springframework.ui.Model 接口存儲(chǔ)模型數(shù)據(jù)。

具體使用步驟:

Spring MVC 在調(diào)用方法之前會(huì)創(chuàng)建一個(gè)隱含的模型對(duì)象作為模型對(duì)象的存儲(chǔ)容器。

如果方法的入?yún)?Map 或者 Model 類型,Spring MVC 會(huì)將隱含模型的引用傳遞給入?yún)ⅰ?/p>

在方法體內(nèi),可以通過(guò)這個(gè)入?yún)?duì)象訪問(wèn)到模型中的所有數(shù)據(jù),也可以向模型中添加新的屬性數(shù)據(jù)。

@Controller
public class TestController {
    @RequestMapping("/testMap")
    public String testMap(Map<String, User> map) {
        map.put("user", new User(1, "kernel", "123456", "kernel@qq.com", 18, 
                                 new Address("山東", "德州")));
        return "/success";
    }
}

@SessionAttribute

若希望在多個(gè)請(qǐng)求之間共用某個(gè)模型對(duì)象,可以在控制器上標(biāo)志這個(gè)注解,Spring MVC 將對(duì)應(yīng)的模型屬性暫存到HttpSession 中。

@SessionAttributes(types=User.class) 會(huì)將隱含模型的所有類型為 User 的屬性全部添加到會(huì)話中。
@SessionAttributes(value={“user1”, “user2”}) 將 user1、user2 添加到會(huì)話中。

源碼參考

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes {
    String[] value() default {}; // 推薦使用

    Class<?>[] types() default {}; // 作用范圍太廣
}
@Controller
@SessionAttribue("user)
public class TestController {
    @RequestMapping("/testMap")
    public String testMap(Map<String, User> map) {
        // 如果該類有 @SessionAttribute 注解,會(huì)同時(shí)將 user 存放到 request 作用域和 session 作用域
        // 否則將 user 放到 request 作用域。
        map.put("user", new User(1, "kernel", "123456", "kernel@qq.com", 18, 
                                 new Address("山東", "德州")));
        return "/success";
    }
}

@ModelAttribute

使用場(chǎng)景?

假如說(shuō)我修改一個(gè)訂單的時(shí)候,訂單的創(chuàng)建時(shí)間是不允許被修改的,所以我更新時(shí),因?yàn)闀r(shí)間字段沒(méi)有值,所有更新會(huì)將該字段更新為 null。

解決方法有:

隱藏域,字段多太麻煩,還有用戶可以在源代碼中看到隱藏域中的信息,不安全。

先查詢數(shù)據(jù)庫(kù),然后一一賦值,比較麻煩。

下面使用 @ModelAttribute

在方法定義上使用該注解,會(huì)在調(diào)用目標(biāo)方法之前逐個(gè)調(diào)用方法上標(biāo)注該注解的的方法。

在方法的入?yún)⑸蠘?biāo)注該注解,可以從隱含對(duì)象中獲取隱含模型數(shù)據(jù)中獲取對(duì)象,再將請(qǐng)求參數(shù)綁定到對(duì)象中,在傳入入?yún)ⅰ?/p>

@Controller
@SessionAttribue("user)
public class TestController {
    @ModelAttribute
    public void getUser(@RequestParam(value = "id", required = false) Integer id,
    Map<String, User> map) {
        if (id != null) {
            User user = new User(1, "kernel", "123456", "kernel@qq.com", 18, 
            new Address("山東", "德州"));
            System.out.println(user);
            map.put("user", user);
        }
    }

    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(User user) {
        System.out.println("testModelAttribute...user" + user);
        return "/success";
    }
}

原理分析:

執(zhí)行 @ModelAttribute 注解所修飾的方法,將從數(shù)據(jù)庫(kù)中獲取的對(duì)象存放到 Map 集合中,key 為 user。

Spring MVC 從 map 中查找 user 對(duì)象,將表單數(shù)據(jù)封裝到與參數(shù)名稱對(duì)應(yīng)的 user 對(duì)象屬性上。

SpringMVC將user對(duì)象作為參數(shù),傳遞給目標(biāo)方法。

SpringMVC 確定目標(biāo)方法 POJO 類型入?yún)⒌倪^(guò)程:

確定一個(gè) key:若目標(biāo)方法的 POJO 類型的參數(shù)木有使用 @ModelAttribute 作為修飾,則 key 為 POJO 類名第一個(gè)字母的小寫(xiě),若使用了@ModelAttribute 來(lái)修飾,,則 key 為 @ModelAttribute 注解的 value 屬性值。

在 implicitModel 中查找 key 對(duì)應(yīng)的對(duì)象, 若存在,則作為入?yún)魅搿?/p>

若 implicitModel 中不存在 key 對(duì)應(yīng)的對(duì)象,則檢查當(dāng)前的 Handler 是否使用 @SessionAttributes 注解修飾。

若使用了該注解,且 @SessionAttributes 注解的 value 屬性值中包含了 key,則會(huì)從 HttpSession 中來(lái)獲取 key 所對(duì)應(yīng)的 value 值,若存在則直接傳入到目標(biāo)方法的入?yún)⒅校舨淮嬖趧t將拋出異常。

若 Handler 沒(méi)有標(biāo)識(shí) @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key,則會(huì)通過(guò)反射來(lái)創(chuàng)建 POJO 類型的參數(shù),傳入為目標(biāo)方法的參數(shù)。

SpringMVC 會(huì)把 key 和 POJO 類型的對(duì)象保存到 implicitModel 中,進(jìn)而會(huì)保存到 request 中。

視圖解析

視圖和視圖解析器

請(qǐng)求處理方法執(zhí)行后,會(huì)最終返回一個(gè) ModelAndView 對(duì)象,對(duì)于那些返回了 String、Model、Map 等的處理方法,內(nèi)部也會(huì)被裝配成一個(gè)ModelAndView,它包含了邏輯名和模型對(duì)象的視圖。

通過(guò)視圖解析器得到最終的視圖對(duì)象,最終的視圖可以是 JSP、Excel、Pdf 等各種形式。

對(duì)于采取什么視圖對(duì)模型渲染,處理器并不關(guān)心,從而實(shí)現(xiàn) MVC 的充分解耦。

常見(jiàn)的視圖類:

Spring MVC 入門(mén)
每個(gè)視圖解析器都實(shí)現(xiàn)了 Ordered 接口并開(kāi)放出一個(gè) order 屬性,可以通過(guò) order 屬性指定解析器的優(yōu)先順序,order 越小優(yōu)先級(jí)越高。

SpringMVC 會(huì)按視圖解析器順序的優(yōu)先順序?qū)壿嬕晥D名進(jìn)行解析,直到解析成功并返回視圖對(duì)象,否則將拋出 ServletException 異常。

JstlView

若項(xiàng)目中使用了 JSTL,則 InternalResourceView 會(huì)轉(zhuǎn)換成 JstlView。

若想使用 JSTL 的 fmt 標(biāo)簽,則必須配置國(guó)際化資源文件。

i8n國(guó)際化

# i18n.properties
i18n.username=Username
i18n.password=Password
# i18n_zh_CN.properties
i18n.username=用戶名
i18n.password=密碼
# i18n_en_US.properties
i18n.username=Username
i18n.password=Password

application.xml

<?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"
       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">
    <context:component-scan base-package="com.kernel.spring.web"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="i18n"/>
    </bean>
    <!-- 直接配置響應(yīng)的頁(yè)面:無(wú)需經(jīng)過(guò)控制器來(lái)執(zhí)行結(jié)果 -->
    <mvc:view-controller path="/success" view-name="success"/>
</beans>

自定義視圖

若希望使用 Excel 展示數(shù)據(jù)列表,僅需要擴(kuò)展 SpringMVC 提供的 AbstractExcelView 或 AbstractJExcelView 即可。

實(shí)現(xiàn) buildExcelDocument() 方法,在方法中使用模型數(shù)據(jù)對(duì)象構(gòu)建 Excel 文檔就可以了。

AbstractExcelView 基于 POI API,而 AbstractJExcelView 是基于 JExcelAPI 的。

視圖對(duì)象需要配置 IOC 容器中的一個(gè) Bean ,使用 BeanNameViewResolver 作為視圖解析器即可。

若希望直接在瀏覽器中直接下載 Excel 文檔,則可以設(shè)置響應(yīng)頭 Content-Disposition 的值為attachment;filename=xxx.xls。

application.xml

<?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"
       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">
    <context:component-scan base-package="com.kernel.spring.web"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="i18n"/>
    </bean>
    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
        <property name="order" value="100"/>
    </bean>
</beans>

HelloView.java

@Component
public class HelloView implements View {
    @Override
    public String getContentType() {
        return "text/html";
    }

    @Override
    public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        httpServletResponse.getWriter().print(new Date());
    }
}

測(cè)試

@Controller
@SessionAttribue("user)
public class TestController {
    @RequestMapping("/testView")
    public String testView(){
        System.out.println("testView...");
        // 與視圖對(duì)象的 id 一致
        return "helloView"; 
    }
}

重定向

一般情況下,控制器方法返回字符串類型的值會(huì)被當(dāng)成邏輯視圖名處理

如果返回的字符串中帶 forward: 或 redirect:前綴時(shí),SpringMVC 會(huì)對(duì)他們進(jìn)行特殊處理。

redirect:success.jsp:會(huì)完成一個(gè)到 success.jsp 的重定向的操作。

forward:success.jsp:會(huì)完成一個(gè)到 success.jsp 的轉(zhuǎn)發(fā)操作。

數(shù)據(jù)綁定

數(shù)據(jù)綁定流程

Spring MVC 將 ServletRequest 對(duì)象及目標(biāo)方法的入?yún)鬟f給 WebDataBinderFactory,以創(chuàng)建 DataBinder 實(shí)例對(duì)象。

DataBinder 調(diào)用裝配在上下文中的 ConversionService 組件進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換、數(shù)據(jù)格式化工作。

調(diào)用 Vaidator 組件對(duì)已綁定了請(qǐng)求消息的入?yún)?duì)象進(jìn)行數(shù)據(jù)合法性校驗(yàn),最終生成數(shù)據(jù)綁定 BindingData 對(duì)象。

Spring MVC 抽取 BindingResult 中的入?yún)?duì)象和校驗(yàn)錯(cuò)誤對(duì)象,將它們賦給處理方法的響應(yīng)入?yún)ⅰ?/p>

Spring MVC 通過(guò)反射機(jī)制對(duì)目標(biāo)處理方法進(jìn)行解析,將請(qǐng)求消息綁定到處理方法的入?yún)⒅?。?shù)據(jù)綁定的核心部件是 DataBinder,運(yùn)行機(jī)制如下:

Spring MVC 入門(mén)

自定義類型轉(zhuǎn)換器

ConversionService 是 Spring 類型轉(zhuǎn)換器的核心。

可以使用 ConversionServiceFactoryBean 在 Spring IOC 容器中定義一個(gè) ConversionService,Spring 將自動(dòng)識(shí)別出 IOC 容器中的 ConversionService,并在 Bean 屬性配置及 Sprng MVC 處理方法入?yún)⒔壎ǖ葓?chǎng)合使用它進(jìn)行數(shù)據(jù)轉(zhuǎn)換。

Spring 支持的轉(zhuǎn)換器類型:

Converter<S, T>:將 S 類型對(duì)象轉(zhuǎn)為 T 類型對(duì)象

ConverterFactory:將相同系列多個(gè) “同質(zhì)” Converter 封裝在一起。如果希望將一種類型的對(duì)象轉(zhuǎn)換為另一種類型及其子類的對(duì)象。

GenericConverter:會(huì)根據(jù)源類對(duì)象及目標(biāo)類對(duì)象所在的宿主類中的上下文信息進(jìn)行類型轉(zhuǎn)換

StringToUserConverter.java

@Component
public class StringToUserConverter implements Converter<String, User> {

    @Override
    public User convert(String s) {
        if (s != null){
            String[] vals = s.split("-");
            String username = vals[0];
            String password = vals[1];
            String email = vals[2];
            Integer age = Integer.valueOf(vals[3]);
            String province = vals[4];
            String city = vals[5];
            return new User(null, username, password,email,age , new Address(province, city));
        }
        return null;
    }
}

application.xml

<?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/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="com.kernel.spring.web"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="i18n"/>
    </bean>
    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
        <property name="order" value="100"/>
    </bean>
    <bean id="serviceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="stringToUserConverter"/>
            </set>
        </property>
    </bean>
    <mvc:annotation-driven conversion-service="serviceFactoryBean"/>
</beans>

測(cè)試

@Controller
@SessionAttribue("user)
public class TestController {
    @RequestMapping("/testConverter")
    public String testConverter(User user) {
        System.out.println(user);
        return "/success";
    }
}

<mvc:annotation-driven>

直接配置響應(yīng)的頁(yè)面,無(wú)需經(jīng)過(guò)控制器的處理,但是直接影響其他請(qǐng)求路徑失效。

找不到靜態(tài)資源需要配置,\<mvc:default-servlet-handler> 將在 SpringMVC 上下文中定義一個(gè)DefaultServletHttpRequestHandler,它會(huì)對(duì)進(jìn)入 DispatcherServlet 的請(qǐng)求進(jìn)行篩查,如果發(fā)現(xiàn)是沒(méi)有經(jīng)過(guò)映射的請(qǐng)求,就將該請(qǐng)求交由 WEB 應(yīng)用服務(wù)器默認(rèn)的 Servlet 處理,如果不是靜態(tài)資源的請(qǐng)求,才由 DispatcherServlet 繼續(xù)處理。

配置類型轉(zhuǎn)換器時(shí),需要指定轉(zhuǎn)換器引用。

完成 JSR303 數(shù)據(jù)驗(yàn)證,也需要配置該標(biāo)簽。

作用:

會(huì)自動(dòng)注冊(cè):RequestMappingHandlerMapping、RequestMappingHandlerAdapter 與ExceptionHandlerExceptionResolver 三個(gè) bean。

還將提供以下支持:

支持使用 ConversionService 實(shí)例對(duì)表單參數(shù)進(jìn)行類型轉(zhuǎn)換。

支持使用 @NumberFormat、@DateTimeFormat 注解完成數(shù)據(jù)類型的格式化。

支持使用 @Valid 注解對(duì) JavaBean 實(shí)例進(jìn)行 JSR 303 驗(yàn)證。

支持使用 @RequestBody 和 @ResponseBody 注解。

@InitBinder注解

由 @InitBinder 標(biāo)識(shí)的方法,可以對(duì) WebDataBinder 對(duì)象進(jìn)行初始化,WebDataBinder 是 DataBinder 的子類,用于完成從表單字段到 JavaBean 的綁定。

由 @InitBinder 標(biāo)識(shí)的方法不能有返回值,必須是 void 類型。

由 @InitBinder 表示的方法入?yún)⑼ǔJ?WebDataBinder。

數(shù)據(jù)的格式化

Spring 在格式化模塊中定義了一個(gè)實(shí)現(xiàn)了 ConversionService 接口的 FormattingConversionService 實(shí)現(xiàn)類,該實(shí)現(xiàn)類擴(kuò)展了 GenericConversionService 實(shí)現(xiàn)類,該類既有類型轉(zhuǎn)換的功能,又有格式化的功能。

FormattingConversionService 擁有一個(gè) FormattingConversionServiceFactory 工廠類,后者用于在 Spring 上下文中給構(gòu)造前者,F(xiàn)ormattingConversionServiceFactory 內(nèi)部注冊(cè)了:

NumberFormatAnnotationFormatterFactroy:支持對(duì)數(shù)字使用 @NumberFormat 注解。

odaDateTimeFormatAnnotationFormatterFactroy:支持對(duì)日期類型使用 @DataTimeFormat 注解。

裝配了 FormattingConversionServiceFactroyBean 后,就可以在 Spring MVC 入?yún)⒔壎澳P蛿?shù)據(jù)輸出時(shí)使用注解驅(qū)動(dòng)了。

@DataTimeFormat 可以對(duì) Date、Calendar、Long 時(shí)間類型進(jìn)行標(biāo)注。

pattern:類型為字符串,指定解析/格式化字符串的模式。

iso:類型為 DateTimeFormat.ISO,指定解析/格式化字符串的 ISO 模式,包括四種:ISO.NONE(不使用,默認(rèn))、ISO.DATE(yyyy-MM-dd)、ISO.TIME(hh:mm:ss:SSSZ)、ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)

style:字符串類型,通過(guò)樣式指定日期時(shí)間的格式,由兩位字符組成,第一位表示日期的格式,第二位表示時(shí)間的格式:S(段日期/時(shí)間格式)、M(中日期/時(shí)間格式)、L(長(zhǎng)日期/時(shí)間格式)、F(完整日期/時(shí)間格式)。

@NumberFormat,可對(duì)類似數(shù)字類型的屬性進(jìn)行標(biāo)注。

pattern:類型為 String,自定義樣式,如 "#,###"。

style:用于指定樣式類型,包括Style.NUMBER(正常數(shù)字類型)、Style.CURRENCY(貨幣類型)、
Style.PERCENT(百分?jǐn)?shù)類型)。

JSR303 數(shù)據(jù)校驗(yàn)

使用 JSR303 驗(yàn)證標(biāo)準(zhǔn)

加入 hibernate validator 驗(yàn)證框架

在 application.xml 文件中配置 \<mvc:annotation-driven/>

在 Bean 屬性上增加對(duì)應(yīng)的驗(yàn)證注解

在目標(biāo)方法的 Bean 類型的前面增加 @Valid 注解

錯(cuò)誤回顯及格式化

Spring MVC 除了會(huì)將表單/命令對(duì)象的校驗(yàn)結(jié)果保存到對(duì)應(yīng)的 BindingResult 或 Errors 對(duì)象中外,還會(huì)將所有校驗(yàn)結(jié)果保存到 “隱含模型”。
即使處理方法的簽名中沒(méi)有對(duì)應(yīng)于表單/命令對(duì)象的結(jié)果入?yún)?,校?yàn)結(jié)果也會(huì)保存在 “隱含對(duì)象” 中。
隱含模型中的所有數(shù)據(jù)最終將通過(guò) HttpServletRequest 的屬性列表暴露給 JSP 視圖對(duì)象,因此在 JSP 中可以獲取錯(cuò)誤信息。
在 JSP 頁(yè)面上可通過(guò) <form:errors path=“userName”> 顯示錯(cuò)誤消息。

<form:errors path="*"/> 顯示所有的錯(cuò)誤信息

<form:errors path="lastName"/> 顯示某個(gè)表單域的錯(cuò)誤信息

每個(gè)屬性在數(shù)據(jù)綁定和數(shù)據(jù)校驗(yàn)發(fā)生錯(cuò)誤時(shí),都會(huì)生成一個(gè)對(duì)應(yīng)的 FieldError 對(duì)象。

當(dāng)一個(gè)屬性校驗(yàn)失敗后,校驗(yàn)框架會(huì)為該屬性生成 4 個(gè)消息代碼,這些代碼以校驗(yàn)注解類名為前綴,結(jié)合 modleAttribute、屬性名及屬性類型名生成多個(gè)對(duì)應(yīng)的消息代碼:

例如 User 類中的 password 屬性標(biāo)注了一個(gè) @Pattern 注解,當(dāng)該屬性值不滿足 @Pattern 所定義的規(guī)則時(shí), 就會(huì)產(chǎn)生以下 4 個(gè)錯(cuò)誤代碼:
Pattern.user.password
Pattern.password
Pattern.java.lang.String
Pattern
當(dāng)使用 Spring MVC 標(biāo)簽顯示錯(cuò)誤消息時(shí), Spring MVC 會(huì)查看 WEB 上下文是否裝配了對(duì)應(yīng)的國(guó)際化消息,如果沒(méi)有,則顯示默認(rèn)的錯(cuò)誤消息,否則使用國(guó)際化消息。
若數(shù)據(jù)類型轉(zhuǎn)換或數(shù)據(jù)格式轉(zhuǎn)換時(shí)發(fā)生錯(cuò)誤,或該有的參數(shù)不存在,或調(diào)用處理方法時(shí)發(fā)生錯(cuò)誤,都會(huì)在隱含模型中創(chuàng)建錯(cuò)誤消息。

其錯(cuò)誤代碼前綴說(shuō)明如下:
required:必要的參數(shù)不存在。如 @RequiredParam(“param1”) 標(biāo)注了一個(gè)入?yún)?,但是該參?shù)不存在
typeMismatch:在數(shù)據(jù)綁定時(shí),發(fā)生數(shù)據(jù)類型不匹配的問(wèn)題
methodInvocation:Spring MVC 在調(diào)用處理方法時(shí)發(fā)生了錯(cuò)誤

國(guó)際化

國(guó)際化頁(yè)面中獲取國(guó)際化資源信息

使用 JSTL

application.xml

<?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">
    <context:component-scan base-package="com.kernel"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="i18n"/>
    </bean>
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
    </mvc:interceptors>
    <mvc:view-controller path="/i18n1" view-name="i18n1"/>
    <mvc:view-controller path="/i18n2" view-name="i18n2"/>
    <mvc:annotation-driven/>
</beans>

i18n1.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <fmt:message key="i18n.username"/>
    <a href="i18n2">i18n2</a>
</body>
</html>

i18n2.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <fmt:message key="i18n.password"/>
    <a href="i18n1">i18n1</a>
</body>
</html>

在 Bean 中獲取國(guó)際化資源文件 Locale 對(duì)應(yīng)的信息

@Controller
public class TestController {
    @Autowired
    private ResourceBundleMessageSource messageSource;

    @RequestMapping("/i18n")
    public String testi18n(Locale locale){
        System.out.println(locale);
        String userName = messageSource.getMessage("i18n.username", null, locale);
        System.out.println("i18n.username="+userName);
        return "i18n";
    }
}

通過(guò)超鏈接切換 Locale,而不再依賴于瀏覽器的語(yǔ)言設(shè)置情況。

<?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">
    <context:component-scan base-package="com.kernel"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="i18n"/>
    </bean>
    <!-- 配置SessionLocaleResolver -->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"/>
    <!-- 配置LocaleChangeInterceptor攔截器 -->
    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/>
    </mvc:interceptors>
    <mvc:view-controller path="/i18n1" view-name="i18n1"/>
    <mvc:view-controller path="/i18n2" view-name="i18n2"/>
    <mvc:annotation-driven/>
</beans>

測(cè)試:

http://localhost:8080/i18n?locale=zh_CN

http://localhost:8080/i18n?locale=en_US

文件上傳

Spring MVC 為文件上傳提供了直接的支持,這種支持是通過(guò)即插即用的 MultipartResolver 實(shí)現(xiàn)的。
Spring 用 Jakarta Commons FileUpload 技術(shù)實(shí)現(xiàn)了一個(gè) MultipartResolver 實(shí)現(xiàn)類:

CommonsMultipartResovler :
Spring MVC 上下文中默認(rèn)沒(méi)有裝配 MultipartResovler,因此默認(rèn)情況下不能處理文件的上傳工作,如果想使用 Spring 的文件上傳功能,需現(xiàn)在上下文中配置 MultipartResolver。

application.xml

<?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">
    <context:component-scan base-package="com.kernel"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 配置文件上傳解析器 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="maxUploadSize" value="5242880"/>
    </bean>
    <mvc:annotation-driven/>
</beans>

upload.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <form action="testUpload" method="post" enctype="multipart/form-data">
      文件: <input type="file" name="file"/><br><br>
      描述: <input type="text" name="desc"/><br><br>
      <input type="submit" value="提交"/>
  </form>

  </body>
</html>

測(cè)試

@Controller
public class TestController {
    @Autowired
    private ResourceBundleMessageSource messageSource;

    private static void writeToLocal(String destination, InputStream input)
            throws IOException {
        int index;
        byte[] bytes = new byte[1024];
        FileOutputStream downloadFile = new FileOutputStream(destination);
       while ((index = input.read(bytes)) != -1) {
            downloadFile.write(bytes, 0, index);
            downloadFile.flush();
        }
        downloadFile.close();
        input.close();
    }

    @RequestMapping(value = "/testUpload", method = RequestMethod.POST)
    public String testUpload(@RequestParam(value = "desc",required = false) String desc,
                             @RequestParam("file")MultipartFile multipartFile) throws IOException {
        System.out.println("desc " + desc);
        System.out.println("OriginalFilename" + multipartFile.getOriginalFilename());
        InputStream inputStream = multipartFile.getInputStream();
        System.out.println("available " + inputStream.available());
        System.out.println("inputStream " + inputStream);
       writeToLocal(multipartFile.getOriginalFilename(), inputStream);
        return "success";
    }
}

攔截器

自定義攔截器

Spring MVC 可以使用攔截器對(duì)請(qǐng)求進(jìn)行攔截處理,用戶可以自定義攔截器來(lái)實(shí)現(xiàn)特定功能,自定義攔截器必須實(shí)現(xiàn) HandlerInterceptor 接口。

preHandle():這個(gè)方法在業(yè)務(wù)處理器處理請(qǐng)求之前被調(diào)用,在該方法中對(duì)用戶請(qǐng)求 request 進(jìn)行處理。如果程序員決定該攔截器對(duì)請(qǐng)求進(jìn)行攔截處理后還要調(diào)用其他的攔截器,或者是業(yè)務(wù)處理器去進(jìn)行處理,則返回true;如果程序員決定不需要再調(diào)用其他的組件去處理請(qǐng)求,則返回false。

postHandle():這個(gè)方法在業(yè)務(wù)處理器處理完請(qǐng)求后,但是DispatcherServlet 向客戶端返回響應(yīng)前被調(diào)用,在該方法中對(duì)用戶請(qǐng)求request進(jìn)行處理。

afterCompletion():這個(gè)方法在 DispatcherServlet 完全處理完請(qǐng)求后被調(diào)用,可以在該方法中進(jìn)行一些資源清理的操作。

<?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">
    <context:component-scan base-package="com.kernel"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <mvc:interceptors>
        <bean class="com.kernel.interceptor.FirstHandlerInterceptor"/>
        <bean class="com.kernel.interceptor.SecondHandlerInterceptor"/>
    </mvc:interceptors>
    <mvc:annotation-driven/>
</beans>

多個(gè)攔截器的執(zhí)行順序

按照攔截器的配置的順序依次調(diào)用每個(gè)攔截器的 preHandle 方法,當(dāng)請(qǐng)求業(yè)務(wù)處理器執(zhí)行完畢后,依次對(duì)倒敘對(duì)每個(gè)攔截器放行,執(zhí)行 postHandle 方法,當(dāng)請(qǐng)求完全處理完畢后,然后依次倒敘執(zhí)行每個(gè)攔截器的 afterCompletion 方法。

異常處理

Spring MVC 通過(guò) HandlerExceptionResolver 處理程序的異常,包括 Handler 映射、數(shù)據(jù)綁定及目標(biāo)方法執(zhí)行時(shí)發(fā)生的異常。

HandlerExceptionResolver

@ExceptionHandler

可以通過(guò) @ExceptionHandler(value = {java.lang.RuntimeException.class}) 的方式捕捉一個(gè)異常,如果捕捉成功,自動(dòng)執(zhí)行標(biāo)志該注解的方法。

@ExceptionHandler(value = {java.lang.RuntimeException.class})
public ModelAndView handlerException2(Exception ex) {
    ModelAndView mv = new ModelAndView("error");
    System.out.println("出現(xiàn)異常啦!" + ex);
    mv.addObject("exception", ex);
    return mv;
}

如何將異常對(duì)象從控制器攜帶給頁(yè)面

可以通過(guò) ModelAndView 對(duì)象將異常對(duì)象添加。

異常對(duì)象捕捉的優(yōu)先級(jí)

Spring MVC 是有優(yōu)先級(jí)的,他會(huì)執(zhí)行離捕捉異常離發(fā)生異常最近的那個(gè)方法。

@ControllerAdvice

該注解是定義在類級(jí)別上的,在類上標(biāo)注了該注解后,所有控制器上發(fā)生了注解之后都會(huì)通過(guò)這個(gè)類的方法處理。

ExceptionHandlerExceptionResolver

主要處理 Handler 中用 @ExceptionHandler 注解定義的方法。

@ExceptionHandler 注解定義的方法優(yōu)先級(jí)問(wèn)題:

例如發(fā)生的是NullPointerException,但是聲明的異常有 RuntimeException 和 Exception,此候會(huì)根據(jù)異常的最近繼承關(guān)系找到繼承深度最淺的那個(gè) @ExceptionHandler 注解方法,即標(biāo)記了 RuntimeException 的方法

ExceptionHandlerMethodResolver 內(nèi)部若找不到 @ExceptionHandler 注解的話,會(huì)找 @ControllerAdvice 中的@ExceptionHandler 注解方法。

ResponseStatusExceptionResolver

在類上標(biāo)注 @ResponseStatus 注解后,可以自定義狀態(tài)碼及提示信息。

@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用戶名和密碼不匹配")

DefaultHandlerExceptionResolver

對(duì)一些特殊的異常進(jìn)行處理,比如:
NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等。

SimpleMappingExceptionResolver

如果希望對(duì)所有異常進(jìn)行統(tǒng)一處理,可以使用 SimpleMappingExceptionResolver,它將異常類名映射為視圖名,即發(fā)生異常時(shí)使用對(duì)應(yīng)的視圖報(bào)告異常。

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.ArithmeticException">error</prop>
        </props>
    </property>
</bean>

Spring Web 運(yùn)行流程

  1. 用戶向服務(wù)器發(fā)送請(qǐng)求,請(qǐng)求被 Spring MVC DispatcherServlet 捕獲。
  2. DispatcherServlet 對(duì)請(qǐng)求 URL 進(jìn)行解析,得到請(qǐng)求資源標(biāo)識(shí)符(URI),判斷是否存在。
  3. 如果不存在,判斷是否配置了 \<mvc:default-servlet-handler>,如果配置了,執(zhí)行目標(biāo)資源(一般為靜態(tài)資源);如果沒(méi)有配置,客戶端顯示 404 錯(cuò)誤。
  4. 如果存在,根據(jù)該 URI,調(diào)用 HandlerMapping 獲得該 Handler 配置的所有相關(guān)的對(duì)象(包括Handler對(duì)象以及Handler對(duì)象對(duì)應(yīng)的攔截器),最后以 HandlerExecutionChain 對(duì)象的形式返回。
  5. DispatcherServlet 根據(jù)獲得的 Handler,選擇一個(gè)合適的 HandlerAdapter。
  6. 獲得 HandlerAdapter 后,開(kāi)始正序執(zhí)行攔截器的 preHandler 方法。
  7. 提取 Request 中的模型數(shù)據(jù),填充 Handler 入?yún)?,開(kāi)始執(zhí)行 Handler 方法,處理請(qǐng)求(數(shù)據(jù)轉(zhuǎn)換、數(shù)據(jù)格式化、數(shù)據(jù)驗(yàn)證)。
  8. Handler執(zhí)行完成后,向DispatcherServlet 返回一個(gè)ModelAndView對(duì)象。
  9. 逆向執(zhí)行攔截器的 postHandler 方法。
  10. 根據(jù)返回的 ModelAndView 選擇一個(gè)合適的視圖解析器來(lái)渲染視圖。
  11. 在返回給客戶端時(shí)需要逆向執(zhí)行攔截器的 AfterCompletion 方法。
  12. 將渲染結(jié)果返回給客戶端。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

分享名稱:SpringMVC入門(mén)-創(chuàng)新互聯(lián)
URL標(biāo)題:http://muchs.cn/article10/dhecgo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開(kāi)發(fā)、網(wǎng)頁(yè)設(shè)計(jì)公司、搜索引擎優(yōu)化、網(wǎng)站制作自適應(yīng)網(wǎng)站、網(wǎng)站導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站建設(shè)