SpringMVC

Spring为展现层提供的基于MVC设计理念的优秀的Web框架。

SpringMVC通过一套MVC注解,让POJO成为处理请求的控制器,而无须实现任何接口。

MVC:M 模型 V 视图 C 控制器

搭建过程:

1.导入jar包

  1. spring-web-4.0.0.RELEASE.jar
  2. spring-webmvc-4.0.0.RELEASE.jar

2.在web.xml 中配置springMVC的核心(前端)控制器DispatcherServlet。

3.创建一个POJO,在此类上加上@Controller注解,SpringMVC就会将此类作为控制层加载,让其处理请求响应。

4.在控制层中,需要在方法上设置@RequestMapping(value=”xxx”)。

SpringMVC就是通过此注解,将请求与控制层中的方法相匹配。

5.处理请求的方法会返回一个字符串,即视图名称。最终会通过配置文件中配置的视图解析器实现页面跳转

1.HelloWord

1.1web.xml配置

在web.xml 中配置SpringMVC的核心控制器 DispatcherServlet

web.xml配置三步走:

    1.配置核心控制器

    2.配置编码过滤器

    3.配置 HiddenHttpMethodFilter,用来使用REST。
<?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_3_1.xsd"
         version="3.1">

        <!--配置 DispatcherServlet-->
        <servlet>
            <servlet-name>springDispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>


            <!--配置 DispatcherServlet 的一个初始化参数:配置SpringMVC配置文件的位置和名称-->
            <!--实际上也可以不通过 contextConfigLocation 来配置 SpringMVC的配置文件,而使用
            默认的配置文件:/WEB-INF/<servlet-name>-servlet.xml-->
            <init-param>
                <!--contextConfigLocation 这个名字是固定的,上下文配置路径,是SpringMVC为我们设置的一个参数,用来更改配置文件的名字和地址。-->
                <param-name>contextConfigLocation</param-name>
                <!--写 SpringMVC配置文件所在的地址-->
                <param-value>classpath:com/guolian/SpringMVC/helloword/HelloWord.xml</param-value>
            </init-param>


            <!--用来设置 Servlet 的加载时间,默认在第一次访问时加载。
                若设置此标签:会将Servlet的加载时间提前到项目启动时。
                此标签需要写正整数,若写负数或0则使用默认配置。
                值越小,优先级越高。-->
            <load-on-startup>1</load-on-startup>
        </servlet>

        <servlet-mapping>
            <servlet-name>springDispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
</web-app>

1.2SpringMVC.xml

在Spring配置文件中配置

<?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.guolian.SpringMVC.helloword"></context:component-scan>

    <!--配置视图解析器:如何把 hello() 方法返回值解析为实际的物理视图-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/views/"></property>
        <property name="suffix" value=".jsp"></property>
        <!--视图解析器可以配置多个,order配置视图的优先级。
            一般情况下,只会创建一个。-->
        <property name="order" value="1"></property>
    </bean>
</beans>

1.3Controller

使用 @RequestMapping 注解来映射请求的URL:

当在页面中点击超链接<a href="helloword">。。。</a> 时,会自动跳转到hello()方法。

@Controller
public class Helloword {
    /**
     * 1.使用  @RequestMapping 注解来映射请求的URL
     * 2.返回值会通过视图解析器解析为实际的物理视图。对于InternalResourceViewResolver 视图解析器,会做如下解析:

     前缀和后缀在SrpingMVC.xml 中配置。
     * prefix(前缀) + 视图名称(return) + suffix(后缀)
     本例中:/views/success.jsp
     //会自动跳转到 web/views/success.jsp页面
     */
    @RequestMapping("/helloword")
    public String hello(){
        System.out.println("helloword");
        //success   :视图名称
        return "success";
    }
}

1.4JSP

1.4.1index.jsp
  <body>
<a href="helloword">Hello World</a>
  </body>

1.4.2success.jsp
<body>
    <h1>SUCCESS</h1>
</body>

2.@RequestMapping

  • @RequestMapping :设置请求映射, 把请求和控制层中的方法设置映射关系
  • @RequestMapping 可以加在类和方法上
  • 若类和方法上都加了 value 属性。则应该一层一层的访问,先访问类,再访问类中的方法

2.1value属性

value:用来设置请求路径。当请求路径和value属性值一致时,则该注解所标注的方法即为处理请求的方法

    /**
     * 当请求路径和 @RequestMapping 的value属性值一致时,则该注解所标注的方法即为处理请求的方法。
     当属性只有一个value时。value 可以省略。
     @RequestMapping(value = "/test") ==  @RequestMapping("/test")
     */
    @RequestMapping(value = "/test")
    public String test(){
        System.out.println("SUCCESS");
        return "success";
    }

2.2method属性

method :用来设置请求方式,只有当客户端发送请求的方式与method一致时,才能处理请求

2.2.1请求方式:
method = RequestMethod.GET    //用来查询
method = RequestMethod.PUT    //用来添加
                        DELETE    //用来删除
                        POST //用来添加
                        HEAD
                        PATCH
                        OPTIONS
                        TRACE

实例:
//一个请求路径,多个处理方法。
@RequestMapping(value = "/test",method = RequestMethod.POST)
public String testPOST(){
    System.out.println("SUCCESS.POST");
    return "success";
}
@RequestMapping(value = "/test",method = RequestMethod.GET)
public String testGET(){
    System.out.println("SUCCESS.GET");
    return "success";
}

2.3params属性

params:用来设置请求参数。(客户端传到服务器的数据)

    它支持表达式。

例如: username: 请求参数里必须包含 username

      !username:    请求参数里不能有 username

    username=admin:    请求参数里必须包含  username=admin

    username!=admin:    请求参数里不能包含  username=admin
@RequestMapping(value = "/test",params = {"username","age!=12"})

2.4headers

headers :设置请求头的信息,客户端所发送请求的请求头信息一定要和headers属性中所设置的一致

@RequestMapping(value = "/test",headers = {"Accept-Language=zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7"})

2.5Ant方式的访问路径

Ant支持3中匹配符:

?:匹配任意一个字符

*:匹配任意字符

**:任意匹配多层(几个”星”就几层)目录

//http://localhost:8080/SpringMVC01/avc/Antmm/testAnt
@RequestMapping(value = "/*/Ant??/testAnt")
public String testAnt(){
    System.out.println("SUCCESS.GET");
    return "success";
}

2.6映射请求占位符

@PathVariable(“….”):获取占位符中 …. 的值,并赋值给 形参里的 ….。

/**
 * 以前: localhost:8080/SpringMVC01/testREST?id=1001&username=admin
 * 现在: localhost:8080/SpringMVC01/testREST/1001/admin
 */
@RequestMapping("/testREST/{id}/{username}")
public String testREST(@PathVariable("id")Integer id,@PathVariable("username")String username){
    //@PathVariable("id"):获取占位符中 id  的值,并赋值给 形参里的 id。
    //@PathVariable("id"):获取占位符中 username  的值,并赋值给 形参里的 username。
    System.out.println("id:"+id+",username:"+username);
    return "success";
}

3.REST

REST:(资源)表现层状态转化。是目前最流行的一种互联网软件架构。

资源:网络上的实体。

表现层:把资源具体显示出来的形式。

状态转化:每发出一个请求,就代表了客户端和服务器的一次交互过程。

  • HTTP协议里四个表示操作的动词:GET、POST、PUT、DELETE。
  • GET:获取资源 POST:新建资源 PUT:更新资源 DELETE:删除资源

使用REST出现405错误

解决方法:
以上代码在控制台可以输出PUT但是页面405,所以在success.jsp目标页面的头信息加了这样一段

<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8" isErrorPage="true"%>

REST使用

jsp:
<body>
        <a href="testREST/1001">测试GET请求</a>
        <br/>
        <form action="testREST" method="post">
            <input type="submit" value="测试POST"/>
        </form>
        <br/>
        <form action="testREST" method="post">
            <input type="hidden" name="_method" value="PUT"/>
            <input type="submit" value="测试PUT"/>
        </form>
        <br/>
        <form action="testREST/1001" method="post">
            <input type="hidden" name="_method" value="DELETE"/>
            <input type="submit" value="测试DELETE"/>
        </form>
</body>

Controller:
@Controller
public class RESTController {

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

    @RequestMapping(value = "/testREST",method = RequestMethod.POST)
    public String InsertUser(){
        System.out.println("POST");
        return "success";
    }

    @RequestMapping(value = "/testREST",method = RequestMethod.PUT)
    public String updateUser(){
        System.out.println("PUT");
        return "success";
    }

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

filter:
<filter>
    <filter-name>hidden</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hidden</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

4.处理请求数据

4.1获取请求参数

在SpringMVC获取客户端传递的数据:

  1. 在处理请求的方法中,加入相对应的形参,保证形参参数名与传递的数据参数名一致就可以自动赋值
<form action="param" method="post">
    username:<input type="text" name="username"/><br/>
    password:<input type="text" name="password"/><br/>
    <input type="submit" />
</form>
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(String username,String password){
    //形参名与传递数据的参数名一致。
    System.out.println("username:"+username+",password:"+password);
    return "success";
}
  1. 当不满住赋值条件时,可以使用@RequestParam注解的value属性来指定映射关系
    @RequestParam属性:required 设置形参是否必须被赋值。默认是true。
    defaultValue 若形参所得值为null,则设置一个默认值。
    required 和 defaultValue 可以搭配使用,如果形参没有被赋值,则可以使用默认值。
@Controller
public class ParamController {
    @RequestMapping(value = "/param",method = RequestMethod.POST)
    public String param(@RequestParam(value = "username",required = false,defaultValue = "admin")String name, String password){
        System.out.println("username:"+name+",password:"+password);
        return "success";
    }
}

4.1.1@RequestParam

@RequestParam:设置映射关系。

**value**:用来指定映射关系,里面写传递的参数名。

**required**:设置形参是否必须被赋值,默认为true。

            如果是true,没有赋值就会报错。false,不报错。

**defaultValue**:设置形参的默认值。如果传递过来的参数是null,则为形参使用默认值。

4.1.2@RequestHeader

@RequestHeader:获取请求头的信息。

    与@RequestParam的使用方法一致。
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(@RequestHeader(value = "Accept-Language")String language){
    System.out.println("language:"+language);
    return "success";
}

4.1.3@CookieValue

@CookieValue:在处理请求的方法上,获取Cookie的信息。

    与@RequestParam的使用方法一致。
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(@CookieValue(value = "JSESSIONID")String JSESSIONID){
    System.out.println("JSESSIONID:"+JSESSIONID);
    return "success";
}

4.2使用POJO作为参数

可以使用POJO获取客户端数据。

要求:实体类中的属性名一定要和页面中表单元素的name属性值一致。而且支持级联关系

级联关系:
        查找address对象下的province属性:<br />

name=”address.province”

jsp:
<body>
<form action="param" method="post">
    username:<input type="text" name="username"/><br/>
    password:<input type="text" name="password"/><br/>
    age:<input type="text" name="age"/><br/>
    <!--级联关系,查找address对象下的province属性-->
    省:<input type="text" name="address.province"/><br/>
    市:<input type="text" name="address.city"/><br/>
    县:<input type="text" name="address.country"/><br/>
    <input type="submit" />
</form>
</body>

controller:
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(User user){
    System.out.println("user:"+user);
    return "success";
}

4.3使用Servlet原生API作为参数

可以通过设置形参的方式,获取ServletAPI

MVC的Hander方法可以接受哪些 ServletAPI类型的参数:

1.HttpServletRequest 2.HttpServletResponse 3.HttpSession

4.java.security.Principal 5.Locale 6.InputStream

7.OutputStream 8.Reader 9.Writer

@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(User user, HttpServletRequest request, HttpServletResponse response){
    String username = request.getParameter("username");
    System.out.println(username);
    return "success";
}

4.4ModelAndView类

管理模型(实体)和视图(页面)。

在最底层仍然是使用的 request.setAttribute() 和 request.getRequestDispatcher(“”).forward(request,response);

设置request的作用域和请求转发。

@RequestMapping(value = "/param",method = RequestMethod.POST)
public ModelAndView param(){
    ModelAndView mav = new ModelAndView();
    //往Request作用域中放值。
    mav.addObject("username","root");
    //设置视图名称,实现页面跳转
    mav.setViewName("success");
    return mav;
}

4.5三种方式往作用域放值

4.5.1ModelAndView
  1. ModelAndView

4.5.2Map
  1. 与 ModelAndView的执行过程是一样的(所使用的是同一段源码)。
    (因为在ModelAndView的底层就是使用这个Map存储参数和赋值)
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(Map<String,Object> map){
    //向作用域中放值
    map.put("username","admin");
    //返回视图名称
    return "success";
}

4.5.3Model
  1. 与 ModelAndView的执行过程是一样的(所使用的是同一段源码)。
@RequestMapping(value = "/param",method = RequestMethod.POST)
public String param(Model model){
    model.addAttribute("username","张三");
    return "success";
}

4.5.4总结:
根据ModelAndView源码调试。不管使用哪种方式给作用域赋值,最终都会把Model数据和View数据封装到 ModelAndView中。

5.视图解析

return "success";
return "redirect:/success.jsp";
return "forward:/success.jsp";
//这里面的返回值,就是视图名称。

5.1SpringMVC如何解析视图

不管控制器返回一个 Strng、ModelAndView、View 都会转换为 ModelAndView对象,由视图解析器(InternalResourceViewResolver)解析对象,然后,进行页面的跳转。

5.1.1View作用:
    处理模型数据,实现页面跳转(转发、重定向)。

5.1.2View类型:
InternalResourceView:转发视图。(不支持JSTL)

JstlView:转发视图。(支持JSTL)

RedirectView:重定向视图。

5.1.3View底层源码

redirect:/index.jsp(重定向到index.jsp)

    最底层:response.sendRedirect()

forward:/index.jsp(转发到index.jsp)

    最底层:request.forward()

6.Spring中的编码过滤器

编码过滤器必须配置在所有过滤器的第一个。(Spring 里有缓存)

在 init-param 标签里所写的 param-name 一定是class 里的一个属性

<!--设置编码过滤器-->
<filter>
    <filter-name>Cha</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--为编码过滤器类里的属性赋值,确定编码格式-->
    <!--在 init-param 里所写的一定是class里的一个属性-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>Cha</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

7.静态资源处理

静态资源一般都是交给Tomcat的默认servlet 来处理:DefaultServlet

然而当我们重新定义了一个servlet(dispatcher-servlet)且url-pattern的值与其相同时

系统就会默认的先执行我们所定义的servlet,此时就会造成静态资源访问不到。

就需要如下两行代码来设置:当前servlet找不到资源时,交给DefaultServlet来处理。

<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--mvc驱动,只要放在这就会自动实现功能-->
<mvc:annotation-driven></mvc:annotation-driven>

dispatcher-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.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.guolian.crud"></context:component-scan>

    <bean id="views" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"></property>
        <property name="suffix" value=".jsp"></property>
<!--        &lt;!&ndash;视图解析器可以配置多个,order配置视图的优先级&ndash;&gt;
        <property name="order" value="1"></property>-->
    </bean>

    <!--配置Tomcat中默认的servlet:DefaultServlet
        当DefaultServlet所设置的 url-pattern 和开发人员所配置的servlet的 url-pattern相同
        以开发人员所配置的servlet优先。-->
    <!--当客户端发送请求,由于DefaultServlet所设置的 url-pattern 和dispatcher-servlet的url-pattern相同,
        因此先通过 dispatcher-servlet 来处理请求,找该请求是否有相对应的处理器,有则处理,无则交给 DefaultServlet处理-->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>
    <!--mvc驱动,只要放在这就会自动实现功能-->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>

8.SpringMVC处理JSON

1.导入jackson的jar包

jackson-annotations-2.1.5.jar

jackson-core-2.1.5.jar

jackson-databind-2.1.5.jar

2.在springMVC的配置文件中开启MVC驱动

<mvc:annotation-driven></mvc:annotation-driven>

3.在处理JSON的方法上加上 注解 @ResponseBody

4.将要转换为JSON,且响应到客户端的数据,作为方法的返回值直接返回。

8.1例子:

jsp页面:
 <head>
   <title>$Title$</title>
     <script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
     <script type="text/javascript">
           $(function () {
               $("#btn").click(function () {
                  $.ajax({
                      url:"testJson",
                      type:"POST",
                      dataType:"json",
                      /*msg:响应回来的数据*/
                      success:function (msg) {
                          /*遍历JSON数组,i为数组下标*/
                          for(var i in msg){
                           var emp = msg[i];
                           alert("id="+emp.id+",lastName="+emp.lastName);
                          }
                      }
                  });
               });
           });
     </script>
 </head>
 <body>
 <input id="btn" type="button" value="测试Ajax">
<a href="testJson">测试json</a>
 </body>

java代码:
@Controller
public class TestJsonController {
    @Autowired
    private EmployeeDao employeeDao;
    /**
     * SpringMVC 处理 JSON
     * @ResponseBody :响应体,将该方法的返回值作为响应返回到客户端。
     *                  就算返回的是字符串,也会直接显示到客户端。
     */
    @RequestMapping("/testJson")
    @ResponseBody
    public Collection<Employee> testJson(){
        Collection<Employee> all = employeeDao.getAll();
        return all;
    }
}

8.2对JSON的简单操作:

<head>
    <title>$Title$</title>
    <script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn").click(function () {
                $.ajax({
                    url: "testJson",
                    type: "POST",
                    dataType: "json",
                    /*msg:响应回来的数据*/
                    success: function (msg) {
                        /*/!*遍历JSON数组,i为数组下标*!/
                            for(var i in msg){
                            var emp = msg[i];
                            alert("id="+emp.id+",lastName="+emp.lastName);
                                                  }*/

                        var tb = "<table>";
                        tb += "<tr><th>id</th><th>lastName</th><th>email</th><th>gender</th><th>departmentName</th></tr>";

                        for (var i in msg) {
                            var emp = msg[i];
                            tb += "<tr><td>" + emp.id + "</td><td>" + emp.lastName + "</td><td>" + emp.email + "</td><td>" + emp.gender + "</td><td>" + emp.department.departmentName + "</td></tr>";
                        }

                        tb += "</table>";

                        $("body").append(tb);
                    }
                });
            });
        });
    </script>
</head>
<body>
<input id="btn" type="button" value="测试Ajax">
<a href="testJson">测试json</a>
</body>

9.文件的上传和下载

9.1文件的下载

9.1.1HttpMessageConverter接口
HttpMessageConverter<T>负责将请求信息转换为一个对象(类型为T),将对象(类型为T)输出为响应信息。

DispatcherServlet默认装配RequestMappingHandlerAdapter,

而RequestMappingHandlerAdapter默认装配了 HttpMessageConverter。

9.1.2使用HttpMessageConverter

需要使用两个注解和两个类型实现:

使用 @RequestBody/@ResponseBody 对处理方法进行标注

使用 HttpEntity/ResponseEntity 作为处理方法的入参或返回值

ResponseEntity :实现下载 (Response:响应,Entity:实体)

9.1.3实现下载(代码):

HttpSession :主要是为了获取服务器的真实地址。

我们写代码的地方叫:命名空间。

服务器存储数据的地方,又是一个地址:叫真实地址。

@Controller
public class TestUploadAndDownController {

    @RequestMapping(value = "/down")
    public ResponseEntity<byte[]> down(HttpSession session) throws IOException {
        //获取下载文件的路径(映射到Tomcat里的真实路径)
        String realpath = session.getServletContext().getRealPath("img");
        //File.separator:文件分隔符,实际上就是  “/”
        String fianlPath = realpath + File.separator + "夹心酱2.png";
        FileInputStream fileInputStream = new FileInputStream(fianlPath);
        //available():获取输入流所读取的文件的最大字节数
        byte[] bytes = new byte[fileInputStream.available()];
        //把文件里的字节读取到数组中
        fileInputStream.read(bytes);

        //设置请求头
        HttpHeaders httpHeaders = new HttpHeaders();
        //attachment:附件的意思,以附件的方式下载。
        //filename=夹心.png:设置文件的名字
        httpHeaders.add("Content-Disposition","attachment;filename=夹心.png");

        //设置响应状态
        HttpStatus status = HttpStatus.OK;

        //关闭流
        fileInputStream.close();

        //返回值
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, httpHeaders,status);
        return responseEntity;
    }
}

9.2文件的上传

9.2.1MultipartFile

SpringMVC 独有的文件类型,它自动的帮我们封装了File类型,使用起来更加的方便。

它通过CommonsMultipartResolver(文件解析器)类 将File 类型封装成了 MultipartFile

不过需要在SpringMVC里配置一下

<!--文件解析器,将客户端上传的文件处理成 MultipartFile-->
<!--id 只能是 multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--这个bean里的属性都有默认值,都可以不写-->
    <!--设置文件解析的编码,要和页面的pageEncoding保持一致-->
    <property name="defaultEncoding" value="UTF-8"></property>
    <!--maxUploadSize:最大上传文件的大小,单位是字节。有默认值,可不写-->
    <property name="maxUploadSize" value="1000000000"></property>
</bean>

9.2.2初步实现上传(代码):
 @RequestMapping(value = "/up_old",method = RequestMethod.POST)
    public String up_old(String desc, MultipartFile uploudFile,HttpSession session) throws IOException {
        //MultipartFile :SpringMVC自动帮我们封装了File类型。


/*        //获得表单元素的name属性
        String name = uploudFile.getName();
        System.out.println("name:"+name);//uploudFile*/

        //获得上传的文件名
        String originalFilename = uploudFile.getOriginalFilename();
//        System.out.println("originalFilename:"+originalFilename);//夹心酱.png
        //设定上传的文件存储在服务器的哪个位置
        String Path = session.getServletContext().getRealPath("img")+File.separator+originalFilename;
        //获取输入流
        InputStream inputStream = uploudFile.getInputStream();
        //获取输出流
        File file = new File(Path);
        FileOutputStream fileOutputStream = new FileOutputStream(file);

        int i = 0;
        while ((i=inputStream.read())!=-1){
            fileOutputStream.write(i);
        }
        fileOutputStream.close();
        inputStream.close();

        return "success";
    }

9.2.3上传最终代码:

若要实现批量上传:

1.将形参里的传过来的 MultipartFile 改成 MultipartFile[]

2.用 for 循环 将主要代码包起来。

    @RequestMapping(value = "/up",method = RequestMethod.POST)
    public String up(String desc, MultipartFile uploudFile, HttpSession session) throws IOException {
        //获得上传的文件名
            String filename = uploudFile.getOriginalFilename();

            //UUID.randomUUID():获取一个通用唯一识别码(一个很长的唯一的字符串)
            //filename.substring(filename.lastIndexOf(".")); :截取文件的后缀名。(判断同类型文件是否重名)
            String finalFileName = UUID.randomUUID() + filename.substring(filename.lastIndexOf("."));
            //设定上传的文件存储在服务器的哪个位置
            String Path = session.getServletContext().getRealPath("img") + File.separator + finalFileName;

            File file = new File(Path);
            //transferTo :转换,将 uploudFile 中的文件转换到 file 里。
            uploudFile.transferTo(file);

        return "success";
    }

10.拦截器

10.1自定义拦截器概述

SpringMVC 也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能。

自定义的拦截器可以实现 HandlerInterceptor 接口,也可以继承 HandlerInterceptorAdapter适配器类

Interceptor:拦截器。 Adapter:适配器。

HandlerInterceptorAdapter 实现了 HandlerInterceptor。但是方法全部都是空方法

拦截器有三个方法:

preHandler()这个方法在业务处理器处理请求之前被调用,对用户的请求request进行处理

        返回false,进行拦截。返回true,放行。

        在生成 ModelAndView 之前执行。

postHandler():这个方法在业务处理器处理完请求后,但是DispatcheServlet向客户端返回响应之前被调用,对用户的请求request进行处理

        在生成 ModelAndView 之后,但在执行ModelAndView之前,执行。

afterCompletion()这个方法在DispatcherServlet完全处理完请求之后被调用,一般用来清理资源

10.1.1拦截器和过滤器的区别

10.1.2简单的拦截器例子:

1.拦截器页面:
@Component
public class FirstInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("First:preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("First:postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("First:afterCompletion");
    }
}

2.配置拦截器(SpringMVC.xml):
<!--配置拦截器-->
<mvc:interceptors>
    <!--1.默认拦截所有请求-->
    <!--这两句作用一模一样,只不过ref方法要求拦截器类上必须要加上@Component-->
    <bean class="com.guolian.Interceptor.FirstInterceptor"></bean>
    <!--<ref bean="firstInterceptor"></ref>-->
</mvc:interceptors>

3.控制层:
@Controller
public class TestInterceptor {
    @RequestMapping("/testInterceptor")
    public String testInterceptor(){

        //ModelAndView
        return "success";
    }
}

4.页面:
<body>
<a href="testInterceptor">测试Interceptor</a>
</body>

10.2设置自定义拦截方式:

<!--配置拦截器-->
<mvc:interceptors>
    <!--设置自定义拦截方式-->
    <mvc:interceptor>
        <!--设置拦截器类-->
        <bean class="com.guolian.Interceptor.FirstInterceptor" </bean>
        <!--设置需要拦截的路径-->
        <mvc:mapping path=""></mvc:mapping>
        <!--设置在拦截路径里需要排除掉的路径-->
        <mvc:exclude-mapping path=""></mvc:exclude-mapping>
    </mvc:interceptor>
</mvc:interceptors>

10.3多个拦截器的执行顺序

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--1.默认拦截所有请求-->
        <bean class="com.guolian.Interceptor.FirstInterceptor"></bean>
        <bean class="com.guolian.Interceptor.SecondInterceptor"></bean>
    </mvc:interceptors>
</beans>

当有多个拦截器时,preHandle()按照拦截器数组(按顺序存储的拦截器)的正序执行

            **postHandle()按照拦截器数组的反序执行**。

            **afterCompletion()按照拦截器数组的反序执行**。

当多个拦截器的preHandle()有不同返回值时

这里的第一个、第二个指的是排除了默认拦截器的排序。

  1. 第一个返回false,第二个返回false。只有第一个拦截器的preHandle()会执行。

  2. 第一个返回true,第二个返回false
    第一、二个(全部)拦截器的preHandle()都会执行,
    但是(全部)postHandler()都不会执行,
    第一个的afterCompletion()会执行(返回false的拦截器之前的拦截器的afterCompletion()都会执行)。

  3. 第一个返回false,第二个返回true。只有第一个拦截器的preHandle()会执行。

11.异常处理

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

11.1DefaultHandlerExceptionResolver

springMVC默认的异常处理器。我们测试程序时,在页面报出的异常信息,就是这个类处理后的结果。

例子:

这就是处理之后的结果。

11.2SimpleMappingExceptionResolver

自定义异常处理

配置:

    <!--配置自定义异常处理类-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!--key:写需要处理的异常-->
                <!--标签里的文本写出现异常时,需要跳转到的页面。使用的是视图解析器-->
                <prop key="java.lang.NullPointerException">error</prop>
            </props>
        </property>
    </bean>
    <!--视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

12.运行流程图:

1.客户端请求发送到web.xml,查看是否匹配DispatchServlet的url-pattern

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

2.如果匹配,则交给 DispatcherServlet 处理。DispatcherServlet 查看是否存在与请求对应的映射关系

@Controller
public class TestException {
    //映射关系
    @RequestMapping(value = "/testException",method = RequestMethod.GET)
    public String testException(){
        String s = null ;
        System.out.println(s.substring(0,5));
        return "success";
    }
}

3.如果不存在对应的映射关系,则查看是否配置了默认的servlet。

如果默认的Servlet不能处理,则页面404。控制台不会报错。

<!--配置默认的Servlet,当DispatcherServlet 处理不了时,交给默认的Servlet处理。-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>

4.如果存在,则由 HandlerMapping 获取 HandlerExecutionChain 对象。然后再获取 HandlerAdapter对象。

HandlerMapping : 存储所有的请求和处理器之间的关系

HandlerExecutionChain:找到某一个指定的请求和处理器、拦截器之间的关系

        通过**HandlerMapping.getHandler(),获得HandlerExecutionChan对象**。

HandlerAdapter:处理器适配器。用来执行当前的Handler(控制层中的方法)

5.当 HandlerAdapter 要调用控制层中的方法时,先经过 拦截器的perHandler(),再生成 ModelAndView对象,再调用 拦截器的 postHandler()。再根据 ModelAndVIew 对象得到实际的 view,跳转到相应的页面最后调用拦截器的afterCompletion()。跳转功能的底层就是:请求转发。

6.如果调用 ModelAndVIew 时出现异常。那么,根据 HandlerExceptionResolver 处理异常,得到新的ModelAndView 对象。

13.Spring整合SpringMVC

SpringMVC所管理的控制层Servlet,是基于 Spring管理的 业务层service 的。

所以,Spring.xml(applicationContext.xml) 的加载应该在 SpringMVC.xml(dispatcher-servlet.xml) 之前。

在servlet生成之前有:过滤器、监听器。

由于过滤器是每请求一次,就生成一次。所以,我们在监听器中加载Spring.xml。

加载顺序:context(容器)—>listener(监听器)—>filter(过滤器)—>servlet

13.1使用监听器模拟整合

为什么是模拟整合?因为,SpringMVC已经为我们自动的整合了Spring。现在只是模拟看一下过程

右键项目,new-Listener

SpringListener.java:

@WebListener()
public class SpringListener implements ServletContextListener,
        HttpSessionListener, HttpSessionAttributeListener {

    // Public constructor is required by servlet spec
    public SpringListener() {
    }

   //在servlet容器初始化时,加载 Spring.xml。并将其对象放入 servletContext域中。
    public void contextInitialized(ServletContextEvent sce) {
      /* This method is called when the servlet context is
         initialized(when the Web application is deployed). 
         You can initialize servlet context related data here.
      */
        ClassPathXmlApplicationContext ac =
                new ClassPathXmlApplicationContext("com/guolian/applicationContext.xml");
        ServletContext servletContext = sce.getServletContext();
        servletContext.setAttribute("ac",ac);
    }
    }

控制层:

如果没有返回值,也就是参数为 void。则ModelAndView的默认值为:RequestMapping 中value的值。

@Controller
public class TestController {
    @RequestMapping(value = "/testListener",method = RequestMethod.GET)
    private String testListener(HttpSession session){
        //获取Spring所管理的teacher对象
        ServletContext servletContext = session.getServletContext();
        ApplicationContext ac = (ApplicationContext)servletContext.getAttribute("ac");
        Teacher teacher = ac.getBean("teacher", Teacher.class);
        System.out.println(teacher);
        return "success";
    }
}

web.xml:

在web.xml中配置监听器

<listener>
    <listener-class>com.guolian.listener.SpringListener</listener-class>
</listener>

13.2正式整合

在web.xml下配置:

在idea中,生成SpringMVC项目就会自动生成。

<!--用来设置上下文参数
    如果不加这个配置,则默认寻找WEB-INF下的applicationContext.xml-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <!--配置spring.xml的加载路径-->
    <param-value>classpath:com/guolian/spring.xml</param-value>
</context-param>
<!--配置监听器-->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--自己配置的监听器,必须要写在默认监听器的后面-->
    <listener>
        <listener-class>com.guolian.listener.SpringListener</listener-class>
    </listener>

SpringLIstener:

public class SpringListener implements ServletContextListener,
        HttpSessionListener, HttpSessionAttributeListener {

    // Public constructor is required by servlet spec
    public SpringListener() {
    }

    // 容器初始化时
    public void contextInitialized(ServletContextEvent sce) {
        //获取默认的监听器自动帮我们依赖注入的容器对象。
        ApplicationContext context = (ApplicationContext) sce.getServletContext()         .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        sce.getServletContext().setAttribute("ac",context);
    }
        }

监听器,可以在ServletContext加载时,通过监听器加载spring的配置文件,创建spring容器。

spring提供的监听器:ContextLoaderListener。

在springMVC只扫描控制层,在spring中,通过包含排除对所扫描的包进行指定。

spring是父容器。 springMVC是子容器

子容器能够调用访问父容器中的bean,而父容器不能调用访问子容器中的bean

springMVC管理:controller。 spring管理:service、dao。