栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringMVC学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringMVC学习笔记

SpringMVC学习笔记 1.SpringMVC概述 1.1SpringMVC简介

在MVC三层架构中:

  1. 界面层:SpringMVC,用于接收用户请求,显示处理结果
  2. 业务层:Spring,用于处理各种业务,创建service、dao和工具类等对象
  3. 持久层:MyBatis,用于访问数据库,对数据进行增删改查

SpringMVC也叫Spring web mvc。是Spring框架的一部分,是在Spring3.0之后发布的,专门用于web开发。

  1. SpringMVC就是servelt的一个升级版

    • web开发底层就是servlet
    • SpringMVC框架就是在servlet基础上,加入一些功能,使得web开发更为简便
  2. SpringMVC是Spring的一部分,能使用Spring的基本功能

    • Spring可以使用IoC和AOP

      • 可以使用IoC管理对象:通过和@Component,@Service,@Repository,@Controller来创建对象 ,并将创建的对象放入Spirng容器中
      • 可以使用AOP对指定方法织入切面
    • SpringMVC也可以使用IoC和AOP

      • 可以使用IoC管理处理器对象:使用@Controller注解或来创建处理器对象,并将处理器对象放入SpringMVC容器中
      • 可以使用AOP赋予处理器对象额外的功能
      • SpirngMVC容器中放的是处理器对象,用于处理用户请求

使用@Controller注解创建的对象本质上还是普通类的对象,不是servlet。只不过被SpringMVC赋予了一些额外的功能,使得可以接收用户的请求,显示处理结果,可以被当作是一个servlet来使用。

那么SpringMVC是怎么实现让这些普通类对象也能接收用户请求的呢?

SpringMVC中有一个核心servlet:DispatcherServlet【中央调度器】

  1. 中央调度器负责接收所有的用户请求,然后将接收到的请求转发给SpringMVC容器中处理相应请求的处理器对象
  2. 处理器对象处理完请求后,将处理结果返回给中央调度器
  3. 由中央调度器将请求结果写入相应响应协议包中

1.2SpringMVC的优点
  1. 基于MVC架构:功能分工明确,耦合度低
  2. 上手快,使用简单:只使用一个注解就可以开发SpringMVC项目,是轻量级的,jar很小,不依赖特定的接口和类。
  3. 兼具Spring的功能:作为Spring的一部分,可以使用IoC和AOP,方便整合MyBatis等其他框架
  4. SpringMVC强化注解的使用:在控制器,service,dao中都可以使用注解,方便灵活。
    1. 使用@Controller创建处理器对象
    2. 使用@Service创建业务对象
    3. 使用@Autowired或@Resource在处理器类中自动注入Service,在Service类中注入Dao
1.3SpringMVC实例入门

需求:用户在页面通过一个链接发起一个请求,在页面上显示处理结果。

1.3.1步骤分析

1.新建web类型的maven项目

2.添加依赖

​ 1)添加SpringMVC依赖:spring-webmvc

​ 2)添加servlet依赖:java.servlet-api

3.注册中央调度器【重点】

在web.xml中注册SpringMVC框架的核心对象:DispatcherServlet【中央调度器】

​ 1)DispatcherServlet叫做中央调度器,是一个servlet,其父类继承HttpServlet。

​ 2)DispatcherServelt又叫做前端控制器(front controller)。DispatcherServlet负责接收所有用户提交的请求,分配给其他处理器对象,同时接收处理器对象返回的处理结果,最后将处理结果显示给用户

4.创建发起请求页面:index.jsp

5.使用注解创建处理器类

​ 1)在java类上加入@Controller注解,可以创建处理器对象,并放入到SpringMVC容器中

​ 2)在java类中的方法上面加入@RequestMapping注解,用于接收特定请求

@Controller注解修饰的类叫处理器(控制器),又叫做后端控制器(back controller)

@RequestMapping注解修饰的方法叫处理方法,用于处理用户请求

6.创建响应页面:result.jsp

7.创建SpringMVC的配置文件(同Spring配置文件)

​ 1)声明组件扫描器,指定@Controller注解所在类的包名

​ 2)声明视图解析器,帮助处理视图

1.3.2新建web项目

新建一个web类型的maven项目:01-hello-springmvc,使用模板:maven-archetype-webapp

补全maven项目结构

1.3.3添加依赖

在pom.xml文件中添加SpringMVC和Servlet依赖。

SpringMVC基于Spring框架,加入SpringMVC依赖后,会自动加载Spring相关jar包

pom.xml




  4.0.0

  com.tsccg
  01-hello-springmvc
  1.0-SNAPSHOT
  war

  
    UTF-8
    1.8
    1.8
  

  
    
    
      javax.servlet
      javax.servlet-api
      3.1.0
      provided
    
    
    
      org.springframework
      spring-webmvc
      5.2.5.RELEASE
    
  

1.3.4注册中央调度器【重点】

在web.xml中注册SpringMVC框架的核心对象:DispatcherServlet【中央调度器】

​ 1)DispatcherServlet叫做中央调度器,是一个servlet,其父类继承HttpServlet

​ 2)DispatcherServlet又叫做前端控制器(front controller),负责接收所有用户提交的请求,分配给其他处理器对象,同时接收处理器对象返回的处理结果,最后将处理结果显示给用户

1)注册DispatcherServlet





    springmvc
    org.springframework.web.servlet.DispatcherServlet

2)设置为开启服务器时创建

我们需要在开启服务器时,就创建一个DispatcherServelt对象。为什么呢?

因为当DispatcherServlet对象创建时,会同时创建一个SpringMVC容器对象,并把容器对象放入全局作用域中。

将其设置为开启服务器时创建:

1

3)重新指定读取配置文件的路径

开启服务器:

发现报异常,读取不到/WEB-INF/springmvc-servlet.xml,也就是SpringMVC配置文件

SpringMVC创建容器对象时,读取的配置文件默认路径为:

/WEB-INF/标签里的值 + -servelt.xml

我们可以重新指定配置文件的读取路径:


    
    contextConfigLocation
    
    classpath:springmvc.xml

同时,在resources目录下新建一个SpringMVC配置文件springmvc.xml,就可以成功创建DispatcherServlet对象了。

springmvc.xml:




4)

此外,还需要设置

其中,url-pattern可以使用两种值:

  1. 使用拓展名方式,语法:*.xxxxxx是自定义的拓展名,表示当发送过来的请求是以xxx为结尾时,使用该servelt来处理。常用的拓展名:*.do、*.action、*.mvc等
  2. 使用斜杠"/",处理所有请求(不包含jsp请求);使用 @RequestMapping(value = "/some.do") public ModelAndView doSome(){ ModelAndView mv = new ModelAndView(); //Model:添加数据,框架在请求的最后把数据放入到请求作用域对象中 //request.setAttribute("fun","执行的是doSome方法"); mv.addObject("fun","执行的是doSome方法"); mv.addObject("msg","Hello SpringMVC!"); //View:指定视图,value属性指定视图的路径 //框架对视图执行的是请求转发操作:request.getRequestDispatcher("/result.jsp").forword(req,res); mv.setViewName("/result.jsp"); //返回处理结果 return mv; } } 1.3.7创建响应页面:result.jsp

    result.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
    
    
    

    从请求作用域中获取数据

    ${requestScope.msg}

    ${requestScope.fun}

    1.3.8声明组件扫描器

    在SpringMVC配置文件里声明组件扫描器,指定@Controller注解所修饰的类所在的包

    springmvc.xml

    
    
        
        
    
    
    1.3.9开启服务器

    发布网站,设置网站别名为:MyWeb

    开启服务器:

    1.4分析SpringMVC处理请求流程
    1. 通过浏览器页面发起请求some.do给tomcat服务器
    2. tomcat读取web.xml创建DispatcherServlet对象,由于其url-pattern的值为*.do,所以将所有.do结尾的请求交给其处理。
    3. DispatcherServlet对象被创建时,其init()方法创建容器对象,读取SpringMVC配置文件,根据其中的组件扫描器创建处理器对象,并放入容器中,然后将容器对象放入全局作用域中。
    4. DispatcherServlet对象根据SpringMVC配置文件得知处理器处理特定请求的方法:(some.do–>doSome()),于是将some.do的请求分派给MyController.doSome()方法
    5. 框架执行doSome()方法,把得到的处理结果(ModelAndView mv)返回给DispatcherServlet对象,DispatcherServlet对象根据View所指定的视图(result.jsp),请求转发调用。

    1.5视图解析器

    当开启服务器后,如果在浏览器里直接访问result.jsp,显示的结果是不正确的,也是非法的。

    为了保护静态资源文件,我们可以将静态资源文件放到WEB-INF目录下,该目录中的文件不会被非法访问到。

    在WEB-INF目录下新建一个文件夹view,将result.jsp移进去

    当然,我们也必须在处理方法中重新指定视图所在路径才能让程序正常执行

    我们一般在/WEB-INF/view/目录下存放所有视图文件,当在处理方法中指定视图路径时,一般都为:

    /WEB-INF/view/one.jsp
    /WEB-INF/view/two.jsp
    /WEB-INF/view/xxx.jsp
    

    可以看出,路径的前缀和后缀是重复的

    • 前缀:/WEB-INF/view/
    • 后缀:.jsp

    为了方便设置视图路径,我们可以将前缀和后缀提取出来,作为公用模板,使用时只需要写视图文件的名称即可。

    如何实现呢?可以使用视图解析器:InternalResourceViewResolver来提取视图路径的前缀和后缀

    使用方式:

    在SpringMVC配置文件springmvc.xml里声明视图解析器,输入前缀和后缀

    
        
        
        
        
    
    

    当配置了视图解析器后,就可以使用逻辑名称(文件名)来指定视图了。

    框架会使用字符连接操作,将视图解析器的前缀 + 逻辑名称 + 视图解析器的后缀连接起来,组成完整路径。

    2.SpringMVC注解式开发 2.1@RequestMapping定义请求规则 2.1.1处理多种请求 1.使用一个处理方法处理多种请求

    可以在一个处理方法上指定处理多种请求。

    指定方式:在@RequestMapping注解中使用数组

    @RequestMapping(value={"请求1","请求2"})

    @Controller
    public class MyController {
        //指定处理some.do和other.do请求
    	@RequestMapping({value="/some.do","/other.do"})
        public ModelAndView doSome(){
            ModelAndView mv = new ModelAndView();
            mv.addObject("fun","执行的是doSome方法");
            mv.addObject("msg","Hello SpringMVC!");
            //使用视图解析器后,只需要输入视图的逻辑名称
            mv.setViewName("result");
            //返回处理结果
            return mv;
        }
    }
    

    在index.jsp文件里添加一个other.do的请求

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
        
            * {
                font-size: 20px;
            }
        
    
    
    
    发送some.do请求
    发送other.do请求

    打开服务器:

    2.使用多个处理方法处理多种请求

    当只使用Servlet时,一个Servelt接口实现类只能处理一个请求,实现增删查改就需要写四个Servlet类。

    而在SpringMVC中,一个处理方法处理一个请求,在一个控制器类中,使用四个处理方法就能实现增删查改。

    演示使用多个处理方法处理多种请求:

    1.在index.jsp中添加两个不同的请求链接:

    http://localhost:8080/MyWeb/add.do

    http://localhost:8080/MyWeb/remove.do

    2.创建响应页面

    在/WEB-INF/view/目录下新建add.jsp和remove.jsp

    add.jsp:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
    
    
    
    /WEB-INF/view/add.jsp从请求作用域中获取数据 ${requestScope.msg} ${requestScope.fun}

    remove.jsp:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
    
    
    
    /WEB-INF/view/remove.jsp从请求作用域中获取数据 ${requestScope.msg} ${requestScope.fun}

    3.在处理器类中编写对应处理方法

    @Controller
    public class MyController {
        
        @RequestMapping("/some.do")
        public ModelAndView doSome(){
            ModelAndView mv = new ModelAndView();
            mv.addObject("fun","执行的是doSome方法");
            mv.addObject("msg","Hello SpringMVC!");
            mv.setViewName("result");
            return mv;
        }
        @RequestMapping("/add.do")
        public ModelAndView doAdd(){
            ModelAndView mv = new ModelAndView();
            mv.addObject("fun","执行的是doAdd方法");
            mv.addObject("msg","处理add.do请求");
            mv.setViewName("add");
            return mv;
        }
        @RequestMapping("/remove.do")
        public ModelAndView doRemove(){
            ModelAndView mv = new ModelAndView();
            mv.addObject("fun","执行的是doRemove方法");
            mv.addObject("msg","处理remove.do请求");
            mv.setViewName("remove");
            return mv;
        }
    }
    

    4.开启服务器

    2.1.2在类上使用@RequestMapping

    当发送的多个请求有相同的前缀时,这个前缀就叫该类请求的模块名称。

    如:

    相同前缀为/test,那么这个/test就是模块名称

    当不做任何处理时,我们需要在每个处理方法上的@RequestMapping注解中都写上模块名称

    为了减少重复代码,我们可以把模块名称抽取出来。

    如何实现呢?在类上使用@RequestMapping注解,将模块名称写在里面即可实现抽离。

    一般我们在开发中,都会在处理器类上用@RequestMapping指明一个模块名称,表示这个处理器是处理哪部分请求的。

    2.1.3指定请求方式

    @RequestMapping有一个method属性,可以指定处理方法处理什么类型的请求(get/post)

    格式:

    1. 指定处理方法处理GET方式的请求:@RequestMapping(value = "GET方式请求",method = RequestMethod.GET)
    2. 指定处理方法处理POST方式的请求:@RequestMapping(value = "POST方式请求",method = RequestMethod.POST)
    3. 指定处理方法处理任何方式的请求:@RequestMapping(value = "任何方式的请求")

    演示使用:

    1.在index.jsp中分别添加GET方式的请求和POST方式的请求:

    2.在各自处理方法上的@RequestMapping注解中指定处理get/post方式的请求

    @Controller
    @RequestMapping(value = "/test")
    public class MyController {
        //指定处理get方式的请求
        @RequestMapping(value = "/get.do",method = RequestMethod.GET)
        public ModelAndView doGet(){
            ModelAndView mv = new ModelAndView();
            mv.addObject("fun","执行的是doGet方法");
            mv.addObject("msg","处理get.do请求");
            mv.setViewName("result");
            return mv;
        }
        //指定处理post方式的请求
        @RequestMapping(value = "/post.do",method = RequestMethod.POST)
        public ModelAndView doPost(){
            ModelAndView mv = new ModelAndView();
            mv.addObject("fun","执行的是doPost方法");
            mv.addObject("msg","处理post.do请求");
            mv.setViewName("result");
            return mv;
        }
        //指定处理任意方式的请求
        @RequestMapping(value = "/any.do")
        public ModelAndView doAny(){
            ModelAndView mv = new ModelAndView();
            mv.addObject("fun","执行的是doAny方法");
            mv.addObject("msg","处理any.do请求");
            mv.setViewName("result");
            return mv;
        }
    }
    

    3.开启服务器

    4.当请求的请求方式与处理方法指定的请求方式不同时,会报405错误

    使用get方式发送一条请求给处理post请求的doPost方法

    使用GET方式发送post.do请求
    

    2.2处理器方法的参数

    处理器方法中可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,也就是说可以在处理器方法内直接使用

    1. HttpServletRequest request
    2. HttpServletResponse response
    3. HttpSession session
    4. 用户发送的请求中所携带的请求参数(如:name,age,email等)
    2.2.1处理器方法中使用请求对象

    创建一个index2.jsp文件,在里面通过表单发送请求,携带name和age两个参数

    index2.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
        
            * {
                font-size: 20px;
            }
        
    
    
    
    姓名:
    年龄:

    在com.tsccg.controller包下新建处理器类:MyController2,在类上使用@RequestMapping注解抽取模块名称:/receive

    处理器方法doGetParam()形参为:HttpServletRequest request

    在方法内调用请求对象request获取请求参数,并将请求参数放入当前请求作用域中,然后指定视图:show.jsp。

    MyController2:

    @Controller
    @RequestMapping(value = "/receive")
    public class MyController2 {
        @RequestMapping("/getParam.do")
        public ModelAndView doGetParam(HttpServletRequest request) {
            ModelAndView mv = new ModelAndView();
            mv.addObject("userName",request.getParameter("name"));
            mv.addObject("userAge",request.getParameter("age"));
            mv.setViewName("show");
            return mv;
        }
    }
    

    在/WEB-INF/view/下新建show.jsp文件

    show.jsp:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
        
            * {
                font-size: 20px;
                color: red;
            }
        
    
    
        
    用户名:${userName}
    年龄:${userAge}

    打开服务器:

    2.2.2逐个接收请求参数

    当我们直接在处理器方法上添加普通java类型形参时,SpringMVC会根据形参名自动获取对应的请求参数,然后将请求参数赋给形参。

    注意:需要保证请求参数名与处理器方法的形参名相同,形参顺序不影响

    1.演示

    1)修改index2.jsp页面:

    以get方式发送请求,携带name和age两个参数

    2)添加处理方法:

    在方法上添加String类型的name和int类型的age两个形参

    3)打开服务器:

    2.过程分析

    1)使用请求对象获取请求参数

    String strName = request.getParameter("name");
    String strAge = request.getParameter("age");
    

    2)SpringMVC框架通过中央处理器(DispatcherServelt)调用处理器MyController2的方法doParam()

    调用方法时,把相同名称的请求参数赋值给它的形参。

    在赋值时,会自动进行类型转换,将String类型的请求参数age转换为Integer类型,再赋给int类型形参age

    doParam(String name = strName,int age = Integer.parseInt(strAge)){
        ...
    }
    
    3.缺点分析

    当我们在发送请求时,如果没有输入年龄值,会报400错误

    400状态码是客户端错误,表示因发送的请求语法错误,服务器无法正常读取。

    这里报400状态码是因为:在处理器方法doParam(String name,int age)中,需要将String类型的请求参数age转换为Integer类型,再赋给int类型的方法形参age:

    String strAge = request.getParameter("age");
    int age = Integer.parseInt(strAge);
    

    当发送过来的请求中age的值为空字符串""时,Integer.parseInt(strAge)的执行结果为null,而null不能赋值给int类型的参数,所以出现了错误。

    解决方法就是使用Integer类型或者String类型的age形参:

    使用Integer类型只能处理数字和null两种请求参数,当请求参数为非数字类型时,还是会报400错误。

    比如输入“abc”:

    但是也不必非使用String类型,虽然使用后浏览器不会报400错误了,但是还需要我们在处理器方法中再次转换类型,此外当请求参数出现问题时仍然会执行处理器方法,不利于我们程序的安全。

    当使用Integer类型报400状态码时,不会继续执行处理器方法,也便于我们查找到错误:

    [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type ‘java.lang.String’ to required type ‘java.lang.Integer’; nested exception is java.lang.NumberFormatException: For input string: “abc”]

    2.2.3解决请求参数乱码问题

    当我们以GET方式提交中文参数时,不会出现乱码

    而当我们以POST方式发送中文参数时,处理器方法获取到的中文参数会出现乱码问题。

    1.演示

    1)以POST方式发送请求:

    2)打开服务器,在页面中输入中文参数,出现乱码问题:

    2.解决方法

    1)Servlet解决乱码问题的方法

    我们在使用Servlet时,会在doPost方法中设置用utf-8字符集重新编辑请求参数:

    public void doPost(HttpServletRequest request,HttpServletResponse response) {
        request.setCharacterEncoding("utf-8");
    }
    

    因为不想在所有Servlet中都设置一遍,我们将这一步移到过滤器中实现:

    过滤器类:OneFilter

    public class OneFilter implements Filter {
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            servletRequest.setCharacterEncoding("utf-8");//增强
            filterChain.doFilter(servletRequest,servletResponse);//归还
        }
    }
    

    web.xml配置:

    
        OneFilter
        com.tsccg.filter.OneFilter
    
    
        OneFilter
        
        
        @RequestMapping(value = "/ajax.do")
        public void returnVoid(HttpServletResponse response,String name,Integer age) throws IOException {
            //定义json格式字符串
            String json = "{}";
            if (name != null && age != null) {
                //1.处理Ajax请求
                //service调用完毕后,使用student表示处理结果
                Student student = new Student(name,age);
                //2.将student对象转换为json格式字符串
                ObjectMapper mapper = new ObjectMapper();
                json = mapper.writevalueAsString(student);
                System.out.println("json格式字符串:" + json);
            }
            //3.输出数据,将json格式字符串发送回异步对象
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.print(json);
            out.flush();
            out.close();
        }
    }
    

    4.开启服务器,发送Ajax请求

    5.分析处理器方法处理Ajax请求的步骤

    可以发现,除了处理Ajax请求得到处理结果的部分外,将对象转换为json格式字符串和输出数据部分的实现步骤都是固定的,也就是说,每次实现都是做重复的步骤。

    既然存在重复步骤,那么就意味着可以通过框架来封装这些步骤,我们只需要专心写业务逻辑代码即可。

    那么如何使用框架响应Ajax请求呢,这就涉及到了返回值为Object对象的情况。

    2.3.4返回值为Object对象

    处理器方法的返回值可以为Object对象。这个Object可以是Integer、String、自定义对象、List、Map等。

    返回的对象有属性,属性就是数据,所以Object表示数据,和视图无关。

    因此,我们可以使用对象表示数据,通过框架处理得到json格式数据响应Ajax请求。

    1.通过框架处理Ajax请求的实现步骤分析

    1. 添加jackson工具库依赖:处理Ajax请求使用的是json数据格式,需要加入处理json的工具库的依赖,SpringMVC框架默认使用的是jackson
    2. 在SpringMVC的配置文件中加入:注解驱动
    3. 在处理器方法上添加@ResponseBody注解

    2.通过框架处理Ajax请求的内部原理:【了解】

    1.注解驱动

    ​ 1)注解驱动实现的功能是:完成java对象到json、xml、text、二进制等数据格式的转换。

    ​ 2)使用的接口:HttpMessageConveter,消息转换器。该接口定义了java转为json、xml等数据格式的方法。这个接口有很多实现类。这些实现类完成了java对象到json,到xml,到二进制等数据格式的转换。

    ​ 3)下面两个方法是控制器类把结果输出给浏览器时使用的:

    //判断处理器方法返回的对象能否转换为var2指定的数据格式;
    //var2:response.setContentType("application/json;charset=utf-8");
    boolean canWrite(Class var1,@Nullable MediaType var2);
    //调用jackson中的ObjectMapper把处理器方法的返回值对象转换为json格式字符串
    void write(T var1,@Nullable MediaType var2,HttpOutputMessage var3);
    

    ​ 4)当加入到SpringMVC配置文件后,会自动创建HttpMessageConverter接口的七个实现类

    2.@ResponseBody注解

    放在处理器方法上,通过HttpServletResponse对象【响应对象】输出数据,响应Ajax请求。

    效果同如下代码:

    PrintWriter out = response.getWriter();
    out.print(json);
    out.flush();
    out.close();
    
    3.实例演示-返回自定义对象

    1.加入jackson依赖

    
        com.fasterxml.jackson.core
        jackson-core
        2.9.0
    
    
        com.fasterxml.jackson.core
        jackson-databind
        2.9.0
    
    

    2.在SpringMVC的配置文件中声明注解驱动

    注意:加入的是以mvc为结尾的

    完整SpringMVC配置文件:

    
    
        
        
        
        
            
            
            
            
        
        
        
    
    

    3.编写处理器方法

    返回值为自定义对象Student

    在方法上添加@ResponseBody注解

    @Controller
    @RequestMapping(value = "/return")
    public class MyController3 {
        
        @RequestMapping("/object.do")
        @ResponseBody//通过响应对象将转换后的json格式字符串写入响应包中,响应Ajax请求。
        public Student returnObject(String name,Integer age) {
            //调用service,获取处理结果:student
            Student student = null;
            if (name != null && age != null) {
                student = new Student(name,age);
            }
            return student;
        }
    }
    

    4.修改页面发送的url

    5.开启服务器,发送Ajax请求

    4.实例演示-返回List集合

    在实际开发中,可能需要同时返回多个对象,这时就需要用List集合存放多个对象,然后返回List集合对象

    1.处理器方法:返回List集合对象

    @Controller
    @RequestMapping(value = "/return")
    public class MyController3 {
        
        @RequestMapping("/list.do")
        @ResponseBody
        public List returnList(String name,Integer age) {
            //调用service,获取处理结果:studentList
            List studentList = new ArrayList<>();
            Student student1 = new Student(name,age);
            Student student2 = new Student("李四",24);
            studentList.add(student1);
            studentList.add(student2);
            return studentList;
        }
    }
    

    2.修改前端请求页面:

    使得将服务器响应的json数组中的数据写入页面表格中

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
        
            * {
                font-size: 20px;
            }
            table {
                width: 150px;
            }
            td {
                width: 50%;
            }
        
        
        
    
    
    
    姓名:
    年龄:
    姓名 年龄

    3.开启服务器,发送Ajax请求

    5.实例演示-返回String对象

    当处理器方法返回值类型为String时:

    1. 如果没有在方法上添加@ResponseBody,则表示视图
    2. 如果方法上有@ResponseBody,则表示文本数据

    1.处理器方法:

    需要注意的是,当返回String对象时,默认的contentType为:text/plain;charset=ISO-8859-1,会导致浏览器解析响应数据时发生中文乱码问题。

    解决方法:使用@RequestMapping注解的produces属性,重新指定contentType为:text/plain;charset=utf-8

    @Controller
    @RequestMapping(value = "/return")
    public class MyController3 {
        
        @RequestMapping(value = "/string.do",produces = "text/plain;charset=utf-8")
        @ResponseBody
        public String returnString(String name,Integer age) {
            return name + "今年" + age + "岁";
        }
    }
    

    2.修改请求页面:

    3.开启服务器,发送Ajax请求

    2.4解读 2.4.1“/”的作用

    前面说过,当SpringMVC的中央调度器DispatcherServlet的所指定的值为*.do时,表示Tomcat服务器将所有以.do结尾的请求都交给中央调度器来处理。

    那么,当所指定的值为“/”时,会有什么作用呢?

    我们先在项目中添加一些静态资源:.html、.js、.jpg

    在/webapp目录下新建html、js、images三个目录,分别在三个目录中加入相对应的静态资源文件:

    1)html

    welcome.html

    2)js

    放入jquery库文件

    3)images

    放入一张图片dog.jpg

    文件结构:

    在Index.jsp页面中引入这些静态资源文件

    index.jsp文件:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
        
            * {
                font-size: 20px;
            }
        
        
        
    
    
    
    姓名:
    年龄:

    欢迎界面

    开启服务器:发现在打开页面的同时,静态资源也一并加载出来了

    我们点击F12,查看控制台:

    查看浏览器发送的请求:

    http://localhost:8080/MyWeb/index.jsp:动态资源文件,由tomcat处理(jsp会转为servlet)

    http://localhost:8080/MyWeb/images/dog.jpg:静态资源文件,tomcat

    http://localhost:8080/MyWeb/js/jquery-3.4.1.js:静态资源文件,tomcat

    http://localhost:8080/MyWeb/html/welcome.html:静态资源文件,tomcat

    http://localhost:8080/MyWeb/path/some.do:动态资源文件,由SpringMVC框架的中央处理器处理(DispatcherServlet)

    tomcat本身能处理资源文件的访问,使用的是其自带的默认servlet:DefaultServlet(可查看tomcat的conf/web.xml)

    
        default
        org.apache.catalina.servlets.DefaultServlet
        
            debug
            0
        
        
            listings
            false
        
        
        1
    
    
        default
        
        /
    
    

    的值决定了服务器将哪些请求交给当前servlet处理。如果我们搞明白了DefaultServlet的作用,那么就能搞明白“/”的作用。

    我们可以查看官方说明DefaultServlet作用的注释:

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

    意思是说,DefaultServlet可以:

    1. 处理静态资源的请求
    2. 处理未映射到其它servlet的请求,也就是缺省匹配:如果一个请求与除当前servlet外的其他所有servelt都无法匹配,那么就由当前servlet处理该请求

    由此,“/”的作用就可想而知了。

    2.4.2在中央处理器中设置“/”出现的问题

    将我们当前项目的中央处理器的设置为“/”

    重启服务器:

    发现所有的静态资源文件都访问不到了,而动态资源index.jsp和some.do仍然可以访问到:

    1)动态资源index.jsp;静态资源dog.jpg和jquery-3.4.1.js

    2)静态资源welcome.html

    3)动态资源some.so

    这是因为在中央处理器(DispatcherServlet)中设置url-pattern为“/”后,中央处理器就取代了tomcat自带的DefaultServlet,导致现在所有的静态资源的请求和未映射到其他servlet的请求都交给中央处理器处理。

    1. 静态资源请求404:当中央调度器接收到静态资源的请求后,会将其当作一个普通的Controller请求,为其查找相应的处理器,由于当前项目中没有能处理静态资源请求的处理器,所以所有的静态资源请求都会报404错误。
    2. 动态资源index.jsp请求正常:中央调度器的url-pattern设置为“/”,表示缺省匹配,而tomcat的servlet容器有内置的“*.jsp”扩展名匹配器,扩展名匹配的优先级高于缺省匹配,所以中央调度器无法拦截jsp请求。如果想设置中央处理器处理所有的请求,需要设置url-pattern为“*.properties ***.properties ** public class Student { private Integer id; private String name; private Integer age; public Student() { } public Student(Integer id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } //getter and setter //toString() } 3.3.10创建Dao接口及其mapper文件

      在dao包下新建Dao接口:StudentDao,在其中定义增、删、查三个抽象方法

      package com.tsccg.dao;
      
      import com.tsccg.entity.Student;
      
      import java.util.List;
      
      public interface StudentDao {
          
          List selectStudents();
          
          int insertStudent(Student student);
          
          int deleteStudent(Integer id);
      }
      

      在同级包下创建其mapper文件:StudentDao.xml

      
      
      
          
          
                  
                  
                      年龄:
                      
                  
                  
                      
                      
                  
              
          
      
      
      
      
      4.展示学生列表页面:showStudent.jsp

      在WEB-INF/view目录下新建showStudent.jsp

      因为需要用到jquery,将jquery-3.4.1.js文件放入/static/js目录下

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%
          //动态地获取当前项目路径:http://localhost:8080/MyWeb/
          String basePath = request.getScheme() + "://" +
                  request.getServerName() + ":" + request.getServerPort() +
                  request.getContextPath() + "/";
      %>
      
      
          Title
          
          
          
              * {
                  font-size: 20px;
              }
              table {
                  width: 600px;
              }
              td {
                  width: 50px;
              }
          
          
          
      
      
      
      5.展示处理结果页面:result.jsp

      在WEB-INF/view目录下新建result.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%
          //动态地获取当前项目路径:http://localhost:8080/MyWeb/
          String basePath = request.getScheme() + "://" +
                  request.getServerName() + ":" + request.getServerPort() +
                  request.getContextPath() + "/";
      %>
      
      
          结果
          
          
              * {
                  font-size: 20px;
                  color: red;
              }
          
      
      
          
      ${requestScope.result}
      返回管理页面
      3.3.13创建Controller类

      在controller包下新建StudentController,处理前端发送的各种请求。

      package com.tsccg.controller;
      
      import com.tsccg.entity.Student;
      import com.tsccg.service.StudentService;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.ResponseBody;
      import org.springframework.web.servlet.ModelAndView;
      
      import javax.annotation.Resource;
      import java.util.List;
      
      
      @Controller
      @RequestMapping("/student")
      public class StudentController {
          //定义Service对象,使用自动注入为其赋值
          @Resource
          private StudentService service;
      
          
          @RequestMapping("/addStudentPage")
          public String registerStudent() {
              return "addStudent";
          }
          
          @RequestMapping("/showStudentPage")
          public String showStudentPage() {
              return "showStudent";
          }
          
          @RequestMapping("/showList")
          @ResponseBody
          public List findStudents() {
              return service.findStudents();
          }
          
          @RequestMapping("/add")
          public ModelAndView addStudent(Student student) {
              ModelAndView mv = new ModelAndView();
              int result = service.addStudent(student);
              mv.addObject("result",result > 0 ?
                      "注册成功" : "注册失败");
              mv.setViewName("result");
              return mv;
          }
          
          @RequestMapping("/delete")
          public ModelAndView deleteStudent(Integer id) {
              ModelAndView mv = new ModelAndView();
              int result = service.removeStudent(id);
              mv.addObject("result",result > 0 ?
                      "删除成功" : "删除失败");
              mv.setViewName("result");
              return mv;
          }
      }
      
      3.3.14开启服务器,演示功能

      演示注册功能:

      演示删除功能:

      4.SpringMVC核心技术 4.1简化请求转发与重定向操作 4.1.1请求转发与重定向

      我们知道,服务端资源之间的跳转有两种方式:

      1. 请求转发
      2. 重定向

      而根据所要跳转的资源类型,又可分为:

      1. 跳转到页面
      2. 跳转到其他处理器/servlet

      注意:

      1. 对于请求转发的请求,是从服务端内部访问的,即可以访问服务端外部无法访问的资源,如:WEB-INF目录下的页面
      2. 对于重定向发送的请求,是从服务端外部访问的,即不可以访问WEB-INF下的页面

      4.1.2框架封装操作

      SpringMVC框架把原来servlet中的请求转发和重定向操作进行了封装,简化成了两个关键字:forward和redirect

      1. forward:
        1. 表示请求转发
        2. 实现:request.getRequestDispatcher(“xxx.jsp”).forward(req,res);
        3. 使用方式:mv.setViewName(“forword:视图文件完整相对路径”);
      2. redirect:
        1. 表示重定向
        2. 实现:response.sendRedirect(“xxx.jsp”);
        3. 使用方式:mv.setViewName(“redirect:视图文件完整相对路径”);

      forward和redirect有一个共同的特点,就是:不受视图解析器影响。

      ​ 在项目中,当有页面比如login.jsp不在WEB-INF/view目录下时,如果配置了视图解析器,就无法跳转到login.jsp页面。为了解决这种问题,就可以使用forward或者redirect关键字。

      4.1.3演示关键字的作用 1.准备工作

      现在使用SpringMVC重新创建一个项目,在SpringMVC配置文件里配置好视图解析器

      
          
          
          
          
      
      

      发送请求页面:index.jsp

      在/WEB-INF/view/外的位置创建一个login.jsp文件,作为视图:

      2.演示forward:请求转发

      在处理器方法中使用forward关键字指定login.jsp为视图,加上前后缀

      @Controller
      public class MyController {
          @RequestMapping(value = "/some.do")
          public ModelAndView doSome(String name,Integer age){
              ModelAndView mv = new ModelAndView();
              mv.addObject("userName",name);
              mv.addObject("userAge",age);
              //使用forward指定视图
              mv.setViewName("forward:/login.jsp");
              return mv;
          }
      }
      

      开启服务器:

      可见,成功访问到/WEB-INF/view/以外的视图文件:login.jsp,且实现了实现处理器与login.jsp间的数据共享

      3.演示redirect:重定向

      在处理器方法中,使用redirect关键字指定视图为:login.jsp,同样加上前后缀

      @Controller
      public class MyController {
          @RequestMapping(value = "/some.do")
          public ModelAndView doSome(String name,Integer age){
              ModelAndView mv = new ModelAndView();
              mv.addObject("userName",name);
              mv.addObject("userAge",age);
              //使用redirect指定视图
              mv.setViewName("redirect:/login.jsp");
              return mv;
          }
      }
      

      开启服务器:

      同样成功访问到login.jsp,但是未能实现处理器与login.jsp间的数据共享,且第二次浏览器发送的请求携带着在处理器方法中写入Model的数据。

      我们来分析一下全过程:

      1.浏览器第一次发送请求:http://localhost:8080/MyWeb/some.do ,携带参数:name=tom&age=21
      2.处理器接收到some.do请求,对请求进行处理:
      1)将请求参数写入Model:
      mv.addObject(“userName”,“tom”);
      mv.addObject(“userAge”,“21”);
      2)重定向到login.jsp:中央调度器将login.jsp的地址和上一步Model里的数据写入响应协议包
      mv.setViewName(“redirect:/login.jsp”);
      3.浏览器第二次发送请求:http://localhost:8080/MyWeb/login.jsp?userName=tom&userAge=21
      4.服务器定位到login.jsp
      1)根据EL表达式${userName}从当前请求作用域中查找数据,但当前请求作用域中没有数据
      2)服务器将login.jsp的页面数据响应回浏览器

      框架对重定向的操作:框架会把Model中的简单类型数据,转化成String,作为第二次浏览器发送请求的参数使用,目的是实现资源间的数据共享。

      那么如何在login.jsp中拿到第二次请求所携带的数据呢?

      我们可以使用参数集合对象:${param.key}

      重新发送请求:

      4.2统一处理异常 4.2.1框架的异常处理思路

      在我们之前的学习中,一直都是通过try…catch语句来处理异常的。

      这些异常处理代码与业务代码无关,经常在一个页面中多次使用,造成代码的冗余。

      public void fun1() {
          try {
              //业务代码
          } catch(Exception e) {
              
          }
      }
      public void fun2() {
          try {
              //业务代码
          } catch(Exception e) {
              
          }
      }
      public void fun3() {
          try {
              //业务代码
          } catch(Exception e) {
              
          }
      }
      

      为此,SpringMVC框架采用一种统一、全局的异常处理:

      ​ 把Controller对象的所有异常处理都集中到一个地方,采用AOP的思想,将业务逻辑代码和异常处理代码分离,实现解耦合。

      需要使用两个注解:

      1. @ControllerAdvice
      2. @ExceptionHandler
      4.2.2异常处理步骤分析 1.准备工作

      使用SpringMVC技术新建一个maven项目:

      项目包含前端页面:index.jsp,发送携带参数的请求

      有一个控制器用于处理此请求

      @Controller
      public class MyController {
          @RequestMapping(value = "/some.do")
          public ModelAndView doSome(String name,Integer age){
              ModelAndView mv = new ModelAndView();
              mv.addObject("userName",name);
              mv.addObject("userAge",age);
              mv.setViewName("show");
              return mv;
          }
      }
      

      视图页面:show.jsp

      2.添加异常处理

      现分析为以上项目添加异常处理的步骤:

      1.创建三个自定义异常类

      ​ 1)MyUserException,继承Exception

      ​ 2)MyNameException,继承MyUserException,用于处理name参数异常

      ​ 3)MyAgeException,继承MyUserException,用于处理age参数异常

      2.修改Controller,抛出自定义异常

      3.创建一个普通类,作为全局异常处理类

      ​ 1)在类的上面添加@ControllerAdvice

      ​ 2)在类中定义方法,方法上面添加@ExceptionHandler

      4.创建处理异常的视图页面

      5.配置SpringMVC的配置文件

      ​ 1)声明第一个组件扫描器,用于扫描@Controller修饰的类

      ​ 2)声明第二个组件扫描器,用于扫描@ControllerAdvice修饰的类

      ​ 3)声明注解驱动

      4.2.3演示异常处理 1.创建自定义异常类

      在com.tsccg.exception包下新建三个自定义异常类MyUserException、MyNameException和MyAgeException

      其中,MyNameException和MyAgeException是MyUserException的子类。

      1)MyUserException

      public class MyUserException extends Exception{
          public MyUserException() {
              super();
          }
          public MyUserException(String message) {
              super(message);
          }
      }
      

      2)MyNameException

      public class MyNameException extends MyUserException{
          public MyNameException() {
          }
      
          public MyNameException(String message) {
              super(message);
          }
      }
      

      3)MyAgeException

      public class MyAgeException extends MyUserException {
          public MyAgeException() {
              super();
          }
      
          public MyAgeException(String message) {
              super(message);
          }
      }
      
      2.修改Controller抛出自定义异常

      在控制器方法中抛出MyNameException异常对象,MyAgeException异常对象

      @Controller
      public class MyController {
          @RequestMapping(value = "/some.do")
          public ModelAndView doSome(String name,Integer age) throws MyUserException {
              ModelAndView mv = new ModelAndView();
              //判断,当参数不合要求时,抛出自定义异常
              if(!"张三".equals(name)) {
                  throw new MyNameException("姓名不对!");
              }
              if(age < 0 || age > 200) {
                  throw new MyAgeException("年龄不对!");
              }
              mv.addObject("userName",name);
              mv.addObject("userAge",age);
              mv.setViewName("show");
              return mv;
          }
      }
      
      3.创建全局异常处理类

      在com.tsccg.handler包下创建一个普通java类:GlobalExceptionHandler,作为全局异常处理类

      (1)在类的上面添加@ControllerAdvice注解:

      ​ 1)控制器增强,给控制器类增加功能–异常处理功能

      ​ 2)需要在SpringMVC配置文件中使用组件扫描器创建该类对象

      (2)在类中定义方法,方法上面添加@ExceptionHandler注解:

      ​ 1)@ExceptionHandler(value=异常的class):表示当Controller中抛出此类型异常时,由当前方法处理。

      ​ 2)方法作用:处理Controller中抛出的异常。

      ​ 3)方法定义:和控制器方法一样,可以有多个参数,可以有ModelAndView,String,void,对象类型的返回值。

      ​ 4)方法参数:Exception,表示Controller中抛出的异常对象,通过形参可以获取发生的异常信息。

      (3)异常处理逻辑:

      ​ 1)记录异常:记录到数据库,日志文件。记录的内容包括日志发生的事件,由哪个方法发生的,异常错误的内容。

      ​ 2)发送通知:把异常信息通过邮件、短信、微信、qq等形式发送给相关人员。

      ​ 3)给用户返回友好而和善的提示: Σ( ° △ °|||!!!)︴

      GlobalExceptionHandler:

      @ControllerAdvice
      public class GlobalExceptionHandler {
          
          @ExceptionHandler(value = MyNameException.class)
          public ModelAndView doMyNameException(Exception exception) {
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg","名字错啦骚年!");
              //将异常对象一并写入请求作用域
              mv.addObject("ex",exception);
              mv.setViewName("nameError");
              return mv;
          }
          
          @ExceptionHandler(value = MyAgeException.class)
          public ModelAndView doMyAgeException(Exception exception) {
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg","年龄错啦骚年!");
              //将异常对象一并写入请求作用域
              mv.addObject("ex",exception);
              mv.setViewName("ageError");
              return mv;
          }
          
          @ExceptionHandler
          public ModelAndView doDefaultException(Exception exception) {
              ModelAndView mv = new ModelAndView();
              mv.addObject("msg","发生了异常!");
              //将异常对象一并写入请求作用域
              mv.addObject("ex",exception);
              mv.setViewName("defaultError");
              return mv;
          }
      }
      
      4.创建处理异常的视图页面

      nameError:

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      
      
          nameError
          
              * {
                  font-size: 20px;
                  color: red;
              }
          
      
      
      
      nameError.jsp
      提示信息:${requestScope.msg}
      错误信息:${ex.message}

      ageError:

      defaultError:

      5.配置SpringMVC的配置文件
      1. 声明第一个组件扫描器,用于扫描@Controller修饰的类
      2. 声明第二个组件扫描器,用于扫描@ControllerAdvice修饰的类
      3. 声明注解驱动
      
      
      
      
      
      
      6.测试处理异常功能

      开启服务器,通过浏览器发送请求:

      1)符合要求的参数:

      2)抛出MyNameException:

      3)抛出MyAgeException:

      4)抛出其他异常

      4.3拦截器 4.3.1拦截器概述

      什么是拦截器?

      1. 拦截器是SpringMVC框架中的一种对象,需要实现HandlerInterceptor接口

      2. 拦截器和过滤器类似,但功能方向侧重点不同

        1. 过滤器:用于过滤请求参数,设置字符编码集等工作
        2. 拦截器:用于拦截用户请求,对请求做判断处理
      3. 拦截器是全局的,可以对多个Controller做拦截

        1. 一个项目中可以有0个或多个拦截器,它们一起拦截用户的请求
        2. 拦截器常用于做用户登陆处理、权限检查、记录日志等工作
      4. 拦截器可看作是多个Controller中公用的非业务功能,集中到拦截器统一处理,使用的是AOP思想。

      拦截器的使用步骤:

      1. 创建一个普通类,作为拦截器使用
        1. 实现HandlerInterceptor接口
        2. 实现接口中的三个方法
          1. preHandle():在请求处理之前,也就是处理器方法执行之前执行
          2. postHandle():在处理器方法执行之后执行
          3. afterHandle():在请求处理完成之后执行
      2. 在SpringMVC配置文件中,声明拦截器,让框架知道拦截器的存在
        1. 组件扫描器,扫描@Controller注解
        2. 声明拦截器:
          1. 指定拦截的请求uri地址
          2. 声明拦截器对象
      4.3.2单个拦截器的使用 1.前提

      使用SpringMVC技术新建一个maven项目:有发送请求页面,有处理请求的Controller,有响应页面。

      发送请求页面:index.jsp,发送携带参数的请求

      Controller:

      @Controller
      public class MyController {
          @RequestMapping(value = "/some.do")
          public ModelAndView doSome(String name,Integer age) throws Exception {
              System.out.println("====执行MyController的doSome方法====");
              ModelAndView mv = new ModelAndView();
              mv.addObject("userName",name);
              mv.addObject("userAge",age);
              mv.setViewName("show");
              return mv;
          }
      }
      

      响应页面:show.jsp

      2.创建一个普通类,作为拦截器使用
      1. 实现HandlerInterceptor接口
      2. 实现接口中的三个方法
        1. preHandle():在请求处理之前,也就是处理器方法执行之前执行
        2. postHandle():在处理器方法执行之后执行
        3. afterHandle():在请求处理完成之后执行
      public class MyInterceptor implements HandlerInterceptor {
          
          @Override
          public boolean preHandle(HttpServletRequest request,
                                   HttpServletResponse response,
                                   Object handler) throws Exception {
              System.out.println("====1.执行拦截器MyInterceptor中的preHandle方法====");
              boolean isFlag = false;
              String name = request.getParameter("name");
              if ("张三".equals(name)) {
                  isFlag = true;
              }
              return isFlag;
          }
      
          
          @Override
          public void postHandle(HttpServletRequest request,
                                 HttpServletResponse response,
                                 Object handler,
                                 ModelAndView modelAndView) throws Exception {
              System.out.println("====2.执行拦截器MyInterceptor中的postHandle方法====");
      
          }
      
          
          @Override
          public void afterCompletion(HttpServletRequest request,
                                      HttpServletResponse response,
                                      Object handler,
                                      Exception ex) throws Exception {
              System.out.println("====3.执行拦截器MyInterceptor中的afterCompletion方法====");
          }
      }
      
      
      3.配置SpringMVC配置文件

      在SpringMVC配置文件中,声明拦截器,让框架知道拦截器的存在

      1. 组件扫描器,扫描@Controller注解
      2. 声明拦截器:
        1. 指定拦截的请求uri地址
        2. 声明拦截器对象
      
      
      
          
          
              
              
              
              
          
      
      
      4.测试拦截器功能

      开启服务器,通过浏览器发送请求:

      1)preHandle()方法返回值为true:

      后台输出:

      ====1.执行拦截器MyInterceptor中的preHandle方法====
      ====执行MyController的doSome方法====
      ====2.执行拦截器MyInterceptor中的postHandle方法====
      ====3.执行拦截器MyInterceptor中的afterCompletion方法====
      

      发送请求后,拦截器验证后放行,处理器正常处理请求。

      2)preHandle()方法返回值为false:

      后台输出:

      ====1.执行拦截器MyInterceptor中的preHandle方法====
      

      发送请求后,被拦截器预处理方法截断,处理器方法和其他拦截器方法不会执行。

      5.单个拦截器中方法与处理器方法的执行顺序

      5.添加拦截反馈页面

      拦截器截断请求后,页面没有任何显示是因为拦截器的预处理方法没有给任何反馈。

      在预处理方法截断请求时,请求转发一个反馈页面:tip.jsp

      @Override
      public boolean preHandle(HttpServletRequest request,
                               HttpServletResponse response,
                               Object handler) throws Exception {
          boolean isFlag = false;
          String name = request.getParameter("name");
          System.out.println("====1.执行拦截器MyInterceptor中的preHandle方法====");
          if ("张三".equals(name)) {
              isFlag = true;
          } else {
              //请求转发到反馈页面
              request.getRequestDispatcher("/WEB-INF/view/tip.jsp").forward(request,response);
          }
          return isFlag;
      }
      

      在/WEB-INF/view文件夹下新建tip.jsp

      演示:

      6.使用postHandle方法修改处理结果

      postHandle方法可以获取到处理器的处理结果,可以对其进行修改。

      现修改处理器的处理结果,插入一条日期数据,并指定新的视图:show_02.jsp

      @Override
      public void postHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler,
                             ModelAndView modelAndView) throws Exception {
          System.out.println("====2.执行拦截器MyInterceptor中的postHandle方法====");
          //对处理结果进行修改
          if(modelAndView != null) {
              modelAndView.addObject("date",new Date());
              modelAndView.setViewName("show_02");
          }
      }
      

      在/WEB-INF/view目录下新建show_02.jsp:

      开启服务器,发送请求:

      7.计算处理请求所花费时间

      1.定义成员变量startTime作为初始时间戳,在preHandle方法中使用System.currentTimeMillis()为其赋值。

      2.在afterCompletion方法中定义结束时间戳:endTime,计算总耗时并输出。

      开启服务器,查看效果:

      4.3.3多个拦截器的使用 1.创建第二个拦截器

      在之前的基础上,在handler包下再创建第二个拦截器:MyInterceptor2

      设置预处理方法截断条件为年龄。

      public class MyInterceptor2 implements HandlerInterceptor {
          @Override
          public boolean preHandle(HttpServletRequest request,
                                   HttpServletResponse response,
                                   Object handler) throws Exception {
              boolean isFlag = false;
              Integer age = Integer.parseInt(request.getParameter("age"));
              System.out.println("222====1.执行拦截器MyInterceptor2中的preHandle方法====");
              if (age >=0 && age < 200) {
                  isFlag = true;
              } else {
                  request.setAttribute("msg","年龄不对!");
                  //请求转发到反馈页面
                  request.getRequestDispatcher("/WEB-INF/view/tip.jsp").forward(request,response);
              }
              return isFlag;
          }
          @Override
          public void postHandle(HttpServletRequest request,
                                 HttpServletResponse response,
                                 Object handler,
                                 ModelAndView modelAndView) throws Exception {
              System.out.println("222====2.执行拦截器MyInterceptor2中的postHandle方法====");
          }
          @Override
          public void afterCompletion(HttpServletRequest request,
                                      HttpServletResponse response,
                                      Object handler,
                                      Exception ex) throws Exception {
              System.out.println("222====3.执行拦截器MyInterceptor2中的afterCompletion方法====");
          }
      }
      
      2.声明第二个拦截器

      在SpringMVC配置文件中声明第二个拦截器。

      先声明的拦截器先执行:在框架中存放多个拦截器使用的是ArrayList集合,按照声明的先后顺序放入到ArrayList集合中。

      
          
          
              
              
          
          
          
              
              
          
      
      
      3.测试运行

      1)两个拦截器的preHandle方法都返回true:

      后台打印结果:

      111====1.执行拦截器MyInterceptor中的preHandle方法====
      222====1.执行拦截器MyInterceptor2中的preHandle方法====
      ====执行MyController的doSome方法====
      222====2.执行拦截器MyInterceptor2中的postHandle方法====
      111====2.执行拦截器MyInterceptor中的postHandle方法====
      222====3.执行拦截器MyInterceptor2中的afterCompletion方法====
      111====3.执行拦截器MyInterceptor中的afterCompletion方法====
      

      2)第二个拦截器的preHandle方法返回false:

      后台打印结果:

      111====1.执行拦截器MyInterceptor中的preHandle方法====
      222====1.执行拦截器MyInterceptor2中的preHandle方法====
      111====3.执行拦截器MyInterceptor中的afterCompletion方法====
      

      3)第一个拦截器不通过:

      后台打印结果:

      111====1.执行拦截器MyInterceptor中的preHandle方法====
      
      5.多个拦截器方法和处理器方法的执行顺序

      当存在多个拦截器时,形成拦截器链。拦截器链的执行顺序与在配置文件中声明的顺序一致。

      需要强调的是,当某一个拦截器的preHandle()方法返回true并被执行到时,会向一个专门的方法栈中放入该拦截器的afterCompletion()方法。

      多个拦截器中方法与处理器方法的执行顺序如下图所示:

      换一种表现形式,也可以这么理解:

      4.3.4过滤器与拦截器的区别
      1. 功能侧重不同:
        1. 过滤器用来设置request,response中的参数,侧重于对数据的过滤,比如说设置字符编码集
        2. 拦截器用来验证请求,可以截断请求
      2. 所处位置不同:
        1. 过滤器是servlet中的对象
        2. 拦截器是SpringMVC框架中的对象,如果请求不被中央调度器所接收,那么请求就不会执行拦截器器中的内容
      3. 实现接口不同:
        1. 过滤器实现的是Filter接口
        2. 拦截器实现的是InterceptorHandler接口
      4. 对象的创建者不同:
        1. 过滤器是由Tomcat服务器创建的
        2. 拦截器是由SpringMVC的容器创建的
      5. 执行时机不同:
        1. 过滤器只有一个执行时机
        2. 拦截器有三个执行时机
      6. 处理的对象不同:
        1. 过滤器可以处理jsp,js,html等
        2. 拦截器侧重于处理Controller对象
      4.3.5模拟登陆验证

      还是使用前面单个拦截器的例子,在其基础上进行修改。

      1.index.jsp 2.login.jsp

      3.loginOut.jsp

      4.拦截器:MyInterceptor
      public class MyInterceptor implements HandlerInterceptor {
      
          @Override
          public boolean preHandle(HttpServletRequest request,
                                   HttpServletResponse response,
                                   Object handler) throws Exception {
              System.out.println("====1.执行拦截器MyInterceptor中的preHandle方法====");
              boolean isFlag = false;
              String loginName = "";
              //从会话作用域中获取name属性值
              Object attr = request.getSession().getAttribute("name");
              if (attr != null) {
                  loginName = (String)attr;
              }
              if ("张三".equals(loginName)) {
                  isFlag = true;
              } else {
                  request.setAttribute("msg","只有张三有权限登陆!");
                  //请求转发到反馈页面
                  request.getRequestDispatcher("/WEB-INF/view/tip.jsp").forward(request,response);
              }
              return isFlag;
          }
      }
      
      5.测试验证功能

      5.补充 5.1SpringMVC执行流程

      执行流程简析:

      1. 浏览器发起请求到中央调度器
      2. 中央调度器直接将请求转交给处理器映射器
      3. 处理器映射器根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器
      4. 中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器
      5. 处理器适配器调用执行处理器
      6. 处理器将处理结果以及将要跳转的视图封装到一个ModelAndView对象里,然后将其返回给处理器适配器
      7. 处理器适配器直接将ModelAndView对象返回给中央调度器
      8. 中央调度器调用视图解析器,将ModelAndView对象中的视图名称封装成视图对象
      9. 视图解析器将封装了的视图对象返回给中央调度器
      10. 中央调度器调用视图对象,让其自己进行渲染,也就是数据填充,生成响应对象
      11. 中央调度器响应浏览器
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/322200.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号