spring 两次进入拦截器_4.SpringBoot 拦截器Fliter,Interceptor,Controller……
在項目的開發中,在某些情況下,我們需要對客戶端發出的請求進行攔截,常用的API攔截方式有Fliter,Interceptor,ControllerAdvice以及Aspect。
上面的圖是Spring中攔截機制,請求從Filter-->>Controller的過程中,只要在指定的環節出現異常,可以通過對應的機制進行處理。反之在任何一個環節如果異常未處理則不會進入下一個環節,會直接往外拋,例如在ControllerAdvice驗證發生異常則會拋給Filter,如果Filter未處理,則最終會由Tomcat容器拋出。
過濾器:Filter
可以獲得Http原始的請求和響應信息,但是拿不到響應方法的信息。
注冊Filter,在springboot當中提供了FilterRegistrationBean類來注冊Filter
//通過注解實現
@Slf4j
@Component
@WebFilter(filterName = "TimerFilter",urlPatterns = "/*")
public class TimerFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("" + getClass() + " init");}/*** 在這方法中進行攔截* @param request* @param response* @param chain* @throws IOException* @throws ServletException*/@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {log.info("time filter start class is {}",getClass());long start = System.currentTimeMillis();chain.doFilter(request, response);log.info("time filter:{}"+(System.currentTimeMillis()-start));log.info("time filter finish");}@Overridepublic void destroy() {log.info("" + getClass() + " init");}
}
//通過configuration實現
//自定義 一個Servlet類型的Filter實現類
public class FilterDemo3 implements Filter {private final Logger log = LoggerFactory.getLogger(getClass());@Resourceprivate CommonConfig commonConfig;@Overridepublic void destroy() {log.info("" + getClass() + " destroy");}@Overridepublic void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {log.info("" + getClass() + " doFilter " + commonConfig);arg2.doFilter(arg0, arg1);}@Overridepublic void init(FilterConfig arg0) throws ServletException {log.info("" + getClass() + " init");}}/*** web 組件配置* * @author: yx.zh* @date: 2020-06-14 07:03* 自定義注入,并支持依賴注入,組件排序*/
@Configuration
public class WebComponent2Config {@Beanpublic FilterRegistrationBean filterDemo3Registration() {FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(filterDemo3());registration.addUrlPatterns("/*");registration.addInitParameter("paramName", "paramValue");registration.setName("filterDemo3");registration.setOrder(6);return registration;}@Beanpublic FilterRegistrationBean filterDemo4Registration() {FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(filterDemo4());registration.addUrlPatterns("/*");registration.addInitParameter("paramName", "paramValue");registration.setName("filterDemo4");registration.setOrder(7);return registration;}@Beanpublic Filter filterDemo3() {return new FilterDemo3();}@Beanpublic Filter filterDemo4() {return new FilterDemo4();}}常用屬性
攔截器:Interceptor
可以獲得Http原始的請求和響應信息,也拿得到響應方法的信息,但是拿不到方法響應中的參數的值。
在web開發中,攔截器是經常用到的功能。它可以幫我們驗證是否登陸、預先設置數據以及統計方法的執行效率等。在spring中攔截器有兩種,第一種是HandlerInterceptor,第二種是MethodInterceptor。HandlerInterceptor是SpringMVC中的攔截器,它攔截的是Http請求的信息,優先于MethodInterceptor。而MethodInterceptor是springAOP的。前者攔截的是請求的地址,而后者是攔截controller中的方法,因為下面要將Aspect,就不詳細講述MethodInterceptor。
/*** @author: yx.zh* @date: 2020-06-14 08:06* Interceptor攔截器中。**/
@Component
@Slf4j
public class Interceptor implements HandlerInterceptor {/*** 控制器方法調用之前會進行* 和上面的Filter一樣,繼承的某些接口方法中也加了default關鍵字,可以不用重寫,這里為了演示就都寫了** @param request* @param response* @param handler* @return true就是選擇可以調用后面的方法 如果后續有ControllerAdvice的話會去執行對應的方法等。* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info(((HandlerMethod) handler).getBean().getClass().getName());log.info(((HandlerMethod) handler).getMethod().getName());request.setAttribute("startTime", System.currentTimeMillis());return true;}/*** 控制后方法執行之后會進行,拋出異常則不會被執行** @param request* @param response* @param handler* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("postHandle");Long start = (Long) request.getAttribute("startTime");log.info("time interceptor 耗時:{}" , (System.currentTimeMillis() - start));}/*** 方法被調用或者拋出異常都會被執行* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("afterCompletion");Long start = (Long) request.getAttribute("startTime");log.info("time interceptor 耗時{}", (System.currentTimeMillis() - start));}
}ControllerAdvice(Controller增強)
與ControllerAdvice相同作用的,還有RestControllerAdvice。主要是用于全局的異常攔截和處理,這里的異常可以使自定義異常也可以是JDK里面的異常用于處理當數據庫事務業務和預期不同的時候拋出封裝后的異常,進行數據庫事務回滾,并將異常的顯示給用戶。
/*** @author: yx.zh* @date: 2020-06-13 16:59**/
@Slf4j
@RestControllerAdvice
public class ControllerExceptionFilter {/*** 處理自定義異常*/@ExceptionHandler(FrameException.class)public Response handleFrameException(FrameException e) {log.error(e.getMessage(), e);return ResultUtil.exceptionResult(e);}/*** 方法參數校驗*/@ExceptionHandler(MethodArgumentNotValidException.class)public Response handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {log.error(e.getMessage());return ResultUtil.exceptionResult(new FrameException(ExceptionEnum.PARAMS_ERROR), e.getBindingResult().getFieldError().getDefaultMessage());}/*** ValidationException*/@ExceptionHandler(ValidationException.class)public Response handleValidationException(ValidationException e) {log.error(e.getMessage(), e);return ResultUtil.exceptionResult(new FrameException(ExceptionEnum.PARAMS_ERROR), e.getCause().getMessage());}/*** ConstraintViolationException*/@ExceptionHandler(ConstraintViolationException.class)public Response handleConstraintViolationException(ConstraintViolationException e) {log.error(e.getMessage(), e);return ResultUtil.exceptionResult(new FrameException(ExceptionEnum.PARAMS_ERROR), e.getMessage());}@ExceptionHandler(NoHandlerFoundException.class)public Response handlerNoFoundException(Exception e) {log.error(e.getMessage(), e);return ResultUtil.exceptionResult(new FrameException(ExceptionEnum.URLNOTFUOND), "路徑不存在,請檢查路徑是否正確");}@ExceptionHandler(DuplicateKeyException.class)public Response handleDuplicateKeyException(DuplicateKeyException e) {log.error(e.getMessage(), e);return ResultUtil.exceptionResult(new FrameException(ExceptionEnum.DUPLICATE_KEY_CODE), "數據重復,請檢查后提交");}@ExceptionHandler(Exception.class)public Response handleException(Exception e) {log.error(e.getMessage(), e);return ResultUtil.exceptionResult(new FrameException(ExceptionEnum.SYSTEM_EXCEPTION), "系統繁忙,請稍后再試");}}切面:Aspect
主要是進行公共方法的可以拿得到方法響應中參數的值,但是拿不到原始的Http請求和相對應響應的方法,屬于方法級別的攔截器。
執行順序如下
正常異常AroundBeforeAfterReturningAfterThrowing
/*** @auther zhyx* @Date 2020/6/11 9:08* @Description*/
@Component
@Aspect
@Slf4j
public class HttpAspect {@Pointcut("execution(* com.universe.polaris.controller.*.*(..))")public void controllerPointcut(){}@Before("controllerPointcut()")public void before(JoinPoint joinPoint){ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = requestAttributes.getRequest();/*** url*/log.info("Before:url={}",request.getRequestURL());/*** ip*/log.info("Before:ip={}",request.getRemoteAddr());/*** 請求方式*/log.info("Before:method={}",request.getMethod());/*** 代理類*/log.info("Before:代理類調用的方法:{}",joinPoint.getSignature().getDeclaringTypeName() + "#" + joinPoint.getSignature().getName());StringBuilder sb=new StringBuilder();for(Object temp:joinPoint.getArgs()){sb.append(temp.toString());}/*** 參數*/log.info("params={}",sb.toString());}@Around("controllerPointcut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) {try {log.info("Around: 環繞執行");return proceedingJoinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}return null;}@AfterReturning( pointcut = "controllerPointcut()",returning = "object")public void afterReturning(Object object){log.info("AfterReturning 執行: response={}", JSON.toJSONString(object));}@AfterThrowing(throwing = "e", pointcut = "controllerPointcut()")public void afterThrowing(Throwable e) {log.error("系統異常:{}", e.getMessage());}
}
總結
以上是生活随笔為你收集整理的spring 两次进入拦截器_4.SpringBoot 拦截器Fliter,Interceptor,Controller……的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ggplot2中显示坐标轴_R可视化11
- 下一篇: fb50 sap 报记账码未定义_SAP