3. springmvc的進階用法

1 @ResponseBody

  1. 在前面使用SpringMVC時,Controller中的方法返回值會通過視圖處理器ViewResolver處理爲頁面的URL,然後跳轉到對應頁面中
  2. 有時候我們希望Controller不進行頁面跳轉而是直接返回數據,這時候我們可以在方法上,添加註解:@ResponseBody,此時返回值會通過HTTP響應體直接發送給瀏覽器
package com.mashibing.controller;

import com.mashibing.bean.User;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class OtherController {
    @ResponseBody
    @RequestMapping("/testResponseBody")
    public String testResponseBody(){
        return "<h1>success</h1>";
    }
}
  1. 默認情況下,使用@ResponseBody返回的數據只能是String類型,其它類型返回時會出現異常,提示沒有對應的類型轉換器
  2. 阿里開源的fastjson可以實現Java對象和JSON的相互轉換,引入對應的pom依賴後,SpringMVC會自動添加fastjson的轉換器
  3. 此時返回的非字符串對象也可以轉換爲json格式字符串,返回的集合可以轉爲json數組
  4. pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mashibing</groupId>
    <artifactId>springmv_ajax</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.10.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.10.3</version>
        </dependency>
    </dependencies>
</project>
  1. springmvc.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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.mashibing"></context:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    <mvc:default-servlet-handler></mvc:default-servlet-handler>
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>
  1. JsonController.java
package com.mashibing.controller;

import com.mashibing.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Controller
public class JsonController {

    @ResponseBody
    @RequestMapping("/json")
    public List<User> json(){
        List<User> list = new ArrayList<User>();
        list.add(new User(1,"zhangsan",12,"男",new Date(),"[email protected]"));
        list.add(new User(2,"zhangsan2",12,"男",new Date(),"[email protected]"));
        list.add(new User(3,"zhangsan3",12,"男",new Date(),"[email protected]"));
        return list;
    }
}
  1. User.java
package com.mashibing.bean;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.Date;

public class User {

    private Integer id;
    private String name;
    private Integer age;
    private String gender;
    @JsonFormat( pattern = "yyyy-MM-dd")
    private Date birth;
    @JsonIgnore
    private String email;

    public User() {
    }

    public User(Integer id, String name, Integer age, String gender, Date birth, String email) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.birth = birth;
        this.email = email;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", birth=" + birth +
                ", email='" + email + '\'' +
                '}';
    }
}

2 發送ajax請求獲取json數據

  1. ajax.jsp
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script type="text/javascript" src="script/jquery-1.9.1.min.js"></script>
</head>
<%
    pageContext.setAttribute("ctp",request.getContextPath());
%>
<body>
<%=new Date()%>
<a href="${ctp}/json">獲取用戶信息</a>
<div>

</div>
<script type="text/javascript">
    $("a:first").click(function () {
        $.ajax({
            url:"${ctp}/json",
            type:"GET",
            success:function (data) {
                console.log(data)
                $.each(data,function() {
                    var user = this.id+"--"+this.name+"--"+this.age+"--"+this.gender+"--"+this.birth+"--"+this.email;
                    $("div").append(user+'<br/>');
                })
            }
        });
        return false;
    });
</script>
</body>
</html>

3 @RequestBody

  1. @RequestBody註釋可以直接獲取請求體中的數據,默認只能以String對象接收
package com.mashibing.controller;

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

@Controller
public class OtherController {

    @RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body){
        System.out.println("請求體:"+body);
        return "success";
    }
}
  1. @RequestBody也可以根據Content-Type的類型,找對應的類型轉換器,將請求體中數據轉換爲指定對象
  2. 當Content-Type爲application/json,就會將請求體當做json的數據格式,轉換爲指定對象,由於涉及到json與對象互轉,因此也需要依賴阿里開源的fastjson
  3. testOther.jsp
<%--
  Created by IntelliJ IDEA.
  User: root
  Date: 2020/3/13
  Time: 15:04
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<script type="text/javascript" src="script/jquery-1.9.1.min.js"></script>
<html>
<%
    pageContext.setAttribute("ctp",request.getContextPath());
%>
<head>
    <title>Title</title>
</head>
<body>
<form action="${ctp}/testRequestBody" method="post" enctype="multipart/form-data">
    <input name="username" value="zhangsan"><br>
    <input name="password" value="123456"><br>
    <input type="file" name="file" ><br>
    <input type="submit"><br>
</form>
<hr/>
<a href="${ctp}/testRequestJson">發送json數據</a>
<script type="text/javascript">
    $("a:first").click(function () {
        var user = {id:"1",name:"zhangsan",age:"12",gender:"男",birth:"2020-3-13",email:"[email protected]"};
        var userJson = JSON.stringify(user);
       $.ajax({
           url:"${ctp}/testRequestJson",
           type:"POST",
           data:userJson,
           contentType:"application/json",
           success:function (data) {
               alert(data);
           }
       });
       return false;
    });
</script>
</body>
</html>
  1. OtherController.java
package com.mashibing.controller;

import com.mashibing.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class OtherController {

    @RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body){
        System.out.println("請求體:"+body);
        return "success";
    }

    @RequestMapping("/testRequestJson")
    public String testRequestBody(@RequestBody User user){
        System.out.println("對象:"+user);
        return "success";
    }
}

4 HttpEntity對象接收請求參數

  1. 可以使用HttpEntity對象來接收請求中的參數
package com.mashibing.controller;

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

@Controller
public class OtherController {

    @RequestMapping("/testHttpEntity")
    public String testRequestBody(HttpEntity<String> httpEntity) {
        //1. 打印如下信息:[host:"localhost:8080", connection:"keep-alive", upgrade-insecure-requests:"1", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", sec-fetch-mode:"navigate", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", sec-fetch-site:"none", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6", cookie:"Idea-c6a6dd2=69b8d076-5b30-4324-8de0-0512ffa5b2b9; JSESSIONID=26BCB444F17750A980E2498DD81A3511"]
        System.out.println(httpEntity);
        return "success";
    }
}

5 使用RespsonseEntity定製響應內容

package com.mashibing.controller;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class OtherController {
    @RequestMapping("/testResponseEntity")
    public ResponseEntity<String> testResponseEntity(){
        String body = "<h1>hello</h1>";
        MultiValueMap<String,String> header = new HttpHeaders();
        header.add("Set-Cookie","name=zhangsan");
        //1. 可以自己定製響應相關信息,包括響應頭、響應體、響應行中的狀態碼等
        return  new ResponseEntity<String>(body,header, HttpStatus.OK);
    }
}

6 文件下載

package com.mashibing.controller;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.FileInputStream;

@Controller
public class OtherController {

    @RequestMapping("/download")
    public ResponseEntity<byte[]> download(HttpServletRequest request) throws Exception {
        ServletContext servletContext = request.getServletContext();
        //1. 獲取要下載文件的絕對路徑,也就是在電腦上的位置
        String realPath = servletContext.getRealPath("/script/jquery-1.9.1.min.js");
        FileInputStream fileInputStream = new FileInputStream(realPath);

        byte[] bytes = new byte[fileInputStream.available()];
        fileInputStream.read(bytes);
        fileInputStream.close();
        HttpHeaders httpHeaders = new HttpHeaders();
        //2. 設置響應頭中Content-Disposition爲指定內容,這樣瀏覽器纔會下載該資源,與servlet中方法一致
        httpHeaders.set("Content-Disposition","attachment;filename=jquery-1.9.1.min.js");
        //3. 設置響應體中內容就是文件的字節數組
        return  new ResponseEntity<byte[]>(bytes,httpHeaders,HttpStatus.OK);
    }
}

7 文件上傳

  1. Spring MVC 爲文件上傳提供了直接的支持,這種支持是通過即插即用(方法中定義了就會有該對象,沒定義就沒有)的MultipartResolver實現的
  2. Spring MVC沒有爲該類提供實現,我們常用Commons FileUpload中的CommonsMultipartResovler來作爲其具體實現,需導入如下pom依賴
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
  1. Spring MVC 上下文中默認沒有裝配 MultipartResovler,因此默認情況下不能處理文件的上傳工作,如果想使用 Spring 的文件上傳功能,需在applicationContext中配置 multipartResolver
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8"></property>
    <property name="maxUploadSize" value="1024000"></property>
</bean>
  1. index.jsp
<%--
  Created by IntelliJ IDEA.
  User: root
  Date: 2020/3/13
  Time: 17:00
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <!--1. 必須加multipart才能完成上傳操作-->
  <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>

  1. UploadHandler.java
package com.mashibing.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

@Controller
public class UploadHandler {

    @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);
        //1. 獲取上傳的文件名稱
        System.out.println("OriginalFilename : " + multipartFile.getOriginalFilename());
        //2. 將這個文件最後放到D:\\file
        multipartFile.transferTo(new File("E:\\file\\"+multipartFile.getOriginalFilename()));
        return "success"; //增加成功頁面: /views/success.jsp
    }
}

7.1 多文件上傳

  1. index.jsp
<%--
  Created by IntelliJ IDEA.
  User: root
  Date: 2020/3/13
  Time: 17:00
  To change this template use File | Settings | File Templates.
--%>
<%@ 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="file" name="file"/><br><br>
    文件: <input type="file" name="file"/><br><br>
    描述: <input type="text" name="desc"/><br><br>
    <input type="submit" value="提交"/>
  </form>
  </body>
</html>

  1. UploadHandler.java
package com.mashibing.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

@Controller
public class UploadHandler {

    @RequestMapping(value = "/testUpload", method = RequestMethod.POST)
   	//1. 如果頁面中配置了多個名爲file的屬性,那麼可以將MultipartFile改爲數據對文件進行接收
    public String testUpload(@RequestParam(value = "desc", required = false) String desc, @RequestParam("file") MultipartFile[] multipartFile) throws IOException {
        System.out.println("desc : " + desc);
        for (MultipartFile file : multipartFile) {
            //2. 防止雖然寫了三個file,但實際只上傳了兩個文件
            if (!file.isEmpty()) {
                System.out.println("OriginalFilename : " + file.getOriginalFilename());
                file.transferTo(new File("E:\\file\\" + file.getOriginalFilename()));
            }
        }
        return "success"; 
    }
}

8 Springmvc攔截器

  1. SpringMVC提供了攔截器機制,允許運行目標方法之前進行一些攔截工作或者目標方法運行之後進行一下其他相關的處理。自定義的攔截器必須實現HandlerInterceptor接口
  2. 該接口提供如下幾個抽象方法
    1. preHandle()
      1. 業務處理器處理請求之前調用,一般用於處理request
      2. 如果程序員決定該攔截器對請求進行攔截後還要調用其他攔截器,或業務處理器去進行處理,則返回true,如果程序員決定不需要再調用其他的組件去處理請求,則返回false
    2. postHandle()
      1. 業務處理器處理請求後、頁面跳轉前調用
    3. afterCompletion()
      1. 跳轉的頁面執行完成後被調用
      2. 一般在該方法中進行一些資源清理的操作
      3. 如果執行到方法中出現異常,那麼後續流程不會處理但是afterCompletion方法會執行

8.1 自定義第一個攔截器

  1. MyFirstInterceptor.java
package com.mashibing.interceptor;


import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyFirstInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(this.getClass().getName()+"------->preHandle");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println(this.getClass().getName()+"------->postHandle");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println(this.getClass().getName()+"------->afterCompletion");
    }
}

  1. TestInterceptorController.java
package com.mashibing.controller;

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

@Controller
public class TestInterceptorController {

    @RequestMapping("test01")
    public String test01(){
        System.out.println("test01");
        return "success";
    }
}
  1. springmvc.xml
<mvc:interceptors>
    <bean class="com.mashibing.interceptor.MyFirstInterceptor"></bean>
</mvc:interceptors>
  1. success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<% System.out.println("success.jsp");%>
success
</body>
</html>

8.2 定義多個攔截器

  1. MySecondInterceptor.java:另一個攔截器
package com.mashibing.interceptor;


import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MySecondInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(this.getClass().getName()+"------->preHandle");
        return true;
    }
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println(this.getClass().getName()+"------->postHandle");
    }
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println(this.getClass().getName()+"------->afterCompletion");
    }
}

  1. springmvc.xml
```xml
<mvc:interceptors>
    <bean class="com.mashibing.interceptor.MySecondInterceptor"></bean>
    <bean class="com.mashibing.interceptor.MyFirstInterceptor"></bean>
</mvc:interceptors>
  1. 執行順序
    1. 哪個攔截器先執行取決於配置的順序
    2. 攔截器的preHandle是按照順序執行的
    3. 攔截器的postHandle是按照逆序執行的
    4. 攔截器的afterCompletion是按照逆序執行的
    5. 如果執行的時候核心的業務代碼出問題了,那麼已經通過的攔截器的afterCompletion會接着執行
com.mashibing.interceptor.MySecondInterceptor------->preHandle
com.mashibing.interceptor.MyFirstInterceptor------->preHandle
test01
com.mashibing.interceptor.MyFirstInterceptor------->postHandle
com.mashibing.interceptor.MySecondInterceptor------->postHandle
success.jsp
com.mashibing.interceptor.MyFirstInterceptor------->afterCompletion
com.mashibing.interceptor.MySecondInterceptor------->afterCompletion

9 攔截器跟過濾器的區別

  1. 攔截器的本質實際上就是利用AOP爲業務處理器處理的請求前後加上了內容
  2. 攔截器與過濾器的執行流程
    在這裏插入圖片描述
  3. 攔截器和過濾器的包含關係
    在這裏插入圖片描述

10 SpringMVC國際化

  1. 在日常工作中,如果你的網站需要給不同語言地區的人進行查看,此時就需要使用國際化操作,springmvc的國際化操作比較容易
  2. 瀏覽器默認按請求頭中Accept-Language決定使用中文頁面還是英文頁面,而Accept-Language的值和瀏覽器的默認語言有關
  3. 在DispatcherServlet中會包含一個LocaleResolver屬性,保存獲取區域信息的解析器
  4. 該值默認從DispatcherServlet.properties中讀取,爲一個AcceptHeaderLocaleResolver對象
  5. 該對象的resolveLocale方法, 默認是從Accept-Language屬性中讀取,如果該屬性值不存在,從request.getLocale中讀取,而request.getLocale值默認也從Accept-Language屬性中讀取
  6. 而ResourceBundleMessageSource默認以AcceptHeaderLocaleResolver獲取到的語言,匹配properties文件

10.1 在程序中可以獲取Locale的相關信息

package com.mashibing.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Locale;

@Controller
public class I18nController {

    @Autowired
    private MessageSource messageSource;

    @RequestMapping("i18n")
    public String i18n(Locale locale){
        //打印:en_US
        System.out.println(locale);
        String username = messageSource.getMessage("username", null, locale);
        System.out.println(username);
        return "login";
    }
}

10.2 一個國際化配置案例

  1. index.jsp:方便發送i18n請求,其實不寫這層也可以
<%--
  Created by IntelliJ IDEA.
  User: root
  Date: 2020/3/13
  Time: 17:00
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <a href="i18n">國際化頁面登錄</a>
  </body>
</html>

  1. login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<!--1. fmt標籤中,key的值,爲properties文件中的key值,最後頁面中顯式的爲該key對應的value值-->
<h1><fmt:message key="welcomeinfo"/></h1>
<form action="login" method="post">
    <fmt:message key="username"/>: <input type="text" name="username"/><br><br>
    <fmt:message key="password"/>: <input type="password" name="password"/><br><br>
    <input type="submit" value="<fmt:message key="loginBtn"/>"/>
</form>
</body>
</html>
  1. 上面使用了jstl標籤庫,因此需要導入pom依賴
<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
<dependency>
    <groupId>org.apache.taglibs</groupId>
    <artifactId>taglibs-standard-impl</artifactId>
    <version>1.2.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
  1. I18nController.java
package com.mashibing.controller;

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

@Controller
public class I18nController {

    @RequestMapping("i18n")
    public String i18n(){
        return "login";
    }
}
  1. 需要在classpath中建立properties文件
  2. login_en_US.properties
welcomeinfo=welcome to mashibing.com
username=USERNAME
password=PASSWORD
loginBtn=LOGIN
  1. login_zh_CN.properties
welcomeinfo=歡迎進入馬士兵教育
username=用戶名
password=密碼
loginBtn=登錄
  1. springmvc.xml
<!--1. 這個類的作用就是讀取資源屬性文件.properties,然後根據AcceptHeaderLocaleResolver與.properties文件名進行匹配-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
   <!--2. 只配置以login開頭的properties文件-->
    <property name="basename" value="login"></property>
</bean>

10.3 通過超鏈接來切換國際化

10.3.1 利用自定義LocaleResolver
  1. login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1><fmt:message key="welcomeinfo"/></h1>
<form action="login" method="post" >
    <fmt:message key="username"/>: <input type="text" name="username"/><br><br>
    <fmt:message key="password"/>: <input type="password" name="password"/><br><br>
    <input type="submit" value="<fmt:message key="loginBtn"/>"/>
    <!--1. 傳入一個local屬性,相當於發起uri?locale=zh_CN這種請求-->
    <a href="i18n?locale=zh_CN">中文</a><a href="i18n?locale=en_US">英文</a>
</form>
</body>
</html>

  1. MyLocaleResolver.java:自定義LocaleResolver
package com.mashibing;

import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
public class MyLocaleResolver implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        Locale locale = null;
        //1. 自定義的LocaleResolver中,不再默認返回request.getLocale,而是優先使用url中傳入的locale屬性
        String localeStr = request.getParameter("locale");
        if(localeStr!=null && ! "".equals(localeStr)){
            locale = new Locale(localeStr.split("_")[0],localeStr.split("_")[1]);
        }else{
            locale = request.getLocale();
        }
        return locale;
    }
	//2. 該方法不重要,使用其父類默認的實現即可
    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
        throw new UnsupportedOperationException(
                "Cannot change HTTP accept header - use a different locale resolution strategy");
    }
}
  1. springmvc.xml
</bean>
   <!--1. 配置爲使用自定義的LocaleResolver-->
   <bean id="localeResolver" class="com.mashibing.MyLocaleResolver">
</bean>
10.3.2 SpringMVC中自帶的SessionLocaleResolver
  1. 該LocaleResolver優先使用session中屬性名爲SessionLocaleResolver.LOCALE的屬性,作爲返回的Locale對象
  2. I18nController.java
package com.mashibing.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import javax.servlet.http.HttpSession;
import java.util.Locale;

@Controller
public class I18nController {
    @Autowired
    private MessageSource messageSource;
    @RequestMapping("i18n")
    public String i18n(@RequestParam(value = "locale",defaultValue = "zh_CN") String localeStr,Locale locale, HttpSession session){
        Locale l = null;
        if(localeStr!=null && ! "".equals(localeStr)){
            l = new Locale(localeStr.split("_")[0],localeStr.split("_")[1]);
        }else{
            l = locale;
        }
        //1. 只需要向session中加入名爲SessionLocaleResolver.LOCALE的屬性,ResourceBundleMessageSource就會使用該屬性值對應的Locale對象匹配properteis文件了
        session.setAttribute(SessionLocaleResolver.class.getName() + ".LOCALE",l);
        return "login";
    }
}
  1. springmvc.xml
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
</beans>
10.3.3 LocaleChangeInterceptor攔截器實現
  1. springmvc.xml
<!--1. 通過配置LocaleChangeInterceptor,我們可以動態改變本地語言。它會檢測請求中的參數並且改變地區信息。它調用LoacalResolver.setLocal()進行配置-->
<mvc:interceptors>
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
</mvc:interceptors>
  1. I18nController.java
package com.mashibing.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import javax.servlet.http.HttpSession;
import java.util.Locale;

@Controller
public class I18nController {

    @Autowired
    private MessageSource messageSource;

    @RequestMapping("i18n")
    public String i18n(@RequestParam(value = "locale",defaultValue = "zh_CN") String localeStr,Locale locale, HttpSession session){
        return "login";
    }
}

11 SpringMVC異常處理機制

可以解決當報錯時,彈出特別難看的404頁面的問題,對異常統一進行處理

11.1 異常處理類的加載

  1. 在SpringMVC中擁有一套非常強大的異常處理機制,SpringMVC通過HandlerExceptionResolver處理程序的異常,包括請求映射,數據綁定以及目標方法的執行時發生的異常
  2. Spring MVC有兩種加載異常處理類的方式
    1. 根據類型,這種情況下,會加載spring配置文件中所有實現了ExceptionResolver接口的bean
    2. 根據名字,這種情況下會加載spring配置文件中,名字爲handlerExceptionResolver的bean
  3. 不管使用那種加載方式,如果在spring配置文件中中沒有找到異常處理bean,那麼Spring MVC會加載默認的異常處理bean。默認的異常處理bean定義在DispatcherServlet.properties中
org.springframework.web.servlet.HandlerExceptionResolver=
#1. 該處理器會使用@ExceptionHandler註解和@ControllerAdvice定義的方法處理異常
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
#2. 處理使用了ResponseStatus註解的異常,該處理器會根據註解的內容,返回相應的HTTP Status Code和內容給客戶端
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
#3. 處理Spring定義的各種標準異常,將其轉化爲相對應的HTTP Status Code
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

11.2 Spring MVC對異常處理bean的使用

  1. Spring MVC把請求映射和處理過程放到try catch中,捕獲到異常後,使用異常處理bean依次進行處理
  2. 所有異常處理bean按照order屬性排序,在處理過程中,遇到第一個成功處理異常的異常處理bean之後,不再調用後續的異常處理bean

11.3 最佳實踐

  1. 如果自定義異常類,應加上@ResponseStatus,這樣至少能夠返回指定響應碼和自定義的報錯信息
  2. 非@ResponseStatus修飾的異常,一般使用@ExceptionHandler+@ControllerAdvice,或者通過配置SimpleMappingExceptionResolver,來爲整個Web應用提供統一的異常處理
  3. 如果應用中有些異常處理方式,只針對特定的Controller使用,那麼在這個Controller中使用@ExceptionHandler註解
  4. 不要使用過多的異常處理方式,不然的話,維護起來會很苦惱,因爲異常的處理分散在很多不同的地方

11.4 @ExceptionHandler

  1. index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
<a href="exception1">自己處理異常</a>
  </body>
</html>
  1. ExceptionController.java
package com.mashibing.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.jws.WebParam;

@Controller
public class ExceptionController {

    @RequestMapping("exception1")
    public String exception(){
        System.out.println("exception.......");
        System.out.println(10/0);
        return "success";
    }

    @ExceptionHandler(value = {ArithmeticException.class})
    public ModelAndView handlerException1(Exception exception){
        System.out.println("handlerException1........");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        mv.addObject("ex",exception);
        return mv;
    }

    @ExceptionHandler(value = {Exception.class})
    public ModelAndView handlerException2(Exception exception){
        System.out.println("handlerException2........");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        mv.addObject("ex",exception);
        return mv;
    }
}
  1. error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
我的出錯頁面:
錯誤信息:${ex}
</body>
</html>
  1. 在一個類中可能會包含多個@ExceptionHandler修飾的異常的處理方法,在不同的方法上可以使用不同範圍的異常,在查找的時候會優先調用範圍小的方法進行異常處理

11.5 @ControllerAdvice

  1. 某一個類中定義的@ExceptionHandler只能處理當前類的異常信息,無法處理其他類中的異常
  2. 可以使用@ControllerAdvice註解表示全局異常處理類,可以處理其他類中產生的異常
  3. 如果同時使用了@ExceptionHandler和@ControllerAdvice,每次進行異常處理時,如果本類跟全局都有相關異常的處理,那麼會優先使用本類的,異常從小到大,再使用全局的,異常從小到大
  4. MyGlobalExceptionHandler
package com.mashibing.controller;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class MyGlobalExceptionHandler {
    @ExceptionHandler(value = {ArithmeticException.class})
    public ModelAndView handlerException1(Exception exception){
        System.out.println("handlerException1........");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        mv.addObject("ex",exception);
        return mv;
    }
}

11.6 @ResponseStatus的使用

注意默認配置中,如果有@ExceptionHandler和@ControllerAdvice,會使用它們對異常進行處理

11.6.1 在方法上使用
  1. @ResponseStatus可以標註到方法上,但是標註在方法之後,如果value設置的值不是HttpStatus.OK,會導致即使方法中沒有報錯也會顯式報錯頁面,因此一般不在方法上使用
  2. ExceptionController
package com.mashibing.controller;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;

import javax.jws.WebParam;

@Controller
public class ExceptionController {

    @ResponseStatus(reason = "不知道什麼原因,反正錯誤",value = HttpStatus.NOT_ACCEPTABLE)
    @RequestMapping("exception1")
    public String exception(){
        System.out.println("exception.......");
        return "success";
    }
}
  1. 請求結果
    在這裏插入圖片描述
11.6.2 在自定義異常上使用
  1. UserNameException.java
package com.mashibing.controller;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
//reason爲頁面返回的消息詳情,value爲消息錯誤代碼
@ResponseStatus(reason = "名字不是admin",value = HttpStatus.NOT_ACCEPTABLE)
public class UserNameException extends RuntimeException {
}
  1. ExceptionController.java
package com.mashibing.controller;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;

import javax.jws.WebParam;

@Controller
public class ExceptionController {
    @RequestMapping("exception1")
    public String exception(){
        System.out.println("exception.......");
        return "success";
    }
    @RequestMapping("exception2")
    public String exception2(String username){
        System.out.println("exception2222.......");
        if ("admin".equals(username)){
            return "success";
        }else{
            throw new UserNameException();
        }
    }
}

11.7 DefaultHandlerExceptionResolver處理springmvc自定義的異常

  1. index.jsp
<%--
  Created by IntelliJ IDEA.
  User: root
  Date: 2020/3/13
  Time: 17:00
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<a href="exception3">Springmvc自己異常處理</a>
</body>
</html>
  1. ExceptionController.java
package com.mashibing.controller;

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

@Controller
public class ExceptionController {
    //1. 此處由於只接收post請求,因此我們jsp頁面中發送的get請求將無法被接收,會拋出springmvc自定義的一個異常
    //2. 我們可以藉此觀察DefaultHandlerExceptionResolver如何處理這種異常
    @RequestMapping(value = "exception3",method = RequestMethod.POST)
    public String exception3(String username){
        System.out.println("exception3.......");
        return "success";
    }
}

  1. 請求結果
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章