JAVA攔截器的三種實現方式
- 一、java原生過濾器Filter
- 二、springMVC攔截器
- 三、aop切面實現攔截器
一、java原生過濾器Filter
/*** 自定義Filter* 對請求的header 過濾token** 過濾器Filter可以拿到原始的HTTP請求和響應的信息,* 但是拿不到你真正處理請求方法的信息,也就是方法的信息** @Component 注解讓攔截器注入Bean,從而讓攔截器生效* @WebFilter 配置攔截規則** 攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller**/
@Slf4j
@Component
@WebFilter(urlPatterns = {"/**"},filterName = "authFilter")
public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("TokenFilter init {}",filterConfig.getFilterName());}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {String param = request.getParameter("param");response.setContentType("text/html;charset=UTF-8");//獲取請求頭tokenString token = "";HttpServletRequest httpServletRequest = (HttpServletRequest) request;Enumeration<String> headerNames = httpServletRequest.getHeaderNames();while(headerNames.hasMoreElements()) {//判斷是否還有下一個元素String nextElement = headerNames.nextElement();//獲取headerNames集合中的請求頭if ("token".equals(nextElement)){token = httpServletRequest.getHeader(nextElement);log.info("請求頭key[" + nextElement + "]:" + token);}}log.info("doFilter-我攔截到了請求:"+ param);if (null != param && "pass".equals(param)){//驗證tokenif ("7758258xx".equals(token)){chain.doFilter(request,response);//到下一個鏈}else{response.getWriter().write("doFilter-請求頭token不通過");}}else{log.info("doFilter-參數param不符合條件");response.getWriter().write("doFilter-參數param不通過");}}@Overridepublic void destroy() {log.info("destroy");}
}
二、springMVC攔截器
/*** 自定義攔截器* 自定義攔截器后,需要配置進Spring** 攔截器Interceptor可以拿到原始的HTTP請求和響應的信息,* 也可以拿到你真正處理請求方法的信息,但是拿不到傳進參數的那個值。**攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller*/
@Slf4j
@Component
public class MyInterceptor implements HandlerInterceptor {/*** 在訪問Controller某個方法之前這個方法會被調用。* @param request* @param response* @param handler* @return false則表示不執行postHandle方法,true 表示執行postHandle方法* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("Interceptor preHandle {}","");String token = request.getHeader("token");log.info("Interceptor preHandle token :{}",token);log.info("Interceptor preHandle uri {}",request.getRequestURL().toString());response.setContentType("text/html;charset=UTF-8");//spring boot 2.0對靜態資源也進行了攔截,當攔截器攔截到請求之后,// 但controller里并沒有對應的請求時,該請求會被當成是對靜態資源的請求。// 此時的handler就是 ResourceHttpRequestHandler,就會拋出上述錯誤。if (handler instanceof HandlerMethod){HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();log.info("Token Interceptor preHandle getMethod {}",method.getName());}else if(handler instanceof ResourceHttpRequestHandler){//靜態資源ResourceHttpRequestHandler resourceHttpRequestHandler = (ResourceHttpRequestHandler) handler;log.info("Token Interceptor preHandle getMethod {}",resourceHttpRequestHandler.getMediaTypes());}if (!"7758258xx".equals(token)){response.getWriter().write("doInterceptor-請求頭token不通過");return false;}//false則表示不執行postHandle方法,不執行下一步chain鏈,直接返回responsereturn true;}/*** 請求處理之后進行調用,但是在視圖被渲染之前(Controller方法調用之后)* preHandle方法處理之后這個方法會被調用,如果控制器Controller出現了異常,則不會執行此方法* @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("Interceptor postHandle");}/*** 不管有沒有異常,這個afterCompletion都會被調用* @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("Interceptor afterCompletion");}
三、aop切面實現攔截器
引入maven:
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version>
</dependency>
/*** @Description: 切面*/
@Slf4j
@Component //表示它是一個Spring的組件
@Aspect //表示它是一個切面
public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);ThreadLocal<Long> startTime = new ThreadLocal<>();/*** 第一個*代表返回類型不限* 第二個*代表所有類* 第三個*代表所有方法* (..) 代表參數不限* com.zhangximing.springbootinterceptor.controller 測試的controller層*/@Pointcut("execution(public * com.zhangximing.springbootinterceptor.controller.*.*(..))")public void pointCut(){};@Before(value = "pointCut()")public void before(JoinPoint joinPoint){System.out.println("方法執行前執行......before");ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();logger.info("<=====================================================");logger.info("請求來源: =》" + request.getRemoteAddr());logger.info("請求URL:" + request.getRequestURL().toString());logger.info("請求方式:" + request.getMethod());logger.info("響應方法:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());logger.info("請求參數:" + Arrays.toString(joinPoint.getArgs()));logger.info("連接點的方法簽名對象:"+joinPoint.getSignature());logger.info("連接點所在的目標對象:"+joinPoint.getTarget());logger.info("代理對象:"+joinPoint. getThis());logger.info("------------------------------------------------------");startTime.set(System.currentTimeMillis());}// 定義需要匹配的切點表達式,同時需要匹配參數/*** @description 要攔截修改參數的值只有使用這個方法,Around相當于before+after* @param pjp* @param arg 類型可以根據pointCut指定切點類下的方法確定,也可以使用統一的Object,也可以不寫參數* @return* @throws Throwable*/@Around("pointCut() && args(arg)")public Object around(ProceedingJoinPoint pjp, Object arg) throws Throwable{logger.info("入參:{}",arg);logger.info("方法環繞start...around");JSONObject param = JSONObject.parseObject(JSONObject.toJSONString(arg));if ("zxm".equals(param.getString("name"))){JSONObject result = new JSONObject();result.put("success",false);result.put("msg","error");return result;}param.put("exist",true);param.put("name","cml");//修改值Object[] objects = new Object[]{param};Object objectNew = pjp.proceed(objects);logger.info("方法環繞end...around");return objectNew;}@After("within(com.zhangximing.springbootinterceptor.controller.*)")public void after(){System.out.println("方法之后執行...after.");}/**** @param AjaxResult rst 該參數類型需要與測試的Controller層的返回值類型一致,否則不生效,也就是找不到* 該測試中的AjaxResult是測試項目中封裝好的出參*/@AfterReturning(pointcut="pointCut()",returning = "rst")public void afterRunning(JSONObject rst){if(startTime.get() == null){startTime.set(System.currentTimeMillis());}System.out.println("方法執行完執行...afterRunning");logger.info("耗時(毫秒):" + (System.currentTimeMillis() - startTime.get()));logger.info("返回數據:{}", rst);logger.info("==========================================>");}@AfterThrowing("within(com.zhangximing.springbootinterceptor.controller.*)")public void afterThrowing(){System.out.println("異常出現之后...afterThrowing");}
}