4.SpringMVC核心技术

Posted 小马Mark

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.SpringMVC核心技术相关的知识,希望对你有一定的参考价值。

请求重定向和转发

当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发重定向

根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。

**注意:**对于请求转发的页面,可以是WEB-INF中页面;而重定向的页面,是不能为WEB-INF中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。

SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。

  • forward: 表示转发,实现 request.getRequestDispatcher("xx.jsp").forward()
  • redirect: 表示重定向,实现 response.sendRedirect("xxx.jsp")

请求转发

处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward:视图完整路径

    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name, Integer age) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("myName",name);
        modelAndView.addObject("myAge",age);
        modelAndView.setViewName("forward:/WEB-INF/view/show.jsp"); // forward:完整uri
        return modelAndView;
    }

此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图

  • 视图页面必须写出相对于项目根的路径。

  • forward 操作不需要视图解析器。

处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径

    @RequestMapping(value = "/some.do")
    public String doSome(String name, Integer age, HttpServletRequest request) {
        System.out.println("name = " + name + ", age = " + age);
        request.setAttribute("myName", name);
        request.setAttribute("myAge", age);
        // 框架对视图执行的是forward转发操作
        return "forward:/test.jsp";
    }

请求重定向

在处理器方法返回的视图字符串的前面添加 redirect:,则可实现重定向跳转。(与请求转发语法相似)

重定向是不能访问/WEB-INF/目录下的资源的,因为两次请求都是从客户端发送的,是不能直接访问的。

    @RequestMapping(value = "/other.do")
    public ModelAndView doOther(String name, Integer age) {
        System.out.println("name = " + name + ", age = " + age);
        // name = zxx, age = 123
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("myName",name);  // 将你设置的名称作为参数名,放入到了url中
        modelAndView.addObject("myAge",age);
        modelAndView.setViewName("redirect:/test.jsp");
        return modelAndView;
    }

**注意:**重定向与转发有点不一样,重定向是两次请求,所以是两个不同的request域,所以框架就让在重定向时将你想要设置到Request域中的参数和值,重新放到了第二次请求的get请求的url中,传递给了另一个页面。

http://localhost:8080/springmvc06/test.jsp?myName=zxx&myAge=123

异常处理

两个注解:

  • @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度

    • 属性:value = 异常类的class类型
  • @ControllerAdvice :异常集中处理,更好的使业务逻辑与异常处理剥离开;其实对Controller层进行拦截

补充:@ResponseStatus:可以将某种异常映射为HTTP状态码

框架将异常处理方法专门定义在一个类中,作为全局的异常处理类,需要使用注解@ControllerAdvice,字面理解就是“控制器增强”,是给控制器对象增强功能的。

使用@ControllerAdvice 修饰的类中可以使用@ExceptionHandler。当使用@RequestMapping 注解修饰的方法抛出异常时,会执行@ControllerAdvice 修饰的类中的异常处理方法。

@ControllerAdvice 是使用@Component 注解修饰的,可以<context:component-scan>扫描到@ControllerAdvice 所在的类路径(包名),创建对象。

@ControllerAdvice  // 写在类上
public class GlobalExceptionHandler {

    @ExceptionHandler(value = UserException.class)  // 指定value的值就只处理相应的异常
    public ModelAndView doUserException(Exception e){
        ModelAndView mv = new ModelAndView();
        mv.addObject("tips",e);
        mv.setViewName("userError");
        return mv;
    }

    @ExceptionHandler //不指定value,就处理其他没有被处理的所有异常,所以不指定value的方法只能有一个
    public ModelAndView defaultException(Exception e){
        ModelAndView mv = new ModelAndView();
        mv.addObject("tips",e);
        mv.setViewName("userError");
        return mv;
    }
}
<context:component-scan base-package="com.maj.handler" />  <!--扫描创建异常处理类的包-->
<!--声明注解驱动-->
<mvc:annotation-driven />

拦截器

Interceptor 拦截器是springMVC中的一种

**主要作用:**拦截指定的用户请求,并进行相应的预处理与后处理。

拦截的时间点:

  1. 在请求处理之前,也就是Controller类中的方法执行之前先被拦截
  2. 在控制器方法执行之后也会执行拦截器
  3. 在请求处理完成后也会执行拦截器

在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。

拦截器与过滤器类似,但是功能方向侧重不同:(区别)

  • 过滤器是Servlet中的对象,拦截器是框架中的对象

  • 过滤器是实现 Filter接口,拦截器是实现HandlerInterceptor接口

  • 过滤器是用来过滤请求参数,设置编码字符集等工作,拦截器是拦截用户的请求,做请求判断处理的

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

  • 过滤器是Tomcat服务器创建的对象,拦截器是springmvc容器中创建对象

  • 过滤器可以处理jsp,js,html等等;拦截器是侧重拦截Controller对象,(如果请求不能被 DispatcherServlet接收,这个请求不会执行拦截器内容)

特点:

  • 拦截器也是全局的,可以给多个Controller做拦截
  • 拦截器可以有>=0个,他们一起拦截用户的请求

拦截器常用在:用户登录处理,权限检查,记录日志

一个拦截器

自定义拦截器,需要实现 HandlerInterceptor接口。而该接口中含有三个方法:

  • preHandle(request,response, Object handler)
    • 该方法在处理器方法执行之前执行。
    • 参数:Object handler 被拦截的控制器对象
    • 返回值为 boolean
      • true,表示通过放行,执行后续的处理器方法
      • false,表示没有通过拦截器的验证,在拦截器preHandle方法就截止了
  • postHandle(request,response, Object handler,ModelAndView mv)
    • 该方法在处理器方法执行之后执行。
    • 处理器方法若最终未被执行,则该方法不会执行。
    • 由于该方法是在处理器方法执行完后执行,且该方法参数中包含 ModelAndView,所以该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向。
  • afterCompletion(request,response, Object handler, Exception ex)
    • 该方法是最后执行的方法,清除资源
    • 该方法是请求处理完成后(已经给用户发送数据了)再执行的,此时对 ModelAndView 再操作也对响应无济于事。
    • preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。

自定义拦截器

package com.maj.handler;

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

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

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法执行了");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法执行了");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion方法执行了");
    }
}

注册拦截器

    <!--注册拦截器(0或多个)-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--
                指定拦截的请求uri地址
                path:uri地址,可以使用通配符 **
                    ** :表示任意的字符,文件或者多级目录和目录中的文件
                    /user/**:表示user路径里面的全部都拦截
                    例如:http://localhost:8080/springmvc06/user/allUsers.do
                    /** :表示项目下所有路径都拦截
            -->
            <mvc:mapping path="/user/**"/>
            <!--指定自定义的拦截器-->
            <bean class="com.maj.handler.MyInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

多个拦截器

	<mvc:interceptors>
        <!--第一个拦截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.maj.handler.MyInterceptor01" />
        </mvc:interceptor>
        <!--第二个拦截器-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.maj.handler.MyInterceptor02" />
        </mvc:interceptor>
    </mvc:interceptors>

声明拦截器是按照先后顺序放入到ArrayList中,所以先声明的拦截器,会先拦截。

所以执行顺序是:

  1. 拦截器01的preHandle返回true,拦截器02的preHandle返回true

    拦截器01的`preHandle`----》拦截器02的`preHandle`----》Controller处理----》拦截器02的`postHandle`----》拦截器01的`postHandle`----》拦截器02的`afterCompletion`----》拦截器01的`afterCompletion`
    
  2. 拦截器01的preHandle返回true,拦截器02的preHandle返回false

    拦截器01的`preHandle`----》拦截器02的`preHandle`----》拦截器01的`afterCompletion`
    
  3. 拦截器01的preHandle返回false,拦截器02的preHandle返回true/false

    ----》拦截器01的`preHandle`
    

如图:
在这里插入图片描述

以上是关于4.SpringMVC核心技术的主要内容,如果未能解决你的问题,请参考以下文章

4.SpringMVC核心技术

4.SpringMVC核心技术

springmvc的特点

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

Spring知识点总结4 SpringMVC

CSP核心代码片段记录