zoukankan      html  css  js  c++  java
  • SpringMVC

    一、异常处理器

    springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。(它负责捕获,将异常放到我们自己编写的处理类中)

    Spring3.0中对异常的处理方法一共提供了两种:

    ① 实现HandlerExceptionResolver接口:可以实现全局异常控制,并且Spring已经提供了一个默认的实现类SimpleMappingExceptionResolver;
    ② 使用@ExceptionHandler注解:可以在Controller内部实现更个性化点异常处理方式,灵活性更高。

    1、异常处理器思路

    系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

    系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,SpringMVC提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。如下图:

    2、异常处理器使用

    测试准备

     - 错误页面:用于显示异常信息

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        <h1>系统发生异常了!</h1>
        <br />
        <h1>异常信息</h1>
        <br />
        <h2>${error }</h2>
    </body>
    </html>
    error.jsp

    - 自定义异常类:

    为了区别不同的异常,通常根据异常类型进行区分,这里我们创建一个自定义系统异常。
    如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。
    package com.sikiedu.exception;
    
    public class MyException extends Exception {
        // 错误信息
        private String msg;
    
        public MyException(String msg) {
            super();
            this.msg = msg;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
    }
    MyException.java

    - Service异常:发生异常,将异常向上抛出到控制器中

    package com.sikiedu.service;
    
    import 略...
    
    /**
     * @author*/
    @Service
    public class ItemServiceImpl implements ItemService {
    
        @Autowired()
        private ItemMapper itemMapper;
    
        @Override
        public List<ItemInfo> selectByVo(ItemInfoVo vo) throws MyException {
    
            // 随机抛出一个异常
            int x = (int) (Math.random() * 10 - 1);
            if (x % 2 == 0) {
                // 制造运行时异常
                int i = 1 / 0;
            } else {
                // 抛出自定义异常
                throw new MyException("【操作没错 - 但我就是不给你通过】");
            }
            return itemMapper.selectByVo(vo);
        }
    }
    ItemServiceImpl.java

    (1) 通过实现HandlerExceptionResolver接口完成异常处理

     - 编写异常处理器

    package com.sikiedu.exception;
    
    import 略...
    
    /**
     * 自定义异常处理器实现
     * @author*/
    public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    
        /**
         * @param request、response:请求信息
         * @param obj:异常对象的(全包名+类名+方法名)
         * @param e:异常信息
         * @return mav:模型视图
         */
        @Override
        public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object obj,Exception e) {
    
            ModelAndView mav = new ModelAndView();
            // 获取异常信息
            String message = "";
            // 判断异常信息类型
            if (e instanceof MyException) {
                // 执行自定义异常处理 - 需要强制类型转换
                message = "HandlerExceptionResolver - 自定义异常    <br/>" + ((MyException) e).getMsg() + "<br/>" + e.getStackTrace()[0];
            } else {
                // 运行时异常
                message = "HandlerExceptionResolver - 运行时异常    <br/>" + e.getMessage() + "<br/>" + e.getStackTrace()[0];
            }
            // 将异常信息输出到错误页面
            mav.addObject("error", message);
            // 设置要跳转的视图名称
            mav.setViewName("error");
    
            return mav;
        }
    }

     - springmvc.xml配置异常处理器:所有的Controller出现的异常, 都会在这个异常处理器中查找对应的处理方法

    <!-- 配置异常处理器 -->
    <bean class="com.sikiedu.exception.MyHandlerExceptionResolver" />

     - Controller控制器:捕获异常 交给异常处理器

    package com.sikiedu.controller;
    
    import 略...
    
    /**
     * @author*/
    @Controller
    @RequestMapping(value = "/item/")
    public class ItemController {
    
        @Autowired
        private ItemService itemService;
    
        @RequestMapping(value = "selectByVo.do")
        public String selectByVo(ItemInfoVo vo, Model model) throws MyException { // 异常发生,抛出
    List<ItemInfo> itemList = itemService.selectByVo(vo); model.addAttribute("itemList", itemList); return "item_list"; } }

     - 测试结果

     

    (2) 通过@ExceptionHandler注解实现异常处理

     - 开启注解:SpringMVC中默认是没有加装载HandlerExceptionResolver

    <mvc:annotation-driven />

     - Controller控制器:捕获异常 交给异常处理器

    如果RequestMapping所注解的方法出现了异常,则自动寻找当前类中是否存在被@ExceptionHandler注解的方法,如果存在,则执行。
    使用@ExceptionHandler处理,该注解只能处理当前控制器中的异常。 这个方法中可以加入Exception类型的参数,该参数即对于发生的异常对象。
    package com.sikiedu.controller;
    
    import 略...
    
    /**
     * @author*/
    @Controller
    @RequestMapping(value = "/item/")
    public class ItemController {
    
        @Autowired
        private ItemService itemService;
    
        @RequestMapping(value = "selectByVo.do")
        public String selectByVo(ItemInfoVo vo, Model model) throws MyException { // 发生异常,抛出
    List<ItemInfo> itemList = itemService.selectByVo(vo); model.addAttribute("itemList", itemList); return "item_list"; } // 处理运行时异常、自定义异常在,这个方法中可以加入Exception类型的参数,该参数即对于发生的异常对象
    @ExceptionHandler(value = { MyException.class, RuntimeException.class }) public String selectException(Exception e, Model model) { // 获取异常信息 String message = ""; // 判断异常信息类型 if (e instanceof MyException) { // 执行自定义异常处理 - 需要强制类型转换 message = "@ExceptionHandler处理 - 自定义异常 <br/>" + ((MyException) e).getMsg() + "<br/>" + e.getStackTrace()[0]; } else { // 运行时异常 message = "@ExceptionHandler处理 - 运行时异常 <br/>" + e.getMessage() + "<br/>" + e.getStackTrace()[0]; } // 将异常信息输出到错误页面 model.addAttribute("error", message); // 视图名 return "error"; } }

     - 测试结果:

    (3) 通过(@ControllerAdvice + @ ExceptionHandler)注解配置全局异常 - 推荐使用★

    @ControllerAdvice:相当于是AOP,面向切面,给每一个controller都横插了该处理类。
    通过@ControllerAdvice注解使@ExceptionHandler异常处理注解应用到所有使用@RequestMapping的方法上;

      - 开启注解:SpringMVC中默认是没有加装载HandlerExceptionResolver

    <mvc:annotation-driven />

     - 全局异常处理类:

    package com.sikiedu.exception;
    
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    
    /**
     * 自定义全局异常类
     * @author*/
    @ControllerAdvice
    public class MyGlobalException {
    
        // 处理运行时异常
        @ExceptionHandler(value = RuntimeException.class)
        String runtime(RuntimeException e, Model model) {
            // 将异常信息输出
            model.addAttribute("error", "@ControllerAdvice+@ExceptionHandler处理 - 运行时异常    </br>" + e.getMessage() + "</br>" + e.getStackTrace()[0]);
            // 视图名
            return "error";
    
        }
        // 处理自定义异常
        @ExceptionHandler(value = MyException.class)
        String myHandler(MyException e, Model model) {
            // 将异常信息输出
            model.addAttribute("error","@ControllerAdvice+@ExceptionHandler处理 - 自定义异常    </br>" + e.getMsg() + "</br>" + e.getStackTrace()[0]);
            // 视图名
            return "error";
        }
    }

     - Controller控制器:捕获异常 交给异常处理器

    package com.sikiedu.controller;
    
    import 略...
    
    /**
     * @author*/
    @Controller
    @RequestMapping(value = "/item/")
    public class ItemController {
    
        @Autowired
        private ItemService itemService;
    
        @RequestMapping(value = "selectByVo.do")
        public String selectByVo(ItemInfoVo vo, Model model) throws MyException { // 发生异常,抛出
            List<ItemInfo> itemList = itemService.selectByVo(vo);
            model.addAttribute("itemList", itemList);
            return "item_list";
        }
    }

     - 测试结果

    二、拦截器Interceptor

    SpringMVC中的拦截器可以是运行在控制器(Controller)之前的组件,可以设置拦截器应用于哪些请求路径,当发生这些请求时,拦截器会自动执行,在执行过程中,可以对请求相关数据进行判断,选择阻止继续向后执行,或选择放行。

    注意:拦截器是一个若干种请求都会经历的执行过程,但是,并不一定需要阻止继续运行,只要是若干种请求都需要做相同的事情,也许每种请求的处理过程都是选择放行,也可以使用拦截器。

     - 作用:权限检查,日志记录,性能检测等;

    用户可以自己定义一些拦截器来实现特定的功能。例:访问特定页面前验证用户是否登陆等;

     - 拦截器链:HandlerExecutionChain

    拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

     - 拦截器与过滤器的区别:

    ① 过滤器:是 servlet 规范中的一部分, 任何 java web 工程都可以使用。
      拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。
    ② 过滤器:在 url-pattern 中配置了/*之后,可以对所有要访问的资源拦截。
      拦截器它是只会拦截访问的控制器方法,如果访问的是 jsp、html、css、image 或者 js 是不会进行拦截的。

    1、主要相关类和方法

    ● HandlerExecutionChain:该类主要由 handler 和 handler interceptors 组成

    HandlerMapping类通过getHandler方法会调用到该类

    ● HandlerInterceptor

    Spring MVC中对于一个请求可以添加多个拦截器,而这个拦截器集合中会链式调用这些拦截器。每个拦截器会执行固定顺序的方法,而这些方法就定义在HandlerInterceptor类中。

    这是拦截器的一个基础接口,里面有三个方法

     - boolean preHandle ( HttpServletRequest request,HttpServletResponse response,Object handler ) throws Exception

    使用时机:预处理,在拦截方法前执行
    应用场景:可以在该方法中放入一些初始化的操作,比如权限验证,日志管理等 注意:该方法的返回值是boolean类型,若返回值为true,则继续调用后面的拦截器和目标方法,若返回为false,则不会调用后面的拦截器和目标方法,表示请求结束

     - void postHandle ( HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView )throws Exception

    使用时机后处理,在调用目标方法之后,渲染视图之前被调用。具体来说,是在调用了Controller中定义的方法之后,但在DispatcherServlet 处理视图返回渲染结果之前被调用。
    应用场景:根据使用的时机就可以知道,该方法可以对Controller处理之后ModelAndView进行操作
    注意:当有多个interceptor的时候,对于preHandler的调用顺序和postHandler的调用顺序是恰恰相反的。

     - void afterCompletion ( HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex ) throws Exception

    使用时机:渲染后处理,在页面渲染后执行;
    应用场景:释放资源

    2、拦截器的执行流程

    3、开发拦截器

    (1) 自定义拦截器

    自定义拦截器必须要实现HandlerInterceptor接口。

    public class LoginInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            System.out.println("LoginInterceptor.preHandle()");
            return false;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println("LoginInterceptor.postHandle()");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println("LoginInterceptor.afterCompletion()");
        }
    }
    View Code

    接口方法:

    ① preHandle方法:在调用处理器之前调用该方法,如果该方法返回true则请求继续向下进行,否则请求不会继续向下进行,处理器也不会调用;
    ② postHandle方法:在调用完处理器后调用该方法;
    ③ afterCompletion方法:只要该拦截器中的preHandle方法返回true,该方法就会被调用;

    在拦截器的3个方法中,只有preHandle()方法是运行在控制器(Controller)之前的,另2个方法是运行在控制器之后的,所以,只有preHandle()具有真正意义的“拦截”功能,

    该方法的返回值是boolean类型的,当返回true时表示放行,返回false时将阻止继续向后执行,即控制器并不会被执行;

    public class LoginInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
    
            String URL = request.getRequestURI();
            if (!URL.contains("login")) {
                // 不是登录请求 - 拦截
                Object user = request.getSession().getAttribute("user");
                if (user == null) {
                    // 没用登录 - 重定向到登录页面/请求
                    response.sendRedirect(request.getContextPath() + "/login/login.do");
                    return false;
                }
            }
            return true;
        }
    }
    !!!注意:即使已经决定了重定向,还是需要return false;否则处理流程会继续向执行,控制器中的方法还是会被调用,达不到阻止运行的效果!

    (2) 在SpringMVC核心配置文件中注册自定义拦截器

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <!-- 不拦截静态资源 -->
            <mvc:exclude-mapping path="/admin/**/*.*" />
            <bean class="com.sikiedu.interceptor.LoginInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors>

     - 配置若干个拦截器

    允许使用若干个拦截器,形成拦截器链,即某个请求可能需要经过多个拦截器,仅当每个拦截器都放行时,才会执行控制器中的方法!

    在配置文件中,配置的先后顺序决定了多个拦截器的执行顺序

    <mvc:interceptors>
        <!-- 拦截器1 -->
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="com.sikiedu.interceptor.MyInterceptor1" />
        </mvc:interceptor>
        <!-- 拦截器2 -->
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="com.sikiedu.interceptor.MyInterceptor2" />
        </mvc:interceptor>
    </mvc:interceptors>
    View Code

     - 配置若干个拦截路径

    <mvc:interceptor>
        <!-- 拦截路径 -->
        <mvc:mapping path="/main/index.do" />
        <mvc:mapping path="/user/password.do" />
        <mvc:mapping path="/user/info.do" />
        <mvc:mapping path="/user/handle_password.do" />
        <mvc:mapping path="/user/handle_info.do" />
        <!-- 拦截器类 -->
        <bean class="com.sikiedu.interceptor.LoginInterceptor"></bean>
    </mvc:interceptor>
    View Code

     - 使用通配符 * 拦截一个星号 * 只能匹配一层路径

    例如:/main/*可以匹配上/main/index.do,也可以匹配/main/hello.do,但是,不可以匹配上/main/a/index.do!

    <mvc:interceptor>
        <!-- 拦截路径 -->
        <mvc:mapping path="/main/*" />
        <!-- 拦截器类 -->
        <bean class="com.sikiedu.interceptor.LoginInterceptor" />
    </mvc:interceptor>
    View Code

    如果一定要匹配若干层路径,必须使用两个星号 **

    例如:配置为/main/**,可以匹配上/main/index.do,也可以匹配/main/a/hello.do,甚至可以匹配/main/a/b/c/d/hello.do

    即无视路径中后续的层级。

    <mvc:interceptor>
        <!-- 拦截路径 -->
        <mvc:mapping path="/main/**" />
        <mvc:mapping path="/user/**" />
        <!-- 拦截器类 -->
        <bean class="com.sikiedu.interceptor.LoginInterceptor" />
    </mvc:interceptor>
    View Code

     - 排除指定的路径:如果通配符匹配的路径过多,还可以排除某些请求路径

    <mvc:interceptor>
        <!-- 拦截路径:黑名单 -->
        <mvc:mapping path="/main/**" />
        <mvc:mapping path="/user/**" />
        <!-- 例外路径:白名单 -->
        <mvc:exclude-mapping path="/user/reg.do" />
        <mvc:exclude-mapping path="/user/handle_reg.do" />
        <mvc:exclude-mapping path="/user/login.do" />
        <mvc:exclude-mapping path="/user/handle_login.do" />
        <!-- 拦截器类 -->
        <bean class="com.sikiedu.interceptor.LoginInterceptor"></bean>
    </mvc:interceptor>
    View Code

    也就是说,凡/user/下的路径都会经过该拦截器,但是,/user/login.do是不被处理的!添加到“例外”中的路径,在请求时,并不是拦截器直接放行,而是拦截器根本就不执行!

    三、总结 - 拦截器规则

    ① preHandle预处理:--------------根据拦截器定义的顺序,正向执行;

    ② postHandle后处理:-------------根据拦截器定义的顺序,逆向执行;

    ③ afterCompletion渲染后处理:---根据拦截器定义的顺序,逆向执行;

    ④ postHandle预处理:-------------所有拦截器都返回成功调用;

    ⑤ atterCompletion渲染后处理:---preHandle返回true调用;

  • 相关阅读:
    在线|九月月考选填题
    函数$f(x)=e^xpm e^{-x}$相关
    偶函数性质的推广
    2020年全国卷Ⅱ卷文科数学选填题解析版
    2020年全国卷Ⅱ卷文科数学解答题解析版
    待定系数法
    特殊方法求函数解析式
    phd文献阅读日志-4.1
    phd文献阅读日志-1.2~3.2(1.2,2.1,2.2,3.1,3.2)
    完美解决linux下vim在终端不能用鼠标复制的问题
  • 原文地址:https://www.cnblogs.com/Dm920/p/12148231.html
Copyright © 2011-2022 走看看