JAVA三種攔截方式詳解(原生過濾器Filter、springMVC攔截器、aop切面)
最近面試有遇到攔截方式的場景,結(jié)合網(wǎng)上xdm的代碼整理了下,分為以下三種:
java原生過濾器Filter、springMVC攔截器、aop切面
一、java原生過濾器Filter
package com.zhangximing.springbootinterceptor.interceptor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Enumeration; /** * 自定義Filter * 對請求的header 過濾token * * 過濾器Filter可以拿到原始的HTTP請求和響應(yīng)的信息, * 但是拿不到你真正處理請求方法的信息,也就是方法的信息 * * @Component 注解讓攔截器注入Bean,從而讓攔截器生效 * @WebFilter 配置攔截規(guī)則 * * 攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller * */ @Slf4j @Component @WebFilter(urlPatterns = {"/**"},filterName = "authFilter") public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("TokenFilter init {}",filterConfig.getFilterName()); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String param = request.getParameter("param"); response.setContentType("text/html;charset=UTF-8"); //獲取請求頭token String 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)){ //驗證token if ("7758258xx".equals(token)){ chain.doFilter(request,response);//到下一個鏈 }else{ response.getWriter().write("doFilter-請求頭token不通過"); } }else{ log.info("doFilter-參數(shù)param不符合條件"); response.getWriter().write("doFilter-參數(shù)param不通過"); } } @Override public void destroy() { log.info("destroy"); } }
簡單測試直接用的postman,參數(shù)是一個param和一個請求頭token:
這里補充一下:
若非springboot的情況下,不使用@WebFilter則需要自己設(shè)置配置文件
你需要在web.xml文件中配置過濾器,指定要攔截的URL以及要使用的過濾器
<filter> <filter-name>MyFilter</filter-name> <filter-class>com.zhangximing.springbootinterceptor.interceptor.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>MyFilter</filter-name> <url-pattern>/example</url-pattern> </filter-mapping>
二、springMVC攔截器
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; /** * 自定義攔截器 * 自定義攔截器后,需要配置進Spring * * 攔截器Interceptor可以拿到原始的HTTP請求和響應(yīng)的信息, * 也可以拿到你真正處理請求方法的信息,但是拿不到傳進參數(shù)的那個值。 * *攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller */ @Slf4j @Component public class MyInterceptor implements HandlerInterceptor { /** * 在訪問Controller某個方法之前這個方法會被調(diào)用。 * @param request * @param response * @param handler * @return false則表示不執(zhí)行postHandle方法,true 表示執(zhí)行postHandle方法 * @throws Exception */ @Override public 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對靜態(tài)資源也進行了攔截,當攔截器攔截到請求之后, // 但controller里并沒有對應(yīng)的請求時,該請求會被當成是對靜態(tài)資源的請求。 // 此時的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){//靜態(tài)資源 ResourceHttpRequestHandler resourceHttpRequestHandler = (ResourceHttpRequestHandler) handler; log.info("Token Interceptor preHandle getMethod {}",resourceHttpRequestHandler.getMediaTypes()); } if (!"7758258xx".equals(token)){ response.getWriter().write("doInterceptor-請求頭token不通過"); return false; } //false則表示不執(zhí)行postHandle方法,不執(zhí)行下一步chain鏈,直接返回response return true; } /** * 請求處理之后進行調(diào)用,但是在視圖被渲染之前(Controller方法調(diào)用之后) * preHandle方法處理之后這個方法會被調(diào)用,如果控制器Controller出現(xiàn)了異常,則不會執(zhí)行此方法 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("Interceptor postHandle"); } /** * 不管有沒有異常,這個afterCompletion都會被調(diào)用 * @param request * @param response * @param handler * @param ex * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("Interceptor afterCompletion"); }
這里注意下,需要將攔截器配置進spring
import com.zhangximing.springbootinterceptor.interceptor.MyInterceptor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * MyInterceptor 自定義攔截器后,需要配置進Spring * 也可以mapping,跨域設(shè)置 */ @Slf4j @Configuration public class MyInterceptorConfig implements WebMvcConfigurer { @Autowired MyInterceptor myInterceptor; /** * 添加攔截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { log.info("addInterceptors tokenInterceptor"); registry.addInterceptor(myInterceptor) .addPathPatterns("/**")//指定該類攔截的url .excludePathPatterns( "/static/**");//過濾靜態(tài)資源 } /** * 如果實現(xiàn)了Filter跨域攔截,這個跨域無效 * 攔截器實現(xiàn) 跨域支持 * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { log.info("addInterceptors addCorsMappings"); registry.addMapping("/**") .allowedOriginPatterns("*") //本人測試時springboot2.7版本用的是這個 .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT","OPTIONS","HEAD") .allowedHeaders("*") .maxAge(3600); } }
測試同理:
三、aop切面
引入maven:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
若出現(xiàn)無法解析aspectjweaver則需要手動加入其他版本maven解決問題
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; /** * @Author: zhangximing * @Email: 530659058@qq.com * @Date: 2023/8/18 10:15 * @Description: 切面 */ @Slf4j @Component //表示它是一個Spring的組件 @Aspect //表示它是一個切面 public class MyAspect { private static final Logger logger = LoggerFactory.getLogger(MyAspect.class); ThreadLocal<Long> startTime = new ThreadLocal<>(); /** * 第一個*代表返回類型不限 * 第二個*代表所有類 * 第三個*代表所有方法 * (..) 代表參數(shù)不限 * 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("方法執(zhí)行前執(zhí)行......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("響應(yīng)方法:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("請求參數(shù):" + Arrays.toString(joinPoint.getArgs())); logger.info("連接點的方法簽名對象:"+joinPoint.getSignature()); logger.info("連接點所在的目標對象:"+joinPoint.getTarget()); logger.info("代理對象:"+joinPoint. getThis()); logger.info("------------------------------------------------------"); startTime.set(System.currentTimeMillis()); } // 定義需要匹配的切點表達式,同時需要匹配參數(shù) /** * @description 要攔截修改參數(shù)的值只有使用這個方法,Around相當于before+after * @param pjp * @param arg 類型可以根據(jù)pointCut指定切點類下的方法確定,也可以使用統(tǒng)一的Object,也可以不寫參數(shù) * @return * @throws Throwable */ @Around("pointCut() && args(arg)") public Object around(ProceedingJoinPoint pjp, Object arg) throws Throwable{ logger.info("入?yún)ⅲ簕}",arg); logger.info("方法環(huán)繞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("方法環(huán)繞end...around"); return objectNew; } @After("within(com.zhangximing.springbootinterceptor.controller.*)") public void after(){ System.out.println("方法之后執(zhí)行...after."); } /** * * @param AjaxResult rst 該參數(shù)類型需要與測試的Controller層的返回值類型一致,否則不生效,也就是找不到 * 該測試中的AjaxResult是測試項目中封裝好的出參 */ @AfterReturning(pointcut="pointCut()",returning = "rst") public void afterRunning(JSONObject rst){ if(startTime.get() == null){ startTime.set(System.currentTimeMillis()); } System.out.println("方法執(zhí)行完執(zhí)行...afterRunning"); logger.info("耗時(毫秒):" + (System.currentTimeMillis() - startTime.get())); logger.info("返回數(shù)據(jù):{}", rst); logger.info("==========================================>"); } @AfterThrowing("within(com.zhangximing.springbootinterceptor.controller.*)") public void afterThrowing(){ System.out.println("異常出現(xiàn)之后...afterThrowing"); } }
實現(xiàn)效果用的是如下controller:
@Slf4j @RestController @RequestMapping("/test") public class TestController { @RequestMapping("/test1") public String test1(@RequestParam(required = false, value = "param") String param){ log.info("test1:"+param); return "test1:"+param; } @RequestMapping("/test2") public JSONObject test2(@RequestBody JSONObject params){ log.info("test2:"+params.toJSONString()); params.put("success",true); return params; } }
參數(shù)判斷攔截以及參數(shù)修改等方式都可以通過aop切面來實現(xiàn),這是比較基本的aop攔截實現(xiàn)
最后關(guān)于aop失效補充下,切面只能對被spring代理的對象起作用,目前是針對的請求入口進行攔截,我試了下踩坑,比如說如果要對service或dao進行攔截,可以使用注解注入的方式生效。
總結(jié)
到此這篇關(guān)于JAVA三種攔截方式(原生過濾器Filter、springMVC攔截器、aop切面)的文章就介紹到這了,更多相關(guān)JAVA攔截方式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Idea?中控制啟動命令的詳細過程?區(qū)分環(huán)境案例詳解
這篇文章主要介紹了Idea?中控制啟動命令的詳細過程?區(qū)分環(huán)境案例詳解,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08Sharding-JDBC自動實現(xiàn)MySQL讀寫分離的示例代碼
本文主要介紹了Sharding-JDBC自動實現(xiàn)MySQL讀寫分離,優(yōu)點在于數(shù)據(jù)源完全有Sharding-JDBC托管,寫操作自動執(zhí)行master庫,讀操作自動執(zhí)行slave庫,感興趣的可以了解一下2021-11-11SpringBoot @PostConstruct原理用法解析
這篇文章主要介紹了SpringBoot @PostConstruct原理用法解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08詳解Java如何使用責任鏈默認優(yōu)雅地進行參數(shù)校驗
項目中參數(shù)校驗十分重要,它可以保護我們應(yīng)用程序的安全性和合法性。這篇文章主要介紹了如何使用責任鏈默認優(yōu)雅地進行參數(shù)校驗,需要的可以參考一下2023-03-03Java使用FutureTask實現(xiàn)預(yù)加載的示例詳解
基于FutureTask的特性,通常可以使用FutureTask做一些預(yù)加載工作,比如一些時間較長的計算等,本文就來和大家講講具體實現(xiàn)方法吧,感興趣的可以了解一下2023-06-06JAVA中關(guān)于Long類型返回前端精度丟失問題處理辦法
這篇文章主要介紹了后端JavaBean的id屬性從Long類型改為雪花算法后出現(xiàn)的精度丟失問題,解決方案包括將id字段類型改為字符串或使用Jackson序列化方式,需要的朋友可以參考下2024-11-11