• 【SpringBoot】 Aspect,Filter、Interceptor、ControllerAdvice区别——记一次RestControllerAdvice无法拦截Filter内抛出异常


    记一次RestControllerAdvice无法拦截Filter内抛出异常

    今天有同事用到Shiro使用JWT的时候在Filter里做身份验证,然后在里面catch捕获并抛出了自定义异常。我们这边是用的RestControllerAdvice做统一异常处理,然后这个异常并没有被RestControllerAdvice所拦截到

    原因

    请求进来 会按照 filter -> interceptor -> controllerAdvice -> aspect -> controller的顺序调用

    当controller返回异常 也会按照controller -> aspect -> controllerAdvice -> interceptor -> filter来依次抛出

    这种Filter发生的404、405、500错误都会到Spring默认的异常处理。如果你在配置文件配置了server.error.path的话,就会使用你配置的异常处理地址,如果没有就会使用你配置的error.path路径地址,如果还是没有,默认使用/error来作为发生异常的处理地址。如果想要替换默认的非Controller异常处理直接实现Spring提供的ErrorController接口就行了

    解决方案

    新建一个ErrorControllerImpl 实现ErrorController 把ErrorPath 指向error 再写一个方法把Error抛出 然后Controller全局统一异常处理RestControllerAdvice就能捕获到异常了

    
    import org.springframework.boot.web.servlet.error.ErrorController;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * @author Joe
     * createTime 2020/07/27 14:39
     * mail joe-code@foxmail.com
     */
    @Controller
    public class ErrorControllerImpl implements ErrorController {
    
        @Override
        public String getErrorPath() {
            return "/error";
        }
    
        @RequestMapping("/error")
      	
        public void handleError(HttpServletRequest request) throws Throwable {
            if (request.getAttribute("javax.servlet.error.exception") != null) {
                throw (Throwable) request.getAttribute("javax.servlet.error.exception");
            }
        }
    }
    
    

    参考stackoverflow链接 https://stackoverflow.com/questions/34595605/how-to-manage-exceptions-thrown-in-filters-in-spring

    1、切面(Aspect)

    是什么

    切面,指切入目标方法,扩展目标方法功能,却不修改目标方法代码,将扩展功能代码从目标方法代码中分离出来。可以针对方法级拦截,并获得方法的参数和返回值,但获取不到http请求,多配合注解使用。

    应用场景

    • 权限校验

    • 字段加密解密

    怎么用

    复制代码
    package com.dy.aspect;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    /**
     * this is a acpect
     * 切入点
     * 在那些方法上起作用
     * 在什么时候起作用
     *
     * @author duoyuan
     * @create 2019-12-20 20:52
     **/
    @Aspect
    @Component
    public class TimeAspect {
        @Around("execution(* com.duoyuan.controller.UserController.*(..))")
        public Object handleControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("time aspect start");
            Object[] args = proceedingJoinPoint.getArgs();
            for (Object arg : args) {
                System.out.println(arg.getClass().getName());
                System.out.println("arg is " + arg);
            }
            long startTime = new Date().getTime();
            Object obj = proceedingJoinPoint.proceed();
            System.out.println("time aspect 耗时" + (new Date().getTime() - startTime));
            System.out.println("time aspect end");
            return obj;
        }
    }
    复制代码

    2、过滤器(Filter)

    是什么

    过滤器就是一个实现了特殊接口的Java类,实现对请求资源的过滤的功能。和框架无关,在所有过滤组件的最外层,颗粒度比较大。

    应用场景

    • 验证token合法性、有效性

    怎么用

    springboot中一般有两种配置方式:

    (1)直接实现java.servlet.Filter接口

    复制代码
    package com.dy.webFilter;
    
    import org.springframework.stereotype.Component;
    
    import javax.servlet.*;
    import java.io.IOException;
    import java.util.Date;
    
    @Component
    public class TimerFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("Time  filter init");
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("Time filter start");
            long startTime = new Date().getTime();
            filterChain.doFilter(servletRequest, servletResponse);
            System.out.println("time filter:"+(new Date().getTime()-startTime));
            System.out.println("time filter finish");
        }
    
        @Override
        public void destroy() {
            System.out.println("Time filter destroy");
        }
    }
    复制代码

    (2)在WebConfig中配置,这种配置方式是因为使用第三方的Filter没有@Component注解。

    复制代码
    package com.dy.config;
    
    import com.nbkj.webFilter.TimerFilter;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Web配置
     *
     * @author duoyuan
     * @Configuration 这个注解声明这个类是配置类
     * @create 2019-12-20 18:00
     **/
    
    @Configuration
    public class WebConfig {
        @Bean
        public FilterRegistrationBean timeFilter() {
            FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            TimerFilter timerFilter = new TimerFilter();
            registrationBean.setFilter(timerFilter);
            List<String> urls = new ArrayList<>();
            urls.add("/*");
            registrationBean.setUrlPatterns(urls);
            return registrationBean;
        }
    }
    复制代码

    3、拦截器(Intereptor) 

    是什么

    拦截器是在面向切面编程中应用的,就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法。是基于JAVA的反射机制。拦截器不是在web.xml,比如struts在struts.xml中配置。可以获取被拦截的controller的方法,获取不到参数。 

    应用场景

    • 执行安全检查

    • 格式化请求头和主体

    • 审查或者记录日志

    • 根据请求内容授权或者限制用户访问

    • 根据请求频率限制用户访问

    怎么用

    复制代码
    package com.dy.interceptor;
    
    import org.springframework.stereotype.Component;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.persistence.Convert;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Date;
    
    /**
     * this is spring interceptor
     *
     * @author duoyuan
     * @create 2019-12-24 18:16
     **/
    
    @Component
    public class TimeInterceptor implements HandlerInterceptor {
    
        /**
         * 控制器方法处理之前
         *
         * @param httpServletRequest
         * @param httpServletResponse
         * @param handler
         * @return
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
            System.out.println("preHandle");
            System.out.println(((HandlerMethod) handler).getBean().getClass().getName());
            System.out.println(((HandlerMethod) handler).getMethod().getName());
            httpServletRequest.setAttribute("startTime", new Date().getTime());
            return false;
        }
    
        /**
         * 控制器方法处理之后
         * 控制器方法调用不抛异常调用
         *
         * @param httpServletRequest
         * @param httpServletResponse
         * @param o
         * @param modelAndView
         * @throws Exception
         */
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle");
            Long startTime = (Long) httpServletRequest.getAttribute("startTime");
            System.out.println("time interceptor 耗时" + (new Date().getTime() - startTime));
        }
    
        /**
         * 控制器方法抛不抛异常都会被调用
         *
         * @param httpServletRequest
         * @param httpServletResponse
         * @param o
         * @param e
         * @throws Exception
         */
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
            System.out.println("afterCompletion");
            Long startTime = (Long) httpServletRequest.getAttribute("startTime");
            System.out.println("time interceptor 耗时" + (new Date().getTime() - startTime));
            System.out.println("ex is" + e);
        }
    }
    复制代码
    复制代码
    package com.dy.config;
    
    import com.nbkj.interceptor.TimeInterceptor;
    import com.nbkj.webFilter.TimerFilter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Web配置
     *
     * @author duoyuan
     * @Configuration 这个注解声明这个类是配置类
     * @create 2019-12-20 18:00
     **/
    
    @Configuration
    public class WebConfig extends WebMvcConfigurerAdapter {
    
        @Autowired
        private TimeInterceptor timeInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(timeInterceptor);
        }
        
    }
    复制代码

    4、ControllerAdvice

    是什么

    controller的一个增强,最常用的是和@ExceptionHandler一起用来做全局异常。

    应用场景

    • 配合@ExceptionHandler做全局异常处理

    • 配合@InitBinder对特殊请求参数做转换

    • 配合@ModelAttribute使用

    怎么用 

    复制代码
    package com.dy.handler;
    
    import com.wusong.order.common.Resp;
    import com.wusong.order.common.ServiceException;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    /**
     * @description: 全局异常处理
     * @author:duoyuan
     * @create: 2019-12-16 下午1:45
     **/
    @ControllerAdvice
    @Slf4j
    public class GlobalExceptionHandler {
    
    
        /**
         * @description: 业务异常统一处理
         * @param e
         * @return:
         * @author:
         * @Date: 2019-12-16 下午12:26
        */
        @ExceptionHandler(value = ServiceException.class)
        @ResponseBody
        public Object serviceExceptionHandle(ServiceException e) {
            log.error("异常{}", e);
            Object obj = Resp.error(e);
            return obj;
        }
    }
    复制代码
     
  • 相关阅读:
    【 js 基础 】【 源码学习 】源码设计 (更新了backbone分析)
    【 js 基础 】【读书笔记】作用域和闭包
    【 js 基础 】【 源码学习 】 setTimeout(fn, 0) 的作用
    入职一个月快速熟悉大型Vue项目经验感想
    Git 实习一个月恍然大悟合集
    不断更新:整理常见的闭包问题
    (转)当margin-top、padding-top的值为百分比时是如何计算的?
    2019年8月面试腾讯前端开发实习生记录
    vue-cli2.X中引入高德地图,将其设为全局对象
    学习整理:用webpack4.x构建基本项目
  • 原文地址:https://www.cnblogs.com/zzsuje/p/14047431.html
Copyright © 2020-2023  润新知