SpringMVC(下)

一、访问静态资源

在进行Spring MVC的配置时,通常我们会配置一个dispatcher servlet用于处理对应的URL

在设置url-pattern时可以设置三种形式

(1)/* :拦截所有 jsp js png .css 真的全拦截。不建议使用。

(2)/ :拦截所有,不包括jsp,包含.js .png.css 建议使用。

(3)**.action .do :拦截以do action 结尾的请求。

<!-- 配置SpringMVC前端控制器 -->
<servlet>
    <servlet-name>mySpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 指定SpringMVC配置文件 -->
    <!-- SpringMVC的配置文件的默认路径是/WEB-INF/${servlet-name}-servlet.xml -->
    <init-param>
        <!-- DispatcherServlet类的初始化参数 -->
        <param-name>contextConfigLocation</param-name>
        <!-- 初始化参数的值,即springmvc配置文件的路径 -->
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!-- 表示web应用启动即加载该servlet -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>mySpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

url-pattern为 / 时,访问静态资源,会被拦截,找不到静态资源

解决方法:

(1)在springmvc.xml中配置

<mvc:default-servlet-handler/>

自动的根据访问地址看下是不是静态资源,如果是直接放行,如果不是就去找 @RequestMapping

(2)在springmvc.xml中配置 mvc:resources (推荐使用)

<mvc:resources location="/img/" mapping="/img/**"/>   
<mvc:resources location="/js/" mapping="/js/**"/>    
<mvc:resources location="/css/" mapping="/css/**"/>  

描述:

  • location元素表示webapp目录下的static包下的所有文件;
  • mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b;
  • 该配置的作用是:DispatcherServlet不会拦截以/static开头的所有请求路径,并当作静态资源交由Servlet处理。

二、Json处理

概述

当前端使用Ajax发送请求时,服务器要以JSON的数据格式响应给浏览器。

使用方式

@ResponseBody来实现;注解方式

@ResponseBody

(1)添加 json处理 相关jar包

+ jackson 2.9.9
    - jackson-annotations-2.9.9.jar
    - jackson-core-2.9.9.jar
    - jackson-databind-2.9.9.jar

(2)在springmvc.xml配置文件当中写上

<mvc:annotation-driven/>

(3)设置映射方法的返回值为@ResponseBody

请求发送页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
  </head>
  <body>
  
  <input type="button" value="请求JSON" id="btn">

  <script>
    $(function () {
        $('#btn').click(function () {
            // 发送Ajax 请求
            $.post("${pageContext.request.contextPath}/getJson",function (data) {
                console.log(data);
            })
        });
    });
  </script>
  </body>
</html>
方式1-直接返回一个对象
@Controller
public class MyController {
    @RequestMapping("getJson")
    @ResponseBody
    public User show(){
        User user = new User();
        user.setUsername("user2");
        user.setPassword("159357");
        user.setAge(20);
        user.setHobby(new String[]{"篮球","足球"});
        return user;
    }
}
方式2-返回一个List集合
@Controller
public class MyController {
    @RequestMapping("getJson")
    @ResponseBody
    public ArrayList<User> show(){
        User user1 = new User();
        user1.setUsername("user2");
        user1.setPassword("159357");
        user1.setAge(20);
        user1.setHobby(new String[]{"篮球","足球"});

        User user2 = new User();
        user2.setUsername("user1");
        user2.setPassword("123456");
        user2.setAge(18);
        user2.setHobby(new String[]{"篮球","足球"});

        ArrayList<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);

        return users;
    }
}
方式3-返回一个Map集合
@Controller
public class MyController {
    @RequestMapping("getJson")
    @ResponseBody
    public HashMap<String, Object> show(){
        User user1 = new User();
        user1.setUsername("user2");
        user1.setPassword("159357");
        user1.setAge(20);
        user1.setHobby(new String[]{"篮球","足球"});

        User user2 = new User();
        user2.setUsername("user1");
        user2.setPassword("123456");
        user2.setAge(18);
        user2.setHobby(new String[]{"篮球","足球"});

        ArrayList<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);

        HashMap<String, Object> map = new HashMap<>();
        map.put("name", "hashmap");
        map.put("list", users);
        return map;
    }
}

表单序列化

serialize()

请求发送页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
  </head>
  <body>

  <form id="myform">
    <p>用户Id :<input type="text" name="id"></p>
    <p>用户名 :<input type="text" name="username"></p>
    <p>密码 :<input type="text" name="password"></p>
    <p>性别 :
      <input type="radio" name="sex" value="男">男
      <input type="radio" name="sex" value="女">女
    </p>
    <p>年龄 :<input type="text" name="age"></p>
    <p>爱好 :
      <input type="checkbox" name="like" value="篮球">篮球
      <input type="checkbox" name="like" value="足球">足球
      <input type="checkbox" name="like" value="排球">排球
      <input type="checkbox" name="like" value="乒乓球">乒乓球
    </p>
  </form>

  <input type="button" value="发送form" id="btn">

  <script>
    $(function () {
        $('#btn').click(function () {
            // 获取表单所有参数,发送给服务器
            var serialize = $('#myform').serialize();
            console.log(serialize);
            // output: 参数=值&参数=值&参数=值
        });
    });
  </script>
  </body>
</html>

我们希望获取的是json格式的数据:

序列化转Json

(function($){
    $.fn.serializeJson=function(){
        var serializeObj={};
        var array=this.serializeArray();
        var str=this.serialize();
        $(array).each(function(){
            if(serializeObj[this.name]){
                if($.isArray(serializeObj[this.name])){
                    serializeObj[this.name].push(this.value);
                }else{
                    serializeObj[this.name]=[serializeObj[this.name],this.value];
                }
            }else{
                serializeObj[this.name]=this.value;
            }
        });
        return serializeObj;
    };
})(jQuery);

请求发送页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
  </head>
  <body>

  <form id="myform">
    <p>用户Id :<input type="text" name="id"></p>
    <p>用户名 :<input type="text" name="username"></p>
    <p>密码 :<input type="text" name="password"></p>
    <p>性别 :
      <input type="radio" name="gender" value="男">男
      <input type="radio" name="gender" value="女">女
    </p>
    <p>年龄 :<input type="text" name="age"></p>
    <p>爱好 :
      <input type="checkbox" name="hobby" value="篮球">篮球
      <input type="checkbox" name="hobby" value="足球">足球
      <input type="checkbox" name="hobby" value="排球">排球
      <input type="checkbox" name="hobby" value="乒乓球">乒乓球
    </p>
  </form>

  <input type="button" value="发送form" id="btn">

  <script>
    (function($){
        $.fn.serializeJson=function(){
            var serializeObj={};
            var array=this.serializeArray();
            var str=this.serialize();
            $(array).each(function(){
                if(serializeObj[this.name]){
                    if($.isArray(serializeObj[this.name])){
                        serializeObj[this.name].push(this.value);
                    }else{
                        serializeObj[this.name]=[serializeObj[this.name],this.value];
                    }
                }else{
                    serializeObj[this.name]=this.value;
                }
            });
            return serializeObj;
        };
    })(jQuery);

    $(function () {
        $('#btn').click(function () {
            // 获取表单所有参数,发送给服务器
            var serialize = $('#myform').serializeJson();
            console.log(serialize);
        });
    });
  </script>
  </body>
</html>

这时候的 serialize 就是以json格式的数据,我们再把数据发送给服务器

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
  </head>
  <body>

  <form id="myform">
    <p>用户Id :<input type="text" name="id"></p>
    <p>用户名 :<input type="text" name="username"></p>
    <p>密码 :<input type="text" name="password"></p>
    <p>性别 :
      <input type="radio" name="gender" value="男">男
      <input type="radio" name="gender" value="女">女
    </p>
    <p>年龄 :<input type="text" name="age"></p>
    <p>爱好 :
      <input type="checkbox" name="hobby" value="篮球">篮球
      <input type="checkbox" name="hobby" value="足球">足球
      <input type="checkbox" name="hobby" value="排球">排球
      <input type="checkbox" name="hobby" value="乒乓球">乒乓球
    </p>
  </form>

  <input type="button" value="发送form" id="btn">

  <script>
    (function($){
        $.fn.serializeJson=function(){
            var serializeObj={};
            var array=this.serializeArray();
            var str=this.serialize();
            $(array).each(function(){
                if(serializeObj[this.name]){
                    if($.isArray(serializeObj[this.name])){
                        serializeObj[this.name].push(this.value);
                    }else{
                        serializeObj[this.name]=[serializeObj[this.name],this.value];
                    }
                }else{
                    serializeObj[this.name]=this.value;
                }
            });
            return serializeObj;
        };
    })(jQuery);

    $(function () {
        $('#btn').click(function () {
            // 获取表单所有参数,发送给服务器
            var serialize = $('#myform').serializeJson();
            console.log(serialize);

            $.post("${pageContext.request.contextPath}/formJson",serialize,function (data) {
                console.log(data);
            })
        });
    });
  </script>
  </body>
</html>

Controller

@Controller
public class MyController {
    @RequestMapping("formJson")
    @ResponseBody
    public String show(User user){
        System.out.println(user);
        return "success";
    }
}

发现问题

运行后会发现,当爱好选一个的时候可以正常运行,当选择大等于两个的时候,会报错。

当传递复杂类型的时候,服务器不能接收。

这是因为,默认情况下,在发送请求的时候 Content-Type 都是 application/x-www-form-urlencoded; 直接使用@RequestParam接收参数。 urlencoded 只能发送简单的 key-value 字符串类型。

@RequestBody

默认情况下我们发送的都是 Content-Type: application/x-www-form-urlencoded,直接使用@RequestParam接收参数。

如果不是Content-Type: application/x-www-form-urlencoded 编码的内容,例如application/json, application/xml等;使用@RequestBody接收。

解决方法

在发送的时候以 json 的形式发送,接受的时候以 json 的形式接收。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
  </head>
  <body>

  <form id="myform">
    <p>用户Id :<input type="text" name="id"></p>
    <p>用户名 :<input type="text" name="username"></p>
    <p>密码 :<input type="text" name="password"></p>
    <p>性别 :
      <input type="radio" name="gender" value="男">男
      <input type="radio" name="gender" value="女">女
    </p>
    <p>年龄 :<input type="text" name="age"></p>
    <p>爱好 :
      <input type="checkbox" name="hobby" value="篮球">篮球
      <input type="checkbox" name="hobby" value="足球">足球
      <input type="checkbox" name="hobby" value="排球">排球
      <input type="checkbox" name="hobby" value="乒乓球">乒乓球
    </p>
  </form>

  <input type="button" value="发送form" id="btn">

  <script>
    (function($){
        $.fn.serializeJson=function(){
            var serializeObj={};
            var array=this.serializeArray();
            var str=this.serialize();
            $(array).each(function(){
                if(serializeObj[this.name]){
                    if($.isArray(serializeObj[this.name])){
                        serializeObj[this.name].push(this.value);
                    }else{
                        serializeObj[this.name]=[serializeObj[this.name],this.value];
                    }
                }else{
                    serializeObj[this.name]=this.value;
                }
            });
            return serializeObj;
        };
    })(jQuery);

    $(function () {
        $('#btn').click(function () {
            // 获取表单所有参数,发送给服务器
            var serialize = $('#myform').serializeJson();
            console.log(serialize);

            $.ajax({
                type:"post",
                url:"${pageContext.request.contextPath}/formJson",
                data:JSON.stringify(serialize),
                dataType:'json',
                contentType:'application/json',
                success:function (data) {
                    console.log(data);
                }
            });
        });
    });
  </script>
  </body>
</html>

Controller

@Controller
public class MyController {
    @RequestMapping("formJson")
    @ResponseBody
    public String show(@RequestBody User user){
        System.out.println(user);
        return "success";
    }
}

这样当爱好选择两个以上的时候就可以接收复杂类型的数据。

现在还有一个小问题:当爱好选择一个时,服务器接收不了。

原因是 当选择一个时,json串中hobby的数据为String类型,而hobby需要数组类型,Content-Type:application/x-www-form-urlencoded; 会自动帮我们解析,而 application/json 不会。所以我们要自己解析。

解决方法:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
  </head>
  <body>

  <form id="myform">
    <p>用户Id :<input type="text" name="id"></p>
    <p>用户名 :<input type="text" name="username"></p>
    <p>密码 :<input type="text" name="password"></p>
    <p>性别 :
      <input type="radio" name="gender" value="男">男
      <input type="radio" name="gender" value="女">女
    </p>
    <p>年龄 :<input type="text" name="age"></p>
    <p>爱好 :
      <input type="checkbox" name="hobby" value="篮球">篮球
      <input type="checkbox" name="hobby" value="足球">足球
      <input type="checkbox" name="hobby" value="排球">排球
      <input type="checkbox" name="hobby" value="乒乓球">乒乓球
    </p>
  </form>

  <input type="button" value="发送form" id="btn">

  <script>
    (function($){
        $.fn.serializeJson=function(){
            var serializeObj={};
            var array=this.serializeArray();
            var str=this.serialize();
            $(array).each(function(){
                if(serializeObj[this.name]){
                    if($.isArray(serializeObj[this.name])){
                        serializeObj[this.name].push(this.value);
                    }else{
                        serializeObj[this.name]=[serializeObj[this.name],this.value];
                    }
                }else{
                    serializeObj[this.name]=this.value;
                }
            });
            return serializeObj;
        };
    })(jQuery);

    $(function () {
        $('#btn').click(function () {
            // 获取表单所有参数,发送给服务器
            var serialize = $('#myform').serializeJson();
            console.log(serialize);
            // 解析
            if (typeof serialize.hobby == "string") {
                serialize.hobby = new Array(serialize.hobby);
            }

            $.ajax({
                type:"post",
                url:"${pageContext.request.contextPath}/formJson",
                data:JSON.stringify(serialize),
                dataType:'json',
                contentType:'application/json',
                success:function (data) {
                    // alert(data.responseText)
                    console.log(data);
                }
            });
        });
    });
  </script>
  </body>
</html>

@RequestBody 获取文件信息

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
  </head>
  <body>
  <form action="${pageContext.request.contextPath}/myfile" method="post" enctype="multipart/form-data">
    <input type="file" name="myfile">
    <input type="submit" value="提交">
  </form>
  </body>
</html>

Controller

@RequestMapping("myfile")
@ResponseBody
public String file(@RequestBody String file){
    System.out.println(file);
    return "success";
}

三、视图解析器

视图解析器

请求处理方法执行完成后,最终返回一个 ModelAndView 对象

对于那些返回 String,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个 ModelAndView 对象。

它包含了逻辑名和模型对象的视图,Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP 。

视图

视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户,视图对象由视图解析器负责实例化

在org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口

常见实现类:InternalResourceView,将JSP或其它资源封装成一个视图,是InternalResourceViewResoler默认使用的实现类。

源码分析

执行DispatcherServlet

(1)获取mapping映射

(2)获取适配器

(3)调用处理器,执行映射方法,返回MV

(4)处理转发页面

(5)在方法内部渲染页面

(6)创建视图对象

(7)调用View对象渲染页面

(8)在render内部解析数据

(9)转发到jsp页面

四、文件下载

请求页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js"></script>
  </head>
  <body>
  <a href="${pageContext.request.contextPath}/download/背景.jpg">下载背景.jpg</a>
  </body>
</html>

Controller

package com.ssm.web.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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLEncoder;

@Controller
public class MyController {
    @RequestMapping("/download/{filename:.+}")
    public ResponseEntity download(@PathVariable String filename, HttpSession session) throws Exception {
        System.out.println(filename);
        /* 1.获取文件路径 */
        // 获取 servletContext
        ServletContext servletContext = session.getServletContext();
        // 获取文件路径
        String realPath = servletContext.getRealPath("/download/" + filename);

        /* 2.把文件读取程序当中 */
        // 创建一个输入流
        InputStream io = new FileInputStream(realPath);
        byte[] body = new byte[io.available()];
        // 将文件流读到数组当中
        io.read(body);

        /* 3.创建响应头 */
        // 创建请求头
        HttpHeaders httpHeaders = new HttpHeaders();
        // 对文件名进行中文编码
        filename = URLEncoder.encode(filename,"UTF-8");
        // 告诉浏览器以附件形式下载文件
        httpHeaders.add("Content-Disposition","attachment;filename="+filename);
        // 设置响应码
        HttpStatus statusCode = HttpStatus.OK;
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(body, httpHeaders, statusCode);
        // 关闭流
        io.close();

        return responseEntity;
    }
}

注意

@RequestMapping("/download/{filename}") :只会传文件名,不会带后缀

@RequestMapping("/download/{filename:.+}") :会传文件名带后缀

五、文件上传

概述

Spring MVC 为文件上传提供了直接的支持,是通过即插即用的 MultipartResolver 实现的。

MultipartResolver是一个接口,Spring MVC 上下文中默认没有装配 MultipartResovler,如果想使用 Spring 的文件上传功能,就必须得要自己下载相关jar包,自己到配置文件当中装配到springMVC当中。

上传步骤

(1)导入相关jar包

- com.springsource.org.apache.commons.fileupload-1.2.0.jar
- com.springsource.org.apache.commons.io-1.4.0.jar
- com.springsource.org.apache.commons.logging-1.1.1.jar

(2)在springmvc配置文件当中装配MultipartResovler

<!--上传文件配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="maxUploadSize" value="102400"/>
</bean>

(3)实现上传代码

@RequestMapping("/download/{filename:.+}")
public ResponseEntity download(@PathVariable String filename, HttpSession session) throws Exception {
    System.out.println(filename);
    /* 1.获取文件路径 */
    // 获取 servletContext
    ServletContext servletContext = session.getServletContext();
    // 获取文件路径
    String realPath = servletContext.getRealPath("/download/" + filename);

    /* 2.把文件读取程序当中 */
    // 创建一个输入流
    InputStream io = new FileInputStream(realPath);
    byte[] body = new byte[io.available()];
    // 将文件流读到数组当中
    io.read(body);

    /* 3.创建响应头 */
    // 创建请求头
    HttpHeaders httpHeaders = new HttpHeaders();
    // 对文件名进行中文编码
    filename = URLEncoder.encode(filename, "UTF-8");
    // 告诉浏览器以附件形式下载文件
    httpHeaders.add("Content-Disposition", "attachment;filename=" + filename);
    // 设置响应码
    HttpStatus statusCode = HttpStatus.OK;
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(body, httpHeaders, statusCode);
    // 关闭流
    io.close();

    return responseEntity;
}

@RequestMapping("/upload")
public String upload(@RequestParam("file") CommonsMultipartFile file, HttpSession session) throws IOException {
    System.out.println("文件类型:" + file.getContentType());
    System.out.println("文件名:" + file.getOriginalFilename());
    System.out.println("文件大小:" + file.getSize());
    System.out.println("表单name属性:" + file.getName());

    // 确定上传的路径
    ServletContext servletContext = session.getServletContext();
    String realPath = servletContext.getRealPath("/upload");
    // 变成程序当中的路径
    File uploadPath = new File(realPath);
    if (!uploadPath.exists()) {
        // 如果路径不存在 ,创建一个新的
        uploadPath.mkdirs();
    }
    // 确认最终的路径  /文件夹/文件名    工程的名称/upload/java.png
    String fileName = file.getOriginalFilename();
    uploadPath = new File(uploadPath + "/" + fileName);

    // 开始上传
    file.transferTo(uploadPath);

    return "result.jsp";
}

多文件上传

WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。

文件上传的代码不动,只要前端页面加入WebUploader插件即可。

引入插件

- webuploader.css
- Uploader.swf
- webuploader.js
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <%--引入css样式--%>
    <link href="${pageContext.request.contextPath}/css/webuploader.css" rel="stylesheet" type="text/css"/>
    <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.min.js" type="text/javascript"></script>
    <%--引入文件上传插件--%>
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/webuploader.js"></script>
    <script type="text/javascript">
        $(function () {
            var $ = jQuery,
                $list = $('#fileList'),
                //优化retina, 在retina下这个值是2
                ratio = window.devicePixelRatio || 1,
                // 缩略图大小
                thumbnailWidth = 100 * ratio,
                thumbnailHeight = 100 * ratio,
                // Web Uploader实例uploader;
                // 初始化Web Uploader
                uploader = WebUploader.create({
                    // 自动上传。
                    auto: false,
                    // swf文件路径
                    swf: '${pageContext.request.contextPath}/js/Uploader.swf',
                    // 文件接收服务端。
                    server: '${pageContext.request.contextPath}/upload',
                    fileVal: 'file',
                    threads: '30',      //同时运行30个线程传输
                    fileNumLimit: '10',   //文件总数量只能选择10个 
                    // 选择文件的按钮。可选。
                    pick: {
                        id: '#filePicker',  //选择文件的按钮
                        multiple: true        //允许可以同时选择多个图片
                    },
                    //图片质量,只有type为`image/jpeg`的时候才有效。
                    quality: 100,
                    //限制传输文件类型,accept可以不写 (用于显示文件类型筛选)
                    /* accept: {
                    title: 'Images',//描述
                    extensions: 'gif,jpg,jpeg,bmp,png,zip',//类型
                    mimeTypes: 'image/*'//mime类型
                   } */
                });

            // 当有文件添加进来的时候,创建img显示缩略图使用
            uploader.on('fileQueued', function (file) {
                var $li = $(
                    '<div id="' + file.id + '" class="file-item thumbnail">' +
                    '<img>' +
                    '<div class="info">' + file.name + '</div>' +
                    '</div>'
                    ),
                    $img = $li.find('img');
                // $list为容器jQuery实例
                $list.append($li);
                // 创建缩略图
                // 如果为非图片文件,可以不用调用此方法。
                // thumbnailWidth x thumbnailHeight 为 100 x 100
                uploader.makeThumb(file, function (error, src) {
                    if (error) {
                        $img.replaceWith('<span>不能预览</span>');
                        return;
                    }
                    $img.attr('src', src);
                }, thumbnailWidth, thumbnailHeight);
            });
            // 文件上传过程中创建进度条实时显示。   
            // uploadProgress事件:上传过程中触发,携带上传进度。
            // file文件对象 percentage传输进度 Number类型
            uploader.on('uploadProgress', function (file, percentage) {
                console.log(percentage);
            });
            // 文件上传成功时候触发,给item添加成功class,
            // 用样式标记上传成功。
            // file:文件对象,   
            // response:服务器返回数据
            uploader.on('uploadSuccess', function (file, response) {
                $('#' + file.id).addClass('upload-state-done');
                //console.info(response);
                $("#upInfo").html("<font color='red'>" + response._raw + "</font>");
            });
            // 文件上传失败
            // file:文件对象 ,
            // code:出错代码
            uploader.on('uploadError', function (file, code) {
                var $li = $('#' + file.id),
                    $error = $li.find('div.error');

                // 避免重复创建
                if (!$error.length) {
                    $error = $('<div class="error"></div>').appendTo($li);
                }
                $error.text('上传失败!');
            });

            // 不管成功或者失败,
            // 文件上传完成时触发。
            // file: 文件对象
            uploader.on('uploadComplete', function (file) {
                $('#' + file.id).find('.progress').remove();
            });
            //绑定提交事件
            $("#btn").click(function () {
                console.log("上传...");
                uploader.upload();   //执行手动提交
                console.log("上传成功");
            });
        });
    </script>
    <script>
        var contextpath = ${pageContext.request.contextPath};
    </script>
</head>
<body>
<h3>多文件上传</h3>
<!--dom结构部分-->
<div id="uploader-demo">
     <!--用来存放item-->
    <div id="fileList" class="uploader-list"></div>
    <div id="upInfo"></div>
    <div id="filePicker">选择文件</div>
</div>
<br>
<input type="button" id="btn" value="开始上传">
</body>
</html>

六、异常

概述

Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。

SpringMVC 提供的 HandlerExceptionResolver 的实现类。

异常处理

处理 Handler 中用 @ExceptionHandler 注解定义的方法。

@RequestMapping("testException")
public String testException() {
    int i = 1 / 0;
    return "/result.jsp";
}

@ExceptionHandler(value = ArithmeticException.class)
public String handleException(Exception e) {
    System.out.println("出现异常" + e.getMessage());
    return "/error.jsp";
}

@ExceptionHandler优先级

根据继承关系,找继承关系比较近的那一个。

@ExceptionHandler(value = ArithmeticException.class)
public String handleException(Exception e) {
    System.out.println("出现异常1" + e.getMessage());
    return "/error.jsp";
}


@ExceptionHandler(value = RuntimeException.class)
public String handleException2(Exception e) {
    System.out.println("出现异常2" + e.getMessage());
    return "/error.jsp";
}

@ExceptionHandler(value = Exception.class)
public String handleException3(Exception e) {
    System.out.println("出现异常3" + e.getMessage());
    return "/error.jsp";

}

@ControllerAdvice

如果在当前类中没有找到 @ExceptionHanler ,则会到 @ControllerAdvice 中的 @ExceptionHandler 注解方法

@ControllerAdvice
public class ExceptionController {
    @ExceptionHandler(value = Exception.class)
    public String handleException(Exception e) {
        System.out.println("出现异常" + e.getMessage());
        return "/error.jsp";
    }
}

七、国际化

概述

SpringMVC 根据 Accept-Language 参数判断客户端的本地化类型,当接受到请求时,SpringMVC 会在上下文中查找一个本地化解析器(LocalResolver),找到后使用它获取请求所对应的本地化类型信息。

默认实现过程

(1)要先创建国际化的资源文件

+ resource
    - language_en_US.properties
    - language_zh_CN.properties

新建配置文件时确保编码正确。在IDEA中

language_en_US.properties

welcome = welcome
introduce = This is Demo
name = ZhangSan

language_zh_CN.properties

welcome = 欢迎光临
introduce = 这是一个国际化示例
name = 张三

(2)添加配置文件 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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--注解扫描-->
    <context:component-scan base-package="com.ssm"/>

    <mvc:view-controller path="success" view-name="/result.jsp"/>
    <mvc:resources location="/imgs/" mapping="/imgs/**"/>
    <mvc:resources location="/js/" mapping="/js/**"/>
    <mvc:resources location="/css/" mapping="/css/**"/>
    <mvc:annotation-driven />

    <!--国际化-->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="language"/>
    </bean>
</beans>

(3)添加JSTL jar包

    - jstl-1.2.jar
    - standard-1.1.2.jar

(4)在页面当中编写标签

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>local</h1>
    <fmt:message key="welcome"/> <br>
    <fmt:message key="introduce"/> <br>
    <fmt:message key="name"/> <br>
<hr>
    <spring:message code="welcome"/> <br>
    <spring:message code="introduce"/> <br>
    <spring:message code="name"/> <br>
</body>
</html>

谷歌浏览器中可以设置不同语言来查看

原理

如果没有显式定义本地化解析器, SpringMVC 会使用 AcceptHeaderLocaleResolver:根据 HTTP 请求头的 Accept-Language 参数确定本地化类型。

中英文切换

概述

默认情况是通过AccepHeaderLocaleResolver来从浏览器当中获取语言信息。

可以从请求参数中获取本次请求对应的本地化类型。获取到本地类型后, 给写入到session当中。

实现

配置文件
<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--注解扫描-->
    <context:component-scan base-package="com.ssm"/>

    <mvc:view-controller path="success" view-name="/result.jsp"/>
    <mvc:resources location="/imgs/" mapping="/imgs/**"/>
    <mvc:resources location="/js/" mapping="/js/**"/>
    <mvc:resources location="/css/" mapping="/css/**"/>
    <mvc:annotation-driven />

    <!--国际化-->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename" value="language"/>
    </bean>

    <!--配置session本地解析器-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
    <mvc:interceptors>
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <property name="paramName" value="language"/>
        </bean>
    </mvc:interceptors>
</beans>
属性文件

language_en_US.properties

language.cn=Chinese
language.en=English
welcome=welcome
introduce=This is Demo
name=ZhangSan

language_zh_CN.properties

language.cn=中文
language.en=英文
welcome=欢迎光临
introduce=这是一个国际化示例
name=张三	
页面信息
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>local</h1>
<a href="?language=zh_CN"><fmt:message key="language.cn" /></a>
<a href="?language=en_US"><fmt:message key="language.en" /></a>
<hr>
    <fmt:message key="welcome"/> <br>
    <fmt:message key="introduce"/> <br>
    <fmt:message key="name"/> <br>
<hr>
    <spring:message code="welcome"/> <br>
    <spring:message code="introduce"/> <br>
    <spring:message code="name"/> <br>
</body>
</html>

八、拦截器

概述

Java 里的拦截器是动态拦截 action 调用的对象。可以在Controller中的方法执行之前与执行之后, 及页面显示完毕后,执行指定的方法。

自定义的拦截器必须实现HandlerInterceptor接口

方法介绍

preHandle()	 		// 在业务处理器处理请求之前被调用
postHandle   		// 在业务处理器处理完请求后
afterCompletion		// 在 DispatcherServlet 完全处理完请求后被调用

SpringMVC拦截器使用

(1)拦截所有请求

① 创建一个类实现HandlerInterceptor接口
package com.ssm.web.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 {

    @Override
    // 在处理器方法执行之前调用
    // 返回值: true-放行; false-不放行-执行不了处理器方法
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("在处理器方法执行之前调用");
        return true;
    }

    @Override
    // 在处理器方法执行之后自动调用
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("在处理器方法执行之后自动调用");
    }

    @Override
    // 请求处理完毕之后会调用
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("请求处理完毕之后会调用");
    }
}
② 配置文件当中添加拦截器
<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--注解扫描-->
    <context:component-scan base-package="com.ssm"/>

    <mvc:resources location="/imgs/" mapping="/imgs/**"/>
    <mvc:resources location="/js/" mapping="/js/**"/>
    <mvc:resources location="/css/" mapping="/css/**"/>
    <mvc:annotation-driven />


    <!--配置session本地解析器-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
    <mvc:interceptors>
        <bean class="com.ssm.web.interceptor.MyFirstInterceptor"></bean>
    </mvc:interceptors>
</beans>

(2)拦截指定请求

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--注解扫描-->
    <context:component-scan base-package="com.ssm"/>

    <mvc:resources location="/imgs/" mapping="/imgs/**"/>
    <mvc:resources location="/js/" mapping="/js/**"/>
    <mvc:resources location="/css/" mapping="/css/**"/>
    <mvc:annotation-driven />


    <!--配置session本地解析器-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>
    <mvc:interceptors>
        <!--自定义 拦截器  拦截所有请求-->
        <!--<bean class="com.ssm.web.interceptor.MyFirstInterceptor"></bean>-->

        <mvc:interceptor>
            <!--设置拦截哪些请求-->
            <mvc:mapping path="/local"/>
            <!--设置哪些请求不拦截-->
            <mvc:exclude-mapping path="/hello"/>
            <bean class="com.ssm.web.interceptor.MyFirstInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

(3)配置多个拦截器执行顺序

从上面结果可以看出,preHandle 是按正序调用,postHandle 和 afterCompletion是倒序调用

源码分析

preHandle 是正序遍历,postHandle 和 afterCompletion是倒序遍历

九、总结

posted @ 2019-10-21 20:53  Lomen~  阅读(545)  评论(1编辑  收藏  举报