SpringMvc扩展

122 阅读8分钟
Springmvc表述层(Controleer层)框架

五:SpringMvc其它扩展

5.1:全局异常处理机制:

5.1.1异常处理的两种机制:

1.编程式异常处理:
   在代码中显示的编写处理异常的逻辑; eg:使用try-catch来捕获异常,然后再catch中编写特定的处理代码,或者再finally中执行一些清理操作;--》:即开发人员需要显示的进行异常处理,可读性较差;
   
2.声明式事务处理:将异常处理的逻辑从具体业务中提取出来,通过配置等方式进行统一管理;
-->:在声明式异常处理中,开发人员只需要为方法或类标注相应的注解(如 @Throws@ExceptionHandler),就可以处理特定类型的异常。

5.1.2:声明式事务处理:

步骤:
   1.声明一个全局异常处理类(全局异常处理器);:使用@RestControllerAdvice注解(底层是aop)
   
   2.在里面写若干个异常处理方法handler:
     在handler上通过@ExceptionHadler注解来指定对应的异常类型!!!!;
     之后发生响应的异常就会自动调用全局异常处理类中对应的handler方法;

  注意:@RestControllerAdvice注解:代表handler方法处理完之间返回字符串;

代码演示:

第一步:

//异常处理类/器: 只要异常发生时:会走此类下对应的handler,然后会根据@ExceptionHandler注解指定的异常
//取找对应的handler方法;
//指定的异常:可以精准查找,如果不是精准或者查找父层

@RestControllerAdvice  //代表直接返回json字符串
public class GlobalExceptionHandler {

    @ExceptionHandler(ArithmeticException.class)  //-->指定异常类型
        //之后当类中的出现异常时:就会到这个异常处理器中取找对应的异常处理信息(即handler方法)!!!

    public Object ArithmeticExceptionHandler(ArithmeticException e){ //-->使用对象接收异常处理信息

        //自定义处理异常逻辑	eg:返回;
        String message = e.getMessage();
        System.out.println("message = " + message);
        return message;
    }
    
    
    
    @ExceptionHandler(Exception.class)  //如果没有精准的对应类型的异常:就都走这个父异常
    public Object Exceptionhandler( ArithmeticException e){
        //自定义处理异常逻辑
        String message = e.getMessage();
        return message;
    }
}


第二步:
    
@RestController  //=@Controller + @ResponseBody
@RequestMapping("user")
public class UserController {


    //handler:
    @GetMapping("data")
    public String data(){
        //1.造一个空指针异常
        String name=null;
        name.toString();
        return "ok";
    }


    @RequestMapping("data1")
    public  String data1(){
        //2.造一个算数异常:
        int i=1/0;
        return "ok";
    }
}


注意:    @ExceptionHandler(Exception.class)  //如果没有精准的对应类型的异常:就都走这个父异常!!!!

第三步: 
    配置:配置类+初始化类

5.2拦截器

作用类似于javaweb的过滤器:从外部整体拦截
而SpringMvc中使用的是:拦截器(HandlerInterceptor);

拦截器:拦截SpringMvc内部的流程
       出现的位置:
       	1.调用handler之前,2.调用handler之后,3.以及整体完毕之后
    
一般我们都使用拦截器
 
使用方法/步骤:
    1.创建拦截器类;
       实现HandlerInterceptor接口:(里面有3个方法:一般用的最多的是:prehandler()方法)
    2.将拦截器加到ioc容器:
        实现WebMvcConfig接口
        然后将拦截器加到注册类就完成了拦截器的注册;
        

代码:


//第一步:创建拦截器类,实现方法
//第二步:配置到拦配置类中:要拦截哪些路径
public class MyInterceptor  implements HandlerInterceptor {


    //1.preHandle()方法:前置处理方法:在执行handler之前调用的拦截方法; 有拦截机制!!!
    // 使用场景:在这里面可以写:编码格式设置、登录保护、权限处理;
 /*
 request:请求对象
 response:响应对象
 handler:是我们要调用的handler方法
 return true:代表放行; false:代表放行 -->我们可以通过它来控制拦截还是放行!!!
  */

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("request = " + request + ", response = " + response + ", handler = " + handler);
        System.out.println("MyInterceptor.preHandle");
        return true; //ture:代表放行
    }

    /*
    2.
    postHandle():后置处理方法:当handler处理完之后触发的方法!没有拦截机制
    此方法只有preHandler()为return true才触发(因为只有放行才会执行下面的方法)

    //使用场景:对结果进行处理,敏感词汇检查!!!


    参数:
     request:请求对象
     response:响应对象
     handler:handler方法
     modelAndView:返回的视图和共享域数据对象
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor.postHandler");
    }

    /*
    3.
    afterCompletion():整体处理完毕会执行它

    参数:
     request:请求对象
     response:响应对象
     handler:handler方法
     ex:如果保错了的异常对象
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor.afterCompletion");
    }
}

第二步:
配置类中配置:


    //第二步:进行拦截器的注册
    @Override
    public void addInterceptors(InterceptorRegistry registry) { //拦截器添加的方法
        //配置方案1:拦截全部请求(默认情况下:拦截器是拦截全部请求)
        //registry.addInterceptor(new MyInterceptor()); //将拦截器注册到方法中
        /*
        之后:我们发现访问:user下的data、data1,都会走拦截器!!!!2
          分析执行过程:
            因为我们设置了拦截器,拦截user下的路径,之后我们访问handler方法时,就会先走拦截器,因为拦截器是true,所以放行
            放行之后,就访问handler方法,执行其相关业务!!!!!!!!
         */


        //配置方案2:指定地址拦截: -->: .addPathPatterns("/user/data1")
                   //也可以使用* :任意一层字符串; **:任意多层字符串
                   //eg:我们要拦截用户下面的所有请求:/uer/**
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/**");
        /*
        之后我们发现:我们访问user下的所有路径都会经过拦截器;
        但是访问order下的路径都不会走拦截器:因为我们只设置了拦截user下的!!!
         */


        //配置方案3:排除拦截;
          //注意:但排除的拦截地址应该在拦截地址的内部 eg:user/data1 在user/**内部!!!!
         /*
         eg:我们拦截user下的请求,但是把user/data1排除掉:.excludePathPatterns("/user/data1")
          */

        registry.addInterceptor(new MyInterceptor()).addPathPatterns("user/**").excludePathPatterns("/user/data1");
        /*
        分析:因为在拦截器中排除了data1,所以之后我们访问data1时,就不会走拦截器了;
         */



        //当有两个拦截器时:哪个拦截器先执行;
        /*registry.addInterceptor(new MyInterceptor());
        registry.addInterceptor(new MyInterceptor1());*/
        /*

         */
    }
  

5.3参数校验:

1.参数校验概述:

参数校验:
  1.背景:
    前端进行访问:传来url(account、password) --> Controller层:进行接收(account、password) 
    --> Service层:进行参数校验:eg:非空校验、格式校验....
    
  2.学习检验 “ 注解 ”:
    jsr303系列(java提供)-->:之后我们就不需要在Controller层和Service层做校验了,直接使用“ 注解 ”即可!!!
        
  3!!.使用/操作方法:直接在接收对象的 实体类 属性上 使用校验(注解):@NotBlank 来完成相应的校验 即可
        eg:
           @NotBlank
            account属性
           @NotBlank
            password属性
           

2.易混总结:

@NotNull@NotEmpty@NotBlank 都是用于在数据校验中 检查 字段值是否为空 的注解,但是它们的用法和校验规则有所不同。

(1@NotNull
     一般用于检查包装类型是否为空
(2@NotEmpty:
    用于检查集合类型是否为空
(3@NotBlank
   用于检查字符串是否为空

3.模拟参数校验:

springmvc-high-other-05:pojo.User + controller.UserController

参数校验发步骤:
      第一步:写校验信息:
               a:在校验对象的属性上 使用相应的 校验注解;
               b:在handler方法的形参上使用 校验对象接收即可
               c:必须使用@Validated注解:才能使校验注解生效;
               d:如果是json数据要加@RequestBody注解;

      第二步:收集错误绑定信息
             自定义返回参数绑定错误信息:
               a:在校验对象后面写一个BindingResult bindingResult对象(与校验对象紧挨着)
               b:BindingResult有一个方法:hasErrors():会检验绑定是否成功
                  如果不成功:hasErrors()就为true,我们自己写错误数据返回即可
                  如果成功:就正常走;
             
               
       

代码演示:


User实体类中的属性(User类就是要校验的对象)

            /*
            要求/校验:
            1.name 不为null和空字符串
            2.password:长度大于6
            3.age 必须>=1
            4.email为邮箱格式的字符串;
            5.birthday:传入的是过去时间
             */
            @Data
            public class User {

                @NotBlank //校验字符串不为空的注解
                private String name;

                @Length(min=6) //长度最小为6
                private String password;

                @Min(1)//最小值为1
                private int age;

                @Email //对邮箱格式进行校验
                private String email;


                @Past //生日必须是过去
                private Date birthday;
            }


UserController的handler方法:
  
        //校验:

        //接收用户数据,用户有校验注解
        /*
        使用校验注解的步骤:
          1.为实体类的属性添加校验注解
          2.handler中使用对应的实体类+@validate注解接值;
            handler(@validate User user)
            注意:a:加上@validate之后:校验注解才会生效;
                 b:接收json参数:在参数处加一个@ReauestBody

          如果:不符合校验规则,会直接向前端抛出异常!
          但是我们要求:接收错误绑定信息!自定义返回结果!  我们约定:参数错误,就给前端返回一个对象:code:400-->代表错误
     捕捉错误绑定信息信息:操作步骤:
       1.在参数后面加BindingResult result:result是绑定校验的结果,如果有它,我们可以在它内部根据结果自定义返回值
         eg:handler(校验对象,BindingResult result):BindingResult紧紧挨着 校验对象;
       2.定义BindingResult绑定错误;

         */
        @PostMapping("register")
        public Object register(@Validated @RequestBody User user, BindingResult result) {

            if (result.hasErrors()) {
                //--》:如果有绑定错误,就不直接返回了,由我们自己决定返回的信息!!!!
                Map data=new HashMap();
                data.put("code",400);
                data.put("msg","参数校验异常");
                return data;
                /*
                有这个:报错之后,就不会直接给前端返回错误了,就会返回这个if里面的语句!!!
                 */
            }

            System.out.println("user = " + user);
            return user;


        }