springMVC学习笔记

Spring MVC


胜利的曙光就在眼前!

一、概述

1. 简介

  • 1)spring MVC是基于spring的一个框架,实际上就是spring的一个模块,是做web开发的。(理解为servlet的一个升级)
  • 2)web开发底层是servlet,框架在servlet基础上面加入一些功能,做web开发更方便
  • 3)springMVC就是一个spring,spring是容器,ioc能够管理对象。springMVC能够创建对象,放入容器中(SpringMVC容器),springmvc容器中放的是控制器对象
  • 4)我们要做的就是使用@Controller创建控制器对象,把对象放入springmvc容器中,把创建的对象作为控制器使用,这个控制器对象能接收用户的请求,显示处理结果,就当作一个servlet使用。
    • @Controller注解创建的是一个普通类对象,不是servlet,springmvc赋予了控制器对象一些额外的功能
  • 5)web开发底层是servlet,springmvc中有一个对象是servlet:DispatherServlet(中央调度器)
    • DispatherServlet:负责接收用户的所有请求,用户把请求给了DispatherServlet之后,DispatherServlet把请求转发给Controller对象,最后Controller对象处理请求。

2. 入门

① 创建maven的web项目,加入依赖

  • 1)依赖:

    • <!--servlet-->
      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
      </dependency>
      
      <!--springmvc,间接把spring依赖都加入到项目-->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.3.3</version>
      </dependency>
      

② 注册springmvc框架的核心对象DispatcherServlet

  • 1)声明springmvc核心对象 DispatcherServlet(中央调度器)

    • <!--声明springmvc核心对象DispatcherServlet
              需要在tomcat服务器启动后,创建DispatcherServlet的实例
              为什么?
                  因为DispatcherServlet在创建过程中,会同时创建springmvc容器对象
                  读取springmvc配置文件,把这个配置文件中的对象都创建好,当用户发起请求时就可以直接使用了
      
              servlet会执行初始化方法init()。DispatcherServlet在init()中{
                  //创建容器,读取配置文件
                  WebApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml);
                  //把容器对象放入ServletContext中
                  getServletContext().setAttribute(key, ctx);
              }
      -->
      <servlet>
          <servlet-name>springmvc</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      
          <!--自定义springmvc配置文件的位置-->
          <init-param>
              <!--springmvc的配置文件的位置属性-->
              <param-name>contextConfigLocation</param-name>
              <!--自定义文件的位置-->
              <param-value>classpath:springmvc.xml</param-value>
          </init-param>
      
          <!--在tomcat启动后,就能创建servlet对象
                  <load-on-startup>:表示tocmat启动后创建对象的顺序。整数值,值越小,tomcat创建对象的时间越早
                                      大于等于0的整数,表示tomcat服务器启动时创建
      							 若为负数,表示第一次访问时创建该对象
              -->
          <load-on-startup>1</load-on-startup>
      </servlet>
      
    • springmvc创建容器对象时,读取的配置文件默认是:/WEB-INF/<servlet-name>-servlet.xml。这样不好,我们需要通过<init-param>标签修改。

  • 2)配置映射

    • <servlet-mapping>
          <servlet-name>springmvc</servlet-name>
          <!--
                  使用框架时,url-pattern可以使用两种值
                      1.使用扩展名方式,语法:*.xxx,xxx是自定义扩展名。常用方式*.do  *.action  *.mvc等
                      2.使用"/"
              -->
          <url-pattern>*.do</url-pattern>
      </servlet-mapping>
      

③ 创建一个发起请求的页面index.jsp

  • <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <p>第一个springmvc项目</p>
        <p><a href="some.do">发起some.do请求</a></p>
    </body>
    </html>
    

④ 创建控制器类

  • 1)在类上面加@Controller,创建对象,并放入到springmvc容器

    • /**
       * @Controller:作用是创建处理器对象,对象是放在springmvc容器中的
       *
       * 和spring中的@Service、@Component一样
       *
       * 能处理请求的都是控制器(处理器),MyController能处理请求,叫做后端控制器
       */
      @Controller
      public class MyController {
          ...
      }
      
  • 2)在类中的方法上面加入@RequestMapping注解

    • @Controller
      public class MyController {
          /**
           * 处理用户提交的请求,springmvc中是使用方法来处理的
           * 方法是自定义的,可以有多种返回值,多种参数,方法名称自定义
           */
      
          /**
           * 准备使用doSome()方法处理some.do请求
           * @RequestMapping:请求映射,作用是把一个请求地址和一个方法绑定在一起。一个请求一个方法处理
           *      属性:
           *          1.value:string类型,表示请求uri地址。唯一的。使用时推荐地址以“/”开头
           *      位置:
           *          1.方法上
           *          2.类上
           *      说明:使用@RequestMapping修饰的方法叫做处理器方法或控制器方法
           *              使用@RequestMapping修饰的方法可以处理请求,类似Servlet的doGet,doPost
           *
           *  返回值:ModelAndView,本次请求的处理结果
           *      Model:数据,请求处理完成后,要显示给用户的数据
           *      View:视图,比如jsp等..
           */
          @RequestMapping(value = "/some.do")
          public ModelAndView doSome(){  //doGet() --- service请求处理
              //处理some。do的请求。相当于service调用处理完成了哦
              ModelAndView mv = new ModelAndView();
      
              //添加数据,框架在请求的最后把数据放到request作用域
              //相当于request.setAttribute("msg", "欢迎使用springmvc!");
              mv.addObject("msg", "欢迎使用springmvc!");
              mv.addObject("fun", "执行的是doSome方法");
      
              //指定视图,指定视图的完整路径
              //框架对视图执行的是forward操作,request.getRequestDispather("/shos.jsp").forward(...)
      //        mv.setViewName("/show.jsp");
      //        mv.setViewName("/WEB-INF/view/show.jsp");
      
              //当配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
              //框架会使用视图解析器的前缀 + 逻辑名称 + 后缀 组成完整路径,--字符串连接操作
              mv.setViewName("show");
      
              //返回mv
              return mv;
          }
      }
      
      

⑤ 创建一个作为结果的jsp页面

  • <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Show页面</title>
    </head>
    <body>
        <h3>show.jsp,从request数据获取数据</h3><br />
        <h3>msg数据:${msg}</h3><br>
        <h3>fun数据:${fun}</h3><br>
    </body>
    </html>
    

⑥ 创建springmvc配置文件(spring配置文件一样)

  • 1)声明组件扫描器,指定@Controller所在包

    • <?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 https://www.springframework.org/schema/context/spring-context.xsd">
      
          <!--声明组件扫描器-->
          <context:component-scan base-package="cn.zhangm.controller" />
      </beans>
      
  • 2)声明视图解析器。帮助处理视图的

    • <!--声明springmvc框架的视图解析器,帮助开发人员设置视图文件路径-->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <!--前缀:视图文件的路径-->
          <property name="prefix" value="/WEB-INF/view/" />
          <!--后缀:视图文件的扩展名-->
          <property name="suffix" value=".jsp" />
      </bean>
      

3. springmvc处理过程

①简易版

  • 1)发起some.do请求
  • 2)tomcat接收到(根据web.xml的url-parttern知道*.do的请求给DispatcherServlet)
  • 3)DispatcherServlet(根据springmvc.xml配置文件知道 some.do 对应 doSome()方法)
  • 4)DispatcherServlet把some.do转发给MyController.doSome()方法
  • 5)框架执行doSome()方法,把得到的ModelAndView进行处理,转发到show.jsp

在这里插入图片描述

② 深层版

  • 1)用户发起请求some.do
  • 2)DispatcherServlet接收请求some.do,把请求转发给处理器映射器
    • 处理器映射器:springmvc的对象,框架把实现了HandlerMapping接口的类叫做映射器(多个)
    • 处理器映射器作用
      • 根据请求,从springmvc容器对象中获取处理器对象
      • 框架把找到的处理器对象放到一个处理器执行链(HandlerExecutionChain)的类保存
        • HandlerExecutionChain:类中保存着处理器对象(MyController)、项目中所有的拦截器对象List<HandlerInterceptor>
        • 执行链对象返回给中央调度器
  • 3)DispatcherServlet把(2)中的HandlerExecutionChain中的处理器对象交给了处理器适配器对象(多个)
    • 处理器适配器:springmvc中的对象,需要实现HandlerAdapter接口
    • 处理器适配器作用:执行处理器方法(调用MyController.doSome()得到返回值ModelAndView),并把结果返回给中央调度器
  • 4)DispatcherServlet把(3)中获取的ModelAndView交给视图解析器对象
    • 视图解析器:springmvc的对象,需要实现ViewResoler接口(可以有多个视图解析器)
    • 视图解析器作用:组成视图的完整路径,使用前缀、后缀。并创建View对象
      • View是一个接口,表示视图,在框架中jsp、html不是string表示,而是使用View和它的实现类表示视图
      • InternalResourceView视图类,表示jsp文件,视图解析器会创建它,这个对象里面有url属性,表示视图的路径:url = /WEB-INF/jsp/show.jsp
  • 5)DispatcherServlet把(4)中创建的View对象获取到,调用View类自己的方法,把Model数据方到request作用域。执行视图对象的forward。请求结束。

在这里插入图片描述

4. 源码分析

① tomcat启动,容器创建过程

  • 1)通过load-on-start标签指定的1,在服务器启动时创建DispatcherServlet对象,它的父类继承了HttpServlet,故在被创建时,执行init()方法。在init()方法中创建容器,读取配置文件

    • WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
      this.getServletContext().setAttribute(key, ctx);
      
    • 上面创建容器的作用:创建@Controller注解所在的类的对象,创建MyController对象。这个对象放入springmvc容器,map类型的容器,例如map.put("myController", MyController对象)

② 请求的处理过程

  • 1)执行servlet的service()
    • protected void service(HttpServletRequest request, HttpServletResponse response){...}
    • protected void doService(HttpServletRequest request, HttpServletResponse response){...}
    • DispatcherServlet.doDispatch(request, reponse){ 调用MyController的doSome()方法 }

二、注解开发

1. @RequestMapping定义请求规则

  • 1)功能

    • @RequestMapping注解定义在类上,表名该类的一个模块名称,访问该类中的方法的路径为:test/some.do

    • 
      /**
       * @RequestMapping:
       *      value:所有请求地址的公共部分,叫做模块名称
       *      位置:类的上面
       */
      @Controller
      @RequestMapping("/test")
      public class MyController {
      
          @RequestMapping(value = "/some.do")
          public ModelAndView doSome(){
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg", "欢迎使用springmvc!");
              mv.addObject("fun", "执行的是doSome方法");
              mv.setViewName("show");
              return mv;
          }
      
          @RequestMapping(value = "/other.do")
          public ModelAndView doOther(){
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg", "欢迎使用springmvc!");
              mv.addObject("fun", "执行的是doOther方法");
              mv.setViewName("show");
              return mv;
          }
      }
      
  • 2)属性

    • method:指定url的请求方式,值:
      • RequestMethod.GET
      • RequestMethod.POST

2. 处理器方法参数

处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值

  • HttpServletRequest

  • HttpServletResponse

  • HttpSession

  • 请求中所携带的请求参数

  • 1)逐个参数接收

    • 处理器方法的形参名和请求中的参数名必须一致,同名的请求参数给同名的形参,例如:

    •  /**
           * 框架接收请求参数:
           *      1.使用request对象接收请求参数
           *          String strName = request.getParameter("name");
           *          String strAge = request.getParameter("age");
           *     2.springmvc框架通过中央调度器调用MyController的doSome方法。调用时,按名称对应,把接收的参数赋值给形参
           *          doSome(strName,Integer.valueOf(strAge)),框架会提供类型转换的功能
           *
           * 400状态码:客户端错误,表示提交请求参数过程中,发生了问题
           */
      @RequestMapping(value = "/receiveproperty.do")
      public ModelAndView doSome(String name, Integer age){
          System.out.println("name: " + name);
          ModelAndView mv = new ModelAndView();
          mv.addObject("mname", name);
          mv.addObject("myage", age);
          mv.setViewName("show");
          return mv;
      }
      
    • <form action="receiveproperty.do" method="post">
          姓名:<input type="text" name="name"><br>
          年龄:<input type="text" name="age"><br>
          <input type="submit" value="提交">
      </form>
      
  • 2)post请求中文乱码

    • 配置过滤器,可以使用自定义的,也可以使用框架中提供的过滤器 CharacterEncodingFilter

    • <!--注册声明过滤器:解决post请求中文乱码问题-->
      <filter>
          <filter-name>characterEncodingFilter</filter-name>
          <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
          <!--设置项目中使用的字符编码-->
          <init-param>
              <param-name>encoding</param-name>
              <param-value>utf-8</param-value>
          </init-param>
          <!--强制请求对象(HttpServletRequest)使用encoding编码值-->
          <init-param>
              <param-name>forceRequestEncoding</param-name>
              <param-value>true</param-value>
          </init-param>
          <!--强制应答对象(HttpServletResponse)使用encoding编码值-->
          <init-param>
              <param-name>forceResponseEncoding</param-name>
              <param-value>true</param-value>
          </init-param>
      </filter>
      <filter-mapping>
          <filter-name>characterEncodingFilter</filter-name>
          <!--
           	/*:表示强制所有的请求先通过过滤器处理
          -->
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      
  • 3)校正请求参数名

    • 注解@RequestParam:处理请求参数名和处理器形参名不一样

    • /**
           * 请求参数名和形参名不一致
           * 使用注解@RequestParam:
           *      属性:1.value 请求中的参数名称
           *            2.required:boolean类型,默认true,表示请求中必须含有此参数
           *      位置:处理器方法的形参前
           */
      @RequestMapping(value = "/receiveproperty2.do")
      public ModelAndView doSome2(@RequestParam(value = "rname",required = false) String name,
                                  @RequestParam(value = "rage",required = false)Integer age){
          System.out.println("name: " + name);
          System.out.println("age: " + age);
          ModelAndView mv = new ModelAndView();
          mv.addObject("rname", name);
          mv.addObject("rage", age);
          mv.setViewName("show");
          return mv;
      }
      
    • 属性:

      • value:请求中的参数名称
      • required:boolean类型,默认true,表示请求中必须含有此参数
  • 4)对象参数接收

    • 要求:处理器方法形参是java对象,这个对象的属性名和请求参数中参数名一样,框架会创建形参java对象,给属性赋值,调用set方法

    • @RequestMapping(value = "/receiveproperty3.do")
      public ModelAndView doSome3(Student mystudent){
          System.out.println("name: " + mystudent.getName() + "," + "age:" + mystudent.getAge());
          ModelAndView mv = new ModelAndView();
          mv.addObject("name", mystudent.getName());
          mv.addObject("age", mystudent.getAge());
          mv.addObject("mystudent", mystudent);
          mv.setViewName("show");
          return mv;
      }
      

3. 处理器方法返回值

返回值有四大类

  • ModelAndView
  • String
  • void
  • Object对象

① ModelAndView

  • 1)若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时
    处理器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则处理器方法中
    需要定义 ModelAndView 对象。
  • 2)在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何
    资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多
    余:要么 Model 多余,要么 View 多余。即此时返回 ModelAndView 将不合适。
  • 3)方法
    • mv.addObject(key, value):向request域添加共享数据
    • mv.setViewName(请求转发路径):请求转发

② String

  • 1)处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址

  • 2)若返回值为完整路径,则不能配置视图解析器

  • 3)示例

    • <!--声明springmvc框架的视图解析器,帮助开发人员设置视图文件路径-->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <!--前缀:视图文件的路径-->
          <property name="prefix" value="/WEB-INF/view/" />
          <!--后缀:视图文件的扩展名-->
          <property name="suffix" value=".jsp" />
      </bean>
      
    • /**
      * 处理器方法返回String:表示视图逻辑名称,需要配置视图解析器
      *
      */
      @RequestMapping(value = "return-view.do")
      public String doReturnView(HttpServletRequest request,String name, Integer age){
          System.out.println("name: " + name + "," + "age: " + age);
      
          //可以自己手工添加数据到request域
          request.setAttribute("myname", name);
          request.setAttribute("myage", age);
      
          //show:逻辑视图名称,项目中配置了视图解析器
          return "show";
      }
      

③ void

  • 1)对于处理器方法返回 void 的应用场景,AJAX 响应.

  • 2)ajax的使用步骤

    • ① 导入依赖,加入jquery库

      • <!--jackson-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.0</version>
        </dependency>
        
    • ② 前端发起ajax请求

      • <html>
        <head>
            <title>Title</title>
            <script type="text/javascript" src="js/jquery.min.js"></script>
            <script type="text/javascript">
                $(function () {
                    $("#btn").click(function () {
                        $.ajax({
                            url:"returnVoid-ajax.do",
                            data:{
                                name:"zhangsan",
                                age:20
                            },
                            type: "post",
                            dataType: "json",
                            success:function (data) {
                                //data从服务器端返回的是字符串:{"name":"zhangsan","age":20}
                                //jquery会把json字符串转化为json对象,赋值给data形参
                                alert(data.name + " " + data.age);
                            }
                        });
                    });
                });
        
            </script>
        </head>
        <body>
            <button id="btn">发起ajax请求</button>
        </body>
        </html>
        
    • ③ 后端响应ajax

      • //处理器方法返回void,响应ajax请求
        @RequestMapping(value = "returnVoid-ajax.do")
        public void doReturnViewAjax(HttpServletResponse response, String name, Integer age) throws IOException {
            System.out.println("name: " + name + "," + "age: " + age);
        
            //处理ajax,使用json做数据格式
            //加入service调用完成了,用Student表示处理结果
            Student student = new Student();
            student.setName(name);
            student.setAge(age);
        
            //把结果的对象转为json数据
            String json = "";
            if (student != null) {
                ObjectMapper om = new ObjectMapper();
                json = om.writeValueAsString(student);
                System.out.println("student转换的json字符串:" + json);
            }
        
            //输出数据,响应ajax请求
            response.setContentType("application/json;charset=utf-8");
            PrintWriter pw = response.getWriter();
            pw.write(json);
            pw.flush();
            pw.close();
        }
        

④ Object

(一)步骤与原理

  • 1)实现步骤:

    • 现在做ajax,主要使用json的数据格式。故加入jackson依赖。springmvc默认使用jackson

    • 在springmvc配置文件加入<mvc:annotation-driven>注解驱动。作用:把java对象转为json字符串

      • <!--注解驱动-->
        <mvc:annotation-driven />
        
    • 在处理器方法上面加@ResponseBody注解。作用:输出数据(通过HttpServletResponse输出流)

  • 2)springmvc处理器方法返回Object对象,可以转为json输出到浏览器,响应ajax的内部原理:

    • <mvc:annotation-driven>注解驱动

      • (1) 注解驱动实现的功能是:完成java对象到json、xml、text、二进制等数据格式的转换
      • (2) <mvc:annotation-driven>标签加入到springmvc配置文件后,会自动创建HttpMessageConverter接口的7个实现类。包括:MappingJackson2HttpMessageConverter(使用jackson工具库中的ObjectMapper实现java对象转换为json字符串的)
      • (3) HttpMessageConverter接口:消息转换器。功能:定义了java对象转为json、xml等数据格式的方法。
        • 这个接口有很多实现类,这些实现类完成java到json、xml等数据格式的转换。
        • 若不加注解驱动,只有四个实现类;加上驱动有8个实现类
      • (4) 下面两个方法是控制器类把结果输出给浏览器时使用的:
        • boolean canRead(Class<?> var1, @Nullable MediaType var2);
        • void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
        • canRead作用:检查处理器方法的返回值,能不能转化为var2表示的数据格式,能返回为true
        • write:把处理器方法的返回值对象,调用jackson中的ObjectMapper转化为json字符串
    • @ResponseBody注解

      • 作用:放在处理器方法上,通过HttpServletResponse输出数据,响应ajax请求

      • 做的事情:

      • PrintWriter pw = response.getWriter();
        pw.write(json);
        pw.flush();
        pw.close();
        

(二)代码示例

  • 1)处理器方法返回Student,通过框架转为json,响应ajax请求

    • /**
      * @ResponseBody:
      *      作用:把处理器方法返回对象转为json后,通过HttpServletResponse输出给浏览器
      *      位置:方法上。和其他注解没有顺序要求
      *
      * 返回对象处理流程
      *      1.框架会把返回值Student类型,调用ArrayList<HttpMessageConverter>中的每个类的canWrite()方法,检查HttpMessageConverter接口的实现类哪个能处理Student数据的类型,(MappingJackson2HttpMessageConverter可以)
      *      2.框架会调用实现类的write(),MappingJackson2HttpMessageConverter的write()方法,把student对象转为json,调用的是Jackson的ObjectMapper实现转为json。类型是:contextType:application/json;charset=utf-8
      *      3.框架会调用@ResponseBody把(2)的结果数据输出到浏览器,ajax请求完成
      */
      @RequestMapping(value="/returnStudentJson.do")
      @ResponseBody
      public Student doStudentJsonObject(String name, Integer age){
          //调用service获取请求结果数据,假如调用方法完成了
          Student student = new Student();
          student.setName(name);
          student.setAge(age);
      
          //student对象会被框架转为json
          return student;
      }
      
  • 2)处理器方法返回List集合,例如:list<Student>

    • /**
      * 处理器方法返回List集合,例如:list<Student >
      *
      * 处理方式与返回Student一样
      */
      @RequestMapping(value="/returnStudentJsonArray.do")
      @ResponseBody
      public List<Student> doStudentJsonObjectArray(String name, Integer age){
          //调用service获取请求结果数据,假如调用方法完成了
          List<Student> list = new ArrayList<>();
          Student student = new Student();
          student.setName(name);
          student.setAge(age);
      
          Student student2 = new Student();
          student2.setName("花花");
          student2.setAge(15);
      
          list.add(student);
          list.add(student2);
      
          //list对象会被框架转为json数组:[{},{}]
          return list;
      }
      
  • 3)返回字符串String对象

    • 处理器方法返回String:String表示数据,不是视图。

    • 如何区分返回值类型String是数据还是视图?

      • 答:看是否有注解@ResponseBody
      • 有该注解:数据
      • 没有该注解:视图
    • 默认使用“text/plain;charset=ISO-8859-1”作为contextType,导致中文乱码

      • 解决方案:给@RequestMapping增添一个属性produces,指定contextType
    • 示例:

      • /**
        * 返回对象处理流程
        *      1.框架会把返回值String类型,调用ArrayList<HttpMessageConverter>中的每个类的canWrite()方法,检查HttpMessageConverter接口的实现类能处理String数据的类型,(StringHttpMessageConverter可以)
        *      2.框架会调用实现类的write(),StringHttpMessageConverter的write()方法,把字符串按照指定编码处理:text/plain;charset=ISO-8859-1(默认)
        *      3.框架会调用@ResponseBody把(2)的结果数据输出到浏览器,ajax请求完成
        */
        @RequestMapping(value="/returnStringData.do", produces = "text/plain;charset=utf-8")
        @ResponseBody
        public String doStringData(String name, Integer age){
            return "hello springmvc 返回Sring对象,表示数据";
        }
        

4. 解读url-pattern

① 分析请求

  • 1)各种请求

    • http://localhost:8080/springmvc05_url_pattern/index.jsp:tomcat处理
    • http://localhost:8080/springmvc05_url_pattern/js/jquery.min.js:tomcat处理
    • http://localhost:8080/springmvc05_url_pattern/images/1.jpg:tomcat处理
    • http://localhost:8080/springmvc05_url_pattern/some.do:DispatcherServlet处理
      • 因为配置了<url-pattern>*.do</url-pattern>
    • <http://localhost:8080/springmvc05_url_pattern/html/test.html>:tomcat处理
  • 2)tomcat本身能处理静态资源的访问,像html、图片、jsp文件都是静态资源

    • toncat的web.xml文件配置了servlet

    • <!-- The default servlet for all web applications, that serves static     -->
      <!-- resources.  It processes all requests that are not mapped to other   -->
      <!-- servlets with servlet mappings (defined either here or in your own   -->
      <!-- web.xml file).     -->
      <servlet>
          <servlet-name>default</servlet-name>
          <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
          <init-param>
              <param-name>debug</param-name>
              <param-value>0</param-value>
          </init-param>
          <init-param>
              <param-name>listings</param-name>
              <param-value>false</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
      </servlet>
      
      <servlet-mapping>
          <servlet-name>default</servlet-name>
      	<!--/表示静态资源和未映射的请求都由defaultServlet处理-->    
          <url-pattern>/</url-pattern>
      </servlet-mapping>
      
    • defaultServlet的作用(参见上述注释):

      • 处理静态资源
      • 处理未映射到其他servlet的请求
  • 3)当中央调度器的url-pattern配置为/时,会出现静态资源无法访问的问题。

    • <servlet>
              <servlet-name>springmvc</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <init-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>classpath:springmvc.xml</param-value>
              </init-param>
              <load-on-startup>1</load-on-startup>
          </servlet>
          
          <servlet-mapping>
              <servlet-name>springmvc</servlet-name>
              <url-pattern>/</url-pattern>
          </servlet-mapping>
      
    • 因为,它会替代tomcat的defaultServlet的映射,导致所有静态资源都给中央调度器处理。默认情况下中央调度器没有处理静态资源的能力,没有控制器对象能够处理静态资源的访问。所以静态资源(html、图片、jsp…)都是404

    • 动态资源可以访问,因为有MyContriller控制器对象

  • 4)如何处理这个问题,请往下看

② < mvc:default-servlet-handler>

  • 1)第一个处理静态资源的方式:

    • 在springmvc配置文件加入:<mvc:default-servlet-handler>标签
    • 原理:在加入该标签,框架会创建控制器对象:DefaultServletHttpRequestHandler。这个对象可以把接收到的请求转发给tomcat的default这个servlet处理。
  • 2)注意:default-servlet-handler@RequestMapping有冲突,需要加入annotation-driven解决问题

    • default-servlet-handler标签将所有的请求交给tomcat默认的servlet处理了,而我们配置@RequestMapping注解是期望该注解的方法交给中央调度器处理
  • 3)示例

    • <!--default-servlet-handler和@RequestMapping有冲突,需要加入annotation-driven解决问题-->
      <mvc:annotation-driven />
      
      <mvc:default-servlet-handler />
      

③ <mvc:resources>

  • 1)第二种处理静态资源的方式(常用)

    • 在springmvc配置文件中加入:<mvc:resources mapping="" location="" />标签
    • 原理:mvc:resources加入后,会创建一个ResourceHttpRequestHandler处理器对象,让这个对象处理静态资源的访问,不依赖tomcat服务器。
    • mapping:访问静态资源的uri地址,使用通配符 **
      • 例如:/images/**:表示images目录下的任意字符,如,images/1.jpg、images/user/logo.jpg
    • location:静态资源在项目中的位置
  • 2)示例

    • <mvc:resources mapping="/images/**" location="/images/" />
      <mvc:resources mapping="/html/**" location="/html/" />
      <mvc:resources mapping="/js/**" location="/js/" />
      
      <!--mvc:resources和@RequestMapping有一定的冲突-->
      <mvc:annotation-driven />
      
  • 3)开发常用

    • 在开发中通常将静态资源放在一个static目录下,这样只需要一条语句就可以指定静态资源了

    • <!--mvc:resources和@RequestMapping有一定的冲突-->
      <mvc:annotation-driven />
      
      <!--使用一条语句指定多个静态资源的访问-->
      <mvc:resources mapping="/static/**" location="/static/" />
      

三、路径问题

1. 路径分类

  • 1)绝对地址:带有协议名称的地址
    • 如:http://www.baidu.comftp://202.122.23.1
  • 2)相对地址:没有协议开头的地址
    • 如:user/some.do/user/some.do
    • 相对地址不能独立使用,必须由一个参考地址。通过参考地址+相对地址本身才能指定资源
  • 3)参考地址

2. 加不加 / 问题

① 在页面中的访问地址不加 “/” 情况一

  • 访问的是:http://localhost:8080/ch06-path/index.jsp
  • 路径:http://localhost:8080/ch06-path/
  • 资源:index.jsp
  • 在index.jsp发起user/some.do的请求时,访问地址变成:http://localhost:8080/ch06-path/user/some.do
  • 当地址没有斜杠开头,例如user/some.do,当点击连接时,访问地址是当前页面的地址 + 连接地址
    • 例如:http://localhost:8080/ch06-path/ + user/some.do

② 在页面中的访问地址不加 “/” 情况二

  • 在index.jsp 访问 user/some.do,返回后现在的地址http://localhost:8080/ch06-path/user/some.do

  • 路径:http://localhost:8080/ch06-path/user/

  • 资源:some.do

  • 再一次在index.jsp访问user/some.do后,地址变为http://localhost:8080/ch06-path/user/user/some.do

  • 如何解决这个问题:

    • 1-加入${pageContext.request.contextPath}El表达式

    • 2-加入一个base标签,是html语言中的标签。表示当前页面访问地址的基地址。页面中所有没有"/"开头的地址,都是以base标签中的地址为参考地址

    • 例如:

    • <html>
          <head>
              <title></title>
              <!--在这里-->
              <base href="http://localohos:8080/ch06-path/" />
          </head>
          <body>
              <!--
      			请求地址为:
      			http://localohos:8080/ch06-path/ + user/some.do
      		-->
              <p><a href="user/some.do">发起user/some.do请求</a></p>
          </body>
      </html>
      
    • jsp中可以动态获取

      • <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <%
        String basePath = request.getScheme() + "://" +
        request.getServerName() + ":" + request.getServerPort() +
        request.getContextPath() + "/";
        %>
        <html>
            <head>
                <title></title>
                <!--在这里-->
                <base href="<%=basePath%>" />
            </head>
            <body>
                <p><a href="user/some.do">发起user/some.do请求</a></p>
            </body>
        </html>
        

③ 在页面中的访问地址不加"/"

  • 访问的是:http://localhost:8080/ch06-path/index.jsp
  • 路径:http://localhost:8080/ch06-path/
  • 资源:index.jsp
  • 点击/user/some.do,访问地址变为:http://localhost:8080/user/some.do
  • 参考地址是:你的服务器地址 http://localhost:8080/
  • 如果想要正确访问资源:在页面中的访问地址添加上项目的虚拟目录即可,如:/ch06-path/user/some.do

④ 总结

  • 页面中的访问地址不加"/":参考地址为当前页面(资源)的路径
  • 页面中的访问地址加"/":参考地址为服务器地址

四、SSM整合

五、核心技术

1. 请求转发和重定向

  • 1)请求转发

    • 语法:setViewName("forward:视图文件完整路径");

    • forward特点:不和视图解析器一起使用,就当项目没有视图解析器

    • 示例:

    • @RequestMapping("/doForward.do")
      public ModelAndView doForward(){
          ModelAndView mv = new ModelAndView();
          mv.addObject("msg", "springmvc方法返回值ModelAndView实现forward");
          //显示转发
          mv.setViewName("forward:/WEB-INF/jsp/result.jsp");
          return mv;
      }
      
  • 2)重定向

    • 语法:setViewName("redirect:视图文件完整路径");

    • redirect特点:

      • 不和视图解析器一起使用,就当项目没有视图解析器
      • 不能访问WEB-INF下的资源
      • 框架会把Model的简单类型数据,转为String使用,作为重定向页面的get请求参数
    • 示例:

    • /**
      * 框架对重定向的操作:
      * 1.框架会把Model的简单类型数据,转为String使用,作为result.jsp的get请求参数
      * 目的是在doRedirect.do和result.jsp两次请求直接可以传递数据
      * 2.在result.jsp页面可以使用参数集合对象${param}获取请求参数值:${param.name}
      */
      @RequestMapping("/doRedirect.do")
      public ModelAndView doRedirect(String name, Integer age){
          ModelAndView mv = new ModelAndView();
          //数据放入request作用域
          mv.addObject("name", name);
          mv.addObject("age", age);
          //重定向:重定向不能访问WEB-INF下的资源
          mv.setViewName("redirect:/result.jsp");
          //http://localhost:8080/springmvc07_forward_redirect/result.jsp?name=lisi&age=18
          return mv;
      }
      

2. 异常处理

  • 1)异常处理:springmvc采用统一的、全局的异常处理。
    • 把controller中所有异常处理都集中到一个地方。采用的是aop思想,把业务逻辑和异常处理分开,解耦合
  • 2)两个注解
    • @ExceptionHandler
    • @ControllerAdvice

异常处理步骤

  • 1)新建maven项目、导入依赖

  • 2)在controller中抛出异常

    • @RequestMapping("/some.do")
      public ModelAndView doSome(String name, Integer age) throws MyUserException {
          ModelAndView mv = new ModelAndView();
      
          //根据请求参数抛出异常
          if (!"zs".equals(name)) {
              throw new NameException("姓名不正确");
          }
          if (age == null || age > 80) {
              throw new AgeException("年龄比较大");
          }
      
          mv.addObject("name", name);
          mv.addObject("age", age);
          mv.setViewName("result");
          return mv;
      }
      
  • 3)创建一个普通类,作为全局异常处理

    • 在类上加注解@ControllerAdvice

    • 在类中定义方法,方法上加@ExcpetionHandler

    • /**
       * @ControllerAdvice:控制器增强(也就是说给控制器类增强功能 --  异常处理功能)
       *      位置:类上
       *      特点:必须让框架直到这个注解所在包名,需要在springmvc配置文件声明组件扫描器
       */
      @ControllerAdvice
      public class GolbalExceptionHandler {
          //定义方法,处理发生的异常
          /**
           * 处理异常的方法和控制器方法的定义一样,可以有多个参数,可以有ModelAndView...
           *
           *  形参:Exception,表示Controller中抛出的异常对象
           *  通过形参可以获取发生的异常信息
           *
           * @ExceptionHandler(异常的class):表示异常的类型,当发生次异常时,有本方法处理
           */
          @ExceptionHandler(value = NameException.class)
          public ModelAndView doNameException(Exception exception){
              //处理nameException的异常
              /**
               * 异常发生处理逻辑:
               *  1.把异常记录下来:记录在数据库或日志文件
               *      记录异常发生的时间,哪个方法,异常的错误内容
               *  2.发送通知,把异常的信息通过邮件、短信微信发送给相关人员
               *  3.给用户友好提示
               */
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg", "姓名必须是zs,其他用户不能访问");
              mv.addObject("ex", exception);
              mv.setViewName("nameError");
              return mv;
          }
      
          //处理ageException
          @ExceptionHandler(value = AgeException.class)
          public ModelAndView doAgeException(Exception exception){
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg", "你的年龄不能大于80");
              mv.addObject("ex", exception);
              mv.setViewName("ageError");
              return mv;
          }
      
          //处理其他异常:NameException、AgeException以外的不知类型的异常
          @ExceptionHandler
          public ModelAndView doOtherException(Exception exception){
              //处理其他异常
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg", "未知异常");
              mv.addObject("ex", exception);
              mv.setViewName("defaultError");
              return mv;
          }
      }
      
      
  • 4)创建处理异常的视图页面

    • <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>NameError</title>
      </head>
      <body>
          nameError.jsp <br/>
          提示信息:${msg} <br/>
          系统消息:${ex.message} <br/>
      </body>
      </html>
      
  • 5)创建springmvc配置文件

    • 组件扫描器,扫描@Controller注解
    • 组件扫描器,扫描@ControllerAdvice
    • 声明注解驱动

3. 拦截器

① 简介

  • 1)拦截器:拦截器是springmvc中的一种,需要实现HandlerInterceptor接口
    • 拦截器和过滤器类似,功能方向侧重点不同
    • 过滤器:用来过滤请求参数,设置字符集编码等
    • 拦截器:拦截用户的请求,对请求做判断处理
  • 2)拦截器特点
    • 拦截器是全局的,可以对多个controller做拦截
    • 一个项目中可以有多个拦截器,他们在一起拦截用户的请求
    • 常用在用户的登录处理、权限检查、记录日志
  • 3)拦截器的执行时间
    • 在请求处理之前,也就是controller类中的方法执行之前先被拦截
    • 在控制器方法执行之后,也会执行拦截器
    • 在请求处理完成后,也会执行拦截器

② 实现步骤

  • 1)定义类实现HandlerInterceptor接口

    • //拦截器类,拦截用户的请求
      public class MyInterceptor implements HandlerInterceptor {
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
              System.out.println("拦截器MyInterceptor的preHandle()方法");
              return true;
          }
      
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("拦截器MyInterceptor的postHandle()方法");
          }
      
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("拦截器MyInterceptor的afterCompletion()方法");
          }
      }
      
      
  • 2)在springmvc配置文件中,声明拦截器

    •  <!--声明拦截器-->
      <mvc:interceptors>
          <mvc:interceptor>
              <!--指定拦截的请求uri地址
                      path:就是uri地址,可以使用通配符 **(任意字符,多级目录和文件)
                  -->
              <mvc:mapping path="/user/**"/>
              <!--声明拦截器对象-->
              <bean class="cn.zhangm.handler.MyInterceptor" />
          </mvc:interceptor>
      </mvc:interceptors>
      

③ 拦截器的三个方法

  • 1) preHandle()方法

    • @Override
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
          System.out.println("拦截器MyInterceptor的preHandle()方法");
          return true;
      }
      
    • 参数:

      • Object handler:被拦截的控制器对象
    • 返回值boolean

      • true:通过了拦截器的验证,可以执行处理器方法
      • false:请求没有通过拦截器的验证,请求没被处理
    • 特点

      • 该方法在控制器方法(MyController的方法)之前先执行的
      • 在这个方法中,可以获取请求的信息,验证请求是否符合要求。
  • 2)postHandle()方法

    • @Override
      public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
          System.out.println("拦截器MyInterceptor的postHandle()方法");
      }
      
    • 参数:

      • Object handler:被拦截的控制器对象
      • ModelAndView modelAndView:处理器方法的返回值
    • 特点

      • 在处理器方法执行之后执行
      • 能够获取到处理器方法的返回值ModelAndView,可以修改其数据和视图,可以影响最后执行结果
      • 主要是对原来的执行结果做二次修正
  • 3)postHandle()方法

    • @Override
      public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
          System.out.println("拦截器MyInterceptor的afterCompletion()方法");
      }
      
    • 参数:

      • Object handler:被拦截的控制器对象
      • Exception ex:程序中发生的异常
    • 特点

      • 请求处理完成后执行。框架中规则当视图处理完成后(对视图执行了forward),就认为请求完成了
      • 一般做资源回收,程序在请求过程中创建的对象,可以在这里清除

④ 多个拦截器执行顺序

  • 1)多个拦截器在框架中保存在一个ArrayList集合,按照声明的先后顺序放入ArrayList

    • <mvc:interceptors>
          <!--11111拦截器-->
          <mvc:interceptor>
              <mvc:mapping path="/**"/>
              <bean class="cn.zhangm.handler.MyInterceptor" />
          </mvc:interceptor>
          <!--22222拦截器-->
          <mvc:interceptor>
              <mvc:mapping path="/**"/>
              <bean class="cn.zhangm.handler.MyInterceptor2" />
          </mvc:interceptor>
      </mvc:interceptors>
      
  • 2)执行顺序(拦截器1:返回值true;拦截器2:返回值true)

    • 11111-拦截器MyInterceptor的preHandle()方法
      22222-拦截器MyInterceptor的preHandle()方法
      =====执行MyController的doSome()方法=====
      22222-拦截器MyInterceptor的postHandle()方法
      11111-拦截器MyInterceptor的postHandle()方法
      22222-拦截器MyInterceptor的afterCompletion()方法
      11111-拦截器MyInterceptor的afterCompletion()方法
      
    • 如图:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S9rOQyFj-1614520386699)(images\多个拦截器.jpg)]

  • 3)执行顺序(拦截器1:返回值true;拦截器2:返回值false)

    • 11111-拦截器MyInterceptor的preHandle()方法
      22222-拦截器MyInterceptor的preHandle()方法
      11111-拦截器MyInterceptor的afterCompletion()方法
      

④ 拦截器和过滤器的区别

  • 1)过滤器是servlet中的对象;拦截器是框架中的对象

  • 2)过滤器是实现Filter接口的对象;拦截器是实现HandlerInterceptor接口的对象

  • 3)过滤器是用来设置resquest、response参数、属性的,侧重数据过滤;拦截器是用来验证请求的,能截断请求

  • 4)过滤器是在拦截器之前先执行

  • 5)过滤器是tomcat服务器创建的对象;拦截器是springmvc容器创建的对象

  • 6)过滤器只有一个执行时间点;拦截器有三个执行时间点
    st

    • <mvc:interceptors>
          <!--11111拦截器-->
          <mvc:interceptor>
              <mvc:mapping path="/**"/>
              <bean class="cn.zhangm.handler.MyInterceptor" />
          </mvc:interceptor>
          <!--22222拦截器-->
          <mvc:interceptor>
              <mvc:mapping path="/**"/>
              <bean class="cn.zhangm.handler.MyInterceptor2" />
          </mvc:interceptor>
      </mvc:interceptors>
      
  • 2)执行顺序(拦截器1:返回值true;拦截器2:返回值true)

    • 11111-拦截器MyInterceptor的preHandle()方法
      22222-拦截器MyInterceptor的preHandle()方法
      =====执行MyController的doSome()方法=====
      22222-拦截器MyInterceptor的postHandle()方法
      11111-拦截器MyInterceptor的postHandle()方法
      22222-拦截器MyInterceptor的afterCompletion()方法
      11111-拦截器MyInterceptor的afterCompletion()方法
      
    • 如图:在这里插入图片描述

  • 3)执行顺序(拦截器1:返回值true;拦截器2:返回值false)

    • 11111-拦截器MyInterceptor的preHandle()方法
      22222-拦截器MyInterceptor的preHandle()方法
      11111-拦截器MyInterceptor的afterCompletion()方法
      

④ 拦截器和过滤器的区别

  • 1)过滤器是servlet中的对象;拦截器是框架中的对象
  • 2)过滤器是实现Filter接口的对象;拦截器是实现HandlerInterceptor接口的对象
  • 3)过滤器是用来设置resquest、response参数、属性的,侧重数据过滤;拦截器是用来验证请求的,能截断请求
  • 4)过滤器是在拦截器之前先执行
  • 5)过滤器是tomcat服务器创建的对象;拦截器是springmvc容器创建的对象
  • 6)过滤器只有一个执行时间点;拦截器有三个执行时间点
  • 7)过滤器可以处理jsp、js、html等;拦截器是侧重拦截controller的对象,如果请求不能被DispatcherServlet接收,这个请求不会执行拦截器的内容
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值