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

框架技术----springMVC收尾【异常、拦截器】

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

框架技术----springMVC收尾【异常、拦截器】

springMVC

内容管理

集中处理异常

创建页面、控制器方法,异常类定义一个集中处理异常类,类上面加上@ControllerAdvice;类方法上加上@ExceptionHandler创建异常的页面 拦截器 实现HandlerInterceptor

声明一个拦截器mvc配置文件声明拦截器,指定url, mvc:interceptors第一个方法:prehandle第二个方法: postHandle第三个方法:afterCompletion多个拦截器的执行顺序 【拦截器拦截器、过滤器的区别 拦截器实例: 用户权限SpringMVC执行流程

javaWeb—SSM中最后控制层MVC框架


异常处理和拦截器


SSM整合其实只是spring和mybatis的整合,springmvc是spring的子结构,所以spirng的配置文件对于mvc来说是可见的;forward和redirct显式路径可以在配置视图解析器的情况下寻找其他的位置的资源

开发最开始就是编写好框架结构:创建好配置文件,加入要使用到的依赖,创建包的结构;业务都是在结构建立好之后才开始编写;

集中处理异常

之前没有使用框架之前,要想处理异常只能分开处理,也就是说每一个方法都需要单独处理异常,比如在A方法中try----catch;在B方法中也try----catch,加上try–catch就代码就很凌乱;MVC利用AOP的思想在一个固定的位置统一处理异常,这样异常发生变动就不用大段修改,并且代码就不需要大量的try catch;代码的结构就更清晰

因为这里的try catch实际上也是属于交叉业务逻辑的,所以将异常使用AOP的思想剥离出来

SpringMVC框架采用的是统一、全局的异常处理,把controller中的所有的异常处理都几种到一个地方,使用的aop的思想,把业务逻辑和异常处理代码分开,解耦合,主要依赖的是两个注解: @ExceptionOnHandler; @ControllerAdvice

这里就演示一下mvc框架使用之后新的异常的处理方式

创建页面、控制器方法,异常类

这里就基于之前建立的项目的基础上来进行操作; 这里建立两个异常类

 

方法返回值为modelAndView的时候实现请求转发
用户名
年龄

自定义一个异常类UserException,定义两个子类NameException和AgeException

集中统一处理异常和控制器的创建类似

package Jning.exception;


public class AgeException extends UserException {
    public AgeException() {
        super();
    }

    public AgeException(String message) {
        super(message);
    }
}
//这里就简单定义了两个异常类

最后简单定义控制器方法,让出现异常的时候抛出,比如姓名不是张三,年龄大于80都会抛出异常

    @RequestMapping(value ="/some.do",produces = "text/html;charset=utf-8")
    public ModelAndView doSome(String user,int age) throws UserException {  //  抛出父类的异常便于统一处理,不再需要try catch
        ModelAndView mv = new ModelAndView();
        if(!"张三".equals(user)) {
            throw new NameException("非法的用户,拒绝访问");
        }
        if(age > 30) {
            throw new AgeException("不是你这个年龄段该访问的");
        }

        mv.addObject("user",user);
        mv.addObject("age",age);
        mv.setViewName("forward:/static/html/test.jsp");
        return mv;
    }

如果按照之前的做法,那么就不会在方法的上面进行异常的抛出,而是直接使用try catch环绕,自己处理,但是加入异常之后很凌乱;并且按照之前的操作,如果直接像这里直接抛出,那么调用者方法就要处理,如果一直都不处理,那么就是JVM处理,直接终止程序;

使用MVC框架就不需要再在某个调用的方法中来处理异常了,而是直接在框架中进行处理

定义一个集中处理异常类,类上面加上@ControllerAdvice;

这里相当于也是一个创建对象,只是不是创建控制层或者其他的对象,而是创建一个advice对象;所以要需要扫描这个包Exception; 同时要识别类中的注解,还需要注解驱动,之前加过,不需要加,所以只是需要在MVC的配置文件中额外加上一个扫描器

类方法上加上@ExceptionHandler

定义的方法就是处理某些异常的

package Jning.handler;

import Jning.exception.AgeException;
import Jning.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;



@ControllerAdvice
public class GlobalExceptionHandler {

    //定义方法,处理发生的异常;这个方法的定义和之前的处理器方法的定义相同,比如ModelAndView、String等

    
    @ExceptionHandler(value = NameException.class)
    public ModelAndView doNameException(Exception e) {
        
        //这里就简单给用户进行提示
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","姓名必须是张三,其他用户拒绝访问");
        mv.addObject("error",e);
        mv.setViewName("forward:/static/html/error.jsp");
        return mv;
        //发生异常之前的页面
    }

    //处理AagException
    @ExceptionHandler(value = AgeException.class)
    public ModelAndView doAgeException(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","你好,你的年龄不能访问这个网页");
        mv.addObject("error",e);
        mv.setViewName("forward:/static/html/error.jsp");
        return mv;
    }

    //捕获其他的未知的异常,因为不一定会发现,不知名的异常
    @ExceptionHandler
    public ModelAndView doOtherException(Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","发生了未知的异常");
        mv.addObject("error",e);
        mv.setViewName("forward:/static/html/error.jsp");
        return mv;
    }
}

当控制层抛出异常的时候,会自动跳转到这个异常处理器中,会将异常的类型和处理器方法进行比较,当比较成功的时候就执行对应的处理方法,如果处理失败,就会执行默认的,类似于if else中的default中的操作; 只能由一个没有value属性的异常处理器方法代表其他的未知的异常的处理方法

创建异常的页面

这里就简单创建一个异常处理的页面,发生异常的时候会处理器方法,从而跳转错误页面,而不会去执行控制层方法中的页面

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


    Error


    

出错啦


对不起, ${msg}
错误的信息: ${error.message}

执行的过程就是:

用户发送请求跳转到处理器方法中执行,如果处理器方法抛出异常,【系统发现由集中处理异常的类,@ControllerAdvise],然后就会停止执行控制器方法,而是将请求转发到异常处理的类从上向下依次寻找和抛出的异常类的类型相同的@ExceptionHandler的value;进入执行第一个找到的方法、所以就算同时发生了很多异常,还是只是会执行第一个匹配到的方法;然后正常给用户反馈;都没有匹配上就会进入执行未知的异常的错误信息

其实这里ConrollerAdivice就是使用的AOP对Controller进行增强,相当于是动态代理,所以发生异常执行的方法其实还是一个控制器,能够处理请求和发送请求给结果页面

这里可以看一下结果

------------张三  25------------------
因为不是JSP页面,并且不在WEB-INF下
访问的用户:张三
年龄:25

----------------张古  25---------------------------
因为不是JSP页面,并且不在WEB-INF下
访问的用户:张三
年龄:25

-------------------张三  39---------------
对不起, 你好,你的年龄不能访问这个网页
错误的信息: 不是你这个年龄段该访问的


------------张三  null-----------------------
对不起, 发生了未知的异常
错误的信息: Failed to convert value of type 'java.lang.String' to required type 'int'; nested exception is java.lang.NumberFormatException: For input string: ""

第三个就是因为接收的参数是空,然后框架内部进行自动类型转换,Integer.ValueOf();null不能转为int类型,这里就抛出了异常【发生了异常】

所以其实就是控制层方法发生异常的时候就会自动执行集中统一处理异常类的方法,这个类的定义和控制器差不多,使用的类的注解是ControllerAdvice;方法上的注解是@ExceptionHandler,属性value代表的是Exception的类型,最后会放一个没有value的代表的是default,发生的其他的没有识别的异常的时候的处理【控制台就不会报错了,因为会继续执行,不会因为异常而不能执行接下来的操作】不再需要在controller中使用try–catch处理异常,集中处理所有的控制层的异常;AOP

其实如果不想过多处理异常,直接定义一个default异常处理方法即可

项目中除了之前的dao、service、controller、entity包之外,多了exception来定义自定义的异常类;handler【处理器】包中存放的就是对于控制层/用户请求的处理器,比如全局异常集中处理器,拦截处理器等

拦截器 实现HandlerInterceptor

MVC中的interceptor拦截器是非常重要的,主要作用是拦截指定的用户请求,并进行相关的预处理和后处理,拦截的时间点 : 处理器映射器根据用户所提交的请求映射出索要执行的处理器类,找到该处理器类的处理适配器,在处理器适配器执行处理器之前; 处理器映射出所要执行处理器类时,已经将拦截器和处理器组合为一个处理器执行链,并交给了中央调度器

这里使用的思想还是AOP

    拦截器和过滤器类似,但是功能方向侧重不同 : 过滤器是过滤请求参数的,设置编码字符集等, 拦截器拦截请求,做请求得判断处理【在原生servlet中,应该都是filter来处理请求】

    拦截器是全局的,可以对多个controller进行拦截,一个项目可以有0-多个拦截器,一起拦截用户的请求;拦截器经常用在 : 用户的登录处理,权限的检查,记录日志等……

    拦截器拦截的时间 : 一共有3个时间

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

    拦截器中的页面路径不能使用视图解析器,视图解析器只是为控制器工作【异常集中处理只是控制增强,是同一个类型】,拦截器在控制器方法之前的prehadnle不能使用

使用拦截器很简单 : 首先就是声明拦截器 :创建一个类实现HanlerInterceptor接口 ; 在Spirng配置文件中声明拦截器;让springMVC知道拦截器的存在

声明一个拦截器

这里就还是使用之前的异常处理的类,这里就不抛出异常了; 创建一个普通的类,实现HandlerInterceptor接口; 接口中有3个方法都要实现

接口中的3个方法分别代表的是不同的执行时机;preHandle代表的是在处理器方法执行之前进行拦截;postHandle代表的是在处理器方法执行后进行拦截【这个时候执行执行完了方法,中央调度器还没有将mv进行请求转发】;afterCompletion是在请求处理完成之后执行,也就是放回的结果页面的请求进行了forward之后就会进行拦截

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

default类型的,不用全部都实现,可以选择性实现【依据功能】

preHandle : @param handler Object类型,表示被拦截的控制器对象【controller】 ;是整个项目的门户;就是之前的原生servlet的filter来处理,登录验证等

这个方法是在控制器方法执行之前先执行的,也就是controller中的do方法,用户的请求先到达这里返回值为bool类型,true就和之前的过滤器放行一样,true就是不执行控制器方法,被拦截后可以通过request重定向到其他的页面给用户进行反馈;表明请求被拦截在这个方法中可以获取请求的信息,验证请求是否符合请求,可以验证用户是否登录,是否有权限访问, 如果验证成功,放行请求,请求到达处理器对象进行处理;如果验证失败,请求不能进行处理

postHandle : 参数也有控制器对象

后处理方法,是在控制器方法执行之后才会触发执行验证所以可以获取处理器方法的返回值modelAndView对象,可以修改这个对象的数据和视图,主要是对于原来的结果进行二次修正

afterCompletion : Exception: 表示程序所发生的异常

最后执行的方法,在请求处理完成后执行的,框架中规定的是当视图处理完成后,对视图进行了forward,那么就认为请求处理完成一般是用作资源的会后工作的,程序执行过程中创建的对象,在这里可以进行删除,把占用的内存回收

执行的时间 : 首先是prehandle ----> 控制层处理器方法doSome —> postHandle —> afterCompletion

mvc配置文件声明拦截器,指定url, mvc:interceptors

在mvc配置文件中声明拦截器,这里可以声明一个或者多个拦截器,使用的标签是interceptors;这里代表的就是多个拦截器,其中可以声明单个的拦截器interceptor

单个拦截器中,mapping表示就是拦截的url,path指定路径,可以通配

    
        
        
            
             

可以看到,这样就可以对于用户的请求进行拦截,通过之后才会放行,需要注意路径没有开启视图解析,要按照之前的普通的路径写法

第二个方法: postHandle

这个方法主要用于修正视图的

package Jning.handler;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

public class MyInterceptorHandler implements HandlerInterceptor {
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("执行拦截器的postHandle方法");
        if(modelAndView != null) {
            //说明处理器方法是返回了视图和数据,那么使用postHandle方法修改数据
            modelAndView.addObject("date",new Date().getTime());
        }
    }
}
--------------------------------------result----------------------------------
访问的用户:张三
年龄:25
使用拦截器修正后的数据: 日期 : 1642418618754

最常用的还是第一个pre方法,这个修正的方法主要是后期如果突然想修改一下数据的时候就使用这种方式进行拦截

第三个方法:afterCompletion

这个方法是在请求处理完成之后才会执行,也就是当处理器方法执行完,框架将请求forward到结果页面的时候就是这个方法执行的时机。一般用于资源的释放【代表一次请求的完成】

这里简单使用以下,计算一次请求得耗时;在第一个preHandle方法计算开始时间,该方法计算结束时间,相差即为执行时间

package Jning.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 MyInterceptorHandler implements HandlerInterceptor {

    private long  bTime;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //现在使用拦截器完成刚刚的异常的处理,如果不是张三不允许访问
        System.out.println("执行了拦截器preHandle方法");
        bTime = System.currentTimeMillis();
        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方法");
        long eTime = System.currentTimeMillis();
        System.out.println("该次请求执行时间是 :" + (eTime - bTime) + " ms");
    }
}

可以看看结果

执行了拦截器preHandle方法
执行了controller得doSome方法
执行拦截器的postHandle方法
执行了拦截器得afterCompletion方法
该次请求执行时间是 :780 ms
执行了拦截器preHandle方法
执行了controller得doSome方法
执行拦截器的postHandle方法
执行了拦截器得afterCompletion方法
该次请求执行时间是 :2 ms

第一次请求因为需要浏览器加载界面,解析资源,所以耗时较长,第二次时间骤降到2ms;这里还可以看到执行的顺序就是prehandle—> doSome—> postHandle—>afterCompletion

多个拦截器的执行顺序 【拦截器

拦截器在mvc中,框架中是使用的ArrayList的,按照声明的顺序依次放入ArrayList集合,所以如果在路径相同的情况下,先声明的先执行

这可以声明再声明一个和上面一模一样的拦截器,这个新的拦截器为2

package Jning.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 MyInterceptorHandler2 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //现在使用拦截器完成刚刚的异常的处理,如果不是张三不允许访问
        System.out.println("执行了拦截器2preHandle方法");
        return true;
    }

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

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

这里注意一定要将拦截器注册,不然mvc不能识别

    
        
        
            
            中央调度器将3中获取的ModelAndView交给视图解析器对象;视图解析器:组成视图的完整视图,前缀、后缀,创建完整的View对象;View是一个接口,表示视图的,在框架中jsp、html不是string表示,而是使用View和子类来表示的 
public interface View {
    String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
    String PATH_VARIABLES = View.class.getName() + ".pathVariables";
    String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";

    @Nullable
    default String getContentType() {
        return null;
    }

    void render(@Nullable Map model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
    中央处理器将4中的View对象对象获取到,调用View的方法,把Model的数据放入到request作用域,执行对象视图的forward,请求结束

对于springMVC的简单介绍就是这些了,深入的部分之后会补充

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/708202.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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