SpringMVC框架----->(5) SpringMVC核心技术(转发和重定向、异常处理、拦截器)

SpringMVC核心技术

1、请求转发和重定向

  • 当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。

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

在这里插入图片描述

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

(1)请求转发

  • forward:

表示转发,实现 request.getRequestDispatcher(“xx.jsp”).forward()

  • 特点:

不和视图解析器一同工作,可以访问WEB-INF中的页面

  • 语法:
setViewName("forward:视图文件完整路径")
@RequestMapping(value = "/doForward.do")
public ModelAndView doSome(){
    
    
    //处理some.do请求了。 相当于service调用处理完成了。
    ModelAndView mv  = new ModelAndView();
    mv.addObject("msg","欢迎使用springmvc做web开发");
    mv.addObject("fun","执行的是doSome方法");
    //使用forward显示转发
    //mv.setViewName("forward:/WEB-INF/view/show.jsp");
    mv.setViewName("forward:/hello.jsp");
    return mv;
}

(2)请求重定向

  • redirect:

表示重定向,实现 response.sendRedirect(“xxx.jsp”)

  • 特点:

不和视图解析器一同工作,不可以访问WEB-INF中的页面

  • 语法:
setViewName("redirect:视图完整路径")
/**
 * 框架对重定向的操作:
 * 1.框架会把Model中的简单类型的数据,转为string使用,作为hello.jsp的get请求参数使用。
 *   目的是在 doRedirect.do 和 hello.jsp 两次请求之间传递数据
 *
 * 2.在目标hello.jsp页面可以使用参数集合对象 ${param}获取请求参数值
 *    ${param.myname}
 *
 * 3.重定向不能访问/WEB-INF资源
 */
@RequestMapping(value = "/doRedirect.do")
public ModelAndView doWithRedirect(String name,Integer age){
    
    
    //处理some.do请求了。 相当于service调用处理完成了。
    ModelAndView mv  = new ModelAndView();
    //数据放入到 request作用域
    mv.addObject("myname",name);
    mv.addObject("myage",age);
    //重定向
    mv.setViewName("redirect:/hello.jsp");
    //http://localhost:8080/springmvc/hello.jsp?myname=lisi&myage=22

    //重定向不能访问/WEB-INF资源
    //mv.setViewName("redirect:/WEB-INF/view/show.jsp");
    return mv;
}

2、异常处理

  • 把controller中的所有异常处理都集中到一个地方,采用的是aop的思想,把业务逻辑和异常处理代码分开实现解耦合。
使用两个注解

1)@ControllerAdvice:定义在类上面,给控制器类增强异常处理功能,使用需要在springmvc配置文件中声明组件扫描器来指定该注解所在的包名。

2)@ExceptionHandler:定义在方法上面,里面value属性表示异常的类型,当发生此类异常则由当前方法处理异常。

(1)自定义异常类

  • 定义三个异常类:NameException、AgeException、MyUserException。其中 MyUserException 是另外两个异常的父类。
public class MyUserException extends Exception{
    
    

    public MyUserException() {
    
    
        super();
    }

    public MyUserException(String message) {
    
    
        super(message);
    }
}
/**
 * 表示用户姓名有异常,抛出异常
 */
public class NameException extends MyUserException {
    
    
    public NameException() {
    
    
        super();
    }

    public NameException(String message) {
    
    
        super(message);
    }
}
/**
 * 表示年龄有异常抛出异常
 */
public class AgeException extends MyUserException{
    
    
    public AgeException() {
    
    
        super();
    }

    public AgeException(String message) {
    
    
        super(message);
    }
}

(2)定义全局异常处理类

@ControllerAdvice
public class GlobalExceptionHandler {
    
    
    //定义方法,处理异常
    @ExceptionHandler(value = NameException.class)
    public ModelAndView doNameException(Exception exception){
    
    
        //处理NameException的异常
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","姓名必须是张三,其它用户不能访问");
        mv.addObject("ex",exception);
        mv.setViewName("nameError");
        return mv;
    }

    @ExceptionHandler(value = AgeException.class)
    public ModelAndView doAgeException(Exception exception){
    
    
        //处理AgeException的异常
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","年龄必须不能大于80");
        mv.addObject("ex",exception);
        mv.setViewName("ageError");
        return mv;
    }

    //处理其它异常
    @ExceptionHandler
    public ModelAndView doOtherException(Exception exception){
    
    
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","发生其它异常");
        mv.addObject("ex",exception);
        mv.setViewName("defaultError");
        return mv;
    }
}

(3)在springmvc配置文件中声明组件扫描器

<!--声明组件扫描器:@Controller所在包名-->
<context:component-scan base-package="com.hcz.controller" />
<!--声明组件扫描器:@ControllerAdvice所在包名-->
<context:component-scan base-package="com.hcz.handler"/>
<!--注解驱动-->
<mvc:annotation-driven/>

(4)创建处理异常结果页面

<body>
ageError.jsp<br/>
提示信息:${msg}<br>
系统异常信息:${ex.message}
</body>
<body>
    nameError.jsp<br/>
    提示信息:${msg}<br>
    系统异常信息:${ex.message}
</body>
<body>
    defaultError.jsp<br/>
    提示信息:${msg}<br>
    系统异常信息:${ex.message}
</body>

3、拦截器

(1)拦截器概述:

  • 1)拦截器是springmvc中的一种,需要实现HandlerInterceptor接口。
  • 2)拦截器和过滤器类似,功能方向侧重点不同。 过滤器是用来过滤器请求参数,设置编码字符集等工作。拦截器是拦截用户的请求,做请求做判断处理的。
  • 3)拦截器是全局的,可以对多个Controller做拦截。 一个项目中可以有0个或多个拦截器, 他们在一起拦截用户的请求。
  • 4)拦截器常用在:用户登录处理,权限检查, 记录日志。

(2)拦截器的使用步骤:

1、定义类实现HandlerInterceptor接口
2、在springmvc配置文件中,声明拦截器, 让框架知道拦截器的存在。

(3)拦截器的执行时间:

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

(4)自定义拦截器

  • 需要实现 HandlerInterceptor 接口,而该接口中含有三个方法:
  • preHandle(request,response, Object handler):

preHandle返回true :请求是通过了拦截器的验证,可以执行处理器方法


拦截器的MyInterceptor的preHandle()
=执行MyController中的doSome方法=
拦截器的MyInterceptor的postHandle()
拦截器的MyInterceptor的afterCompletion()

preHandle返回false :请求没有通过拦截器的验证,请求到达拦截器就截止了, 请求没有被处理

特点
1、方法在控制器方法(MyController的doSome)之前先执行的。用户的请求首先到达此方法
2、在这个方法中可以获取请求的信息, 验证请求是否符合要求。可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。
如果验证失败,可以截断请求,请求不能被处理。
如果验证成功,可以放行请求,此时控制器方法才能执行。

  • postHandle(request,response, Object handler,modelAndView):

特点
1、该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。
2、该方法参数中包含 ModelAndView,所以该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向

  • afterCompletion(request,response, Object handler, Exception ex):

当 preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法,一般做资源回收工作的,它是最后执行的方法。

(5)springmvc注册拦截器

<!--声明拦截器: 拦截器可以有0或多个-->
<mvc:interceptors>
    <!--声明第一个拦截器-->
    <mvc:interceptor>
        <!--指定拦截的请求uri地址
            path:就是uri地址,可以使用通配符 **
                  ** : 表示任意的字符,文件或者多级目录和目录中的文件
            http://localhost:8080/myweb/user/listUser.do
            http://localhost:8080/myweb/student/addStudent.do
        -->
        <mvc:mapping path="/**"/>
        <!--声明拦截器对象-->
        <bean class="com.hcz.handler.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

(6)一个拦截器工作流程图:

在这里插入图片描述

(7)多个拦截器工作流程图:

在这里插入图片描述

从图中可以看出,只要有一个 preHandle()方法返回 false,则上部的执行链将被断开,
其后续的处理器方法与 postHandle()方法将无法执行。但无论执行链执行情况怎样,只要方法栈中有方法,即执行链中只要有 preHandle()方法返回 true,就会执行方法栈中的 afterCompletion()方法,最终都会给出响应。

(8)拦截器和过滤器的区别

1、过滤器是servlet中的对象, 拦截器是框架中的对象;过滤器实现Filter接口的对象, 拦截器是实现HandlerInterceptor
2、过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的;拦截器是用来验证请求的,能截断请求。
3、过滤器是在拦截器之前先执行的。
4、过滤器是tomcat服务器创建的对象;拦截器是springmvc容器中创建的对象
5、过滤器是一个执行时间点;拦截器有三个执行时间点
6、过滤器可以处理jsp,js,html等等;拦截器是侧重拦截对Controller的对象。 如果你的请求不能被中央调度器DispatcherServlet接收, 这个请求不会执行拦截器内容
7、拦截器拦截普通类方法执行,过滤器过滤servlet请求响应

(9)拦截器验证登录项目

  • 案例:只有经过登录的用户方可访问处理器,否则,将返回“无权访问”提示

实现步骤:
1、新建maven
2、修改web.xml注册中央调度器
3、新建index.jsp发起请求
4、创建MyController处理请求
5、创建结果show.jsp
6、创建一个login.jsp,模拟登录(把用户信息保存到session中)
7、创建一个logout.jsp,模拟退出系统(从session中删除数据)
8、创建拦截器,从session中获取用户的登录数据,验证是否访问系统
9、创建一个验证的jsp,如果验证视图,给出提示
10、创建springmvc配置文件
1) 声明组件扫描器
2) 声明拦截器

A、新建index.jsp页面
<body>
    <p>一个拦截器</p>
    <form action="some.do" method="post">
        姓名:<input type="text" name="name"> <br/>
        年龄:<input type="text" name="age"> <br/>
        <input type="submit" value="提交请求">
    </form>
</body>
B、创建Controller处理请求
@Controller
public class MyController {
    
    

    @RequestMapping(value = "/some.do")
    public ModelAndView doSome(String name,Integer age)  {
    
    
        System.out.println("=====执行MyController中的doSome方法=====");
        //处理some.do请求了。 相当于service调用处理完成了。
        ModelAndView mv  = new ModelAndView();
        mv.addObject("myname",name);
        mv.addObject("myage",age);
        mv.setViewName("show");
        return mv;
    }
}
C、创建结果页面show.jsp
<body>
    <h3>/WEB-INF/view/show.jsp从request作用域获取数据</h3><br/>
    <h3>myname数据:${myname}</h3><br/>
    <h3>myage数据:${myage}</h3>
</body>
D、创建登录页面login.jsp
<body>
    模拟登录,zs登录系统
    <%
        session.setAttribute("name","zs");
    %>
</body>
E、创建退出系统页面logout.jsp
<body>
    退出系统
    <%
        session.removeAttribute("name");
    %>
</body>
F、创建拦截器类
/**
 * 拦截器类:拦截用户的请求
 */
public class MyInterceptor implements HandlerInterceptor {
    
    

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
        System.out.println("111111-拦截器的MyInterceptor的preHandle()");
        String loginName = "";
        //从session中获取name数据
        Object attr = request.getSession().getAttribute("name");
        if (attr != null){
    
    
            loginName = (String)attr;
        }
        //判断登录的账号是否符合要求
        if (!"zs".equals(loginName)){
    
    
            //不能访问
            request.getRequestDispatcher("/tips.jsp").forward(request,response);
            return false;
        }
        return true;
    }
}
G、创建还没有登录的提示页面
<body>
  tips.jsp  非张三不能访问
</body>
H、修改springmvc配置文件
<!--声明组件扫描器-->
<context:component-scan base-package="com.hcz.controller" />

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

<!--声明拦截器: 拦截器可以有0或多个
    在框架中保存多个拦截器是ArrayList,
    按照声明的先后顺序放入到ArrayList
-->
<mvc:interceptors>
    <!--声明第一个拦截器-->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <!--声明拦截器对象-->
        <bean class="com.hcz.handler.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>
I、项目测试

1)http://localhost:8080/springmvc/index.jsp
因为zs用户没有登录,所以不能响应index.jsp页面请求
2)http://localhost:8080/springmvc/login.jsp
3)http://localhost:8080/springmvc/index.jsp
因为zs用户已经登录,所以能响应index.jsp页面请求并转发到show.jsp页面
4)http://localhost:8080/springmvc/login.jsp
5)http://localhost:8080/springmvc/logout.jsp
6)http://localhost:8080/springmvc/index.jsp
当zs用户退出系统后也不能再响应index.jsp页面了

猜你喜欢

转载自blog.csdn.net/hcz666/article/details/113988586
今日推荐