JAVA三種攔截方式詳解(原生過濾器Filter、springMVC攔截器、aop切面)
最近面試有遇到攔截方式的場(chǎng)景,結(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 * 對(duì)請(qǐng)求的header 過濾token * * 過濾器Filter可以拿到原始的HTTP請(qǐng)求和響應(yīng)的信息, * 但是拿不到你真正處理請(qǐ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"); //獲取請(qǐng)求頭token String token = ""; HttpServletRequest httpServletRequest = (HttpServletRequest) request; Enumeration<String> headerNames = httpServletRequest.getHeaderNames(); while(headerNames.hasMoreElements()) {//判斷是否還有下一個(gè)元素 String nextElement = headerNames.nextElement();//獲取headerNames集合中的請(qǐng)求頭 if ("token".equals(nextElement)){ token = httpServletRequest.getHeader(nextElement); log.info("請(qǐng)求頭key[" + nextElement + "]:" + token); } } log.info("doFilter-我攔截到了請(qǐng)求:"+ param); if (null != param && "pass".equals(param)){ //驗(yàn)證token if ("7758258xx".equals(token)){ chain.doFilter(request,response);//到下一個(gè)鏈 }else{ response.getWriter().write("doFilter-請(qǐng)求頭token不通過"); } }else{ log.info("doFilter-參數(shù)param不符合條件"); response.getWriter().write("doFilter-參數(shù)param不通過"); } } @Override public void destroy() { log.info("destroy"); } }
簡(jiǎn)單測(cè)試直接用的postman,參數(shù)是一個(gè)param和一個(gè)請(qǐng)求頭token:
這里補(bǔ)充一下:
若非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; /** * 自定義攔截器 * 自定義攔截器后,需要配置進(jìn)Spring * * 攔截器Interceptor可以拿到原始的HTTP請(qǐng)求和響應(yīng)的信息, * 也可以拿到你真正處理請(qǐng)求方法的信息,但是拿不到傳進(jìn)參數(shù)的那個(gè)值。 * *攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller */ @Slf4j @Component public class MyInterceptor implements HandlerInterceptor { /** * 在訪問Controller某個(gè)方法之前這個(gè)方法會(huì)被調(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對(duì)靜態(tài)資源也進(jìn)行了攔截,當(dāng)攔截器攔截到請(qǐng)求之后, // 但controller里并沒有對(duì)應(yīng)的請(qǐng)求時(shí),該請(qǐng)求會(huì)被當(dāng)成是對(duì)靜態(tài)資源的請(qǐng)求。 // 此時(shí)的handler就是 ResourceHttpRequestHandler,就會(huì)拋出上述錯(cuò)誤。 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-請(qǐng)求頭token不通過"); return false; } //false則表示不執(zhí)行postHandle方法,不執(zhí)行下一步chain鏈,直接返回response return true; } /** * 請(qǐng)求處理之后進(jìn)行調(diào)用,但是在視圖被渲染之前(Controller方法調(diào)用之后) * preHandle方法處理之后這個(gè)方法會(huì)被調(diào)用,如果控制器Controller出現(xiàn)了異常,則不會(huì)執(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"); } /** * 不管有沒有異常,這個(gè)afterCompletion都會(huì)被調(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"); }
這里注意下,需要將攔截器配置進(jìn)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 自定義攔截器后,需要配置進(jìn)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)資源 } /** * 如果實(shí)現(xiàn)了Filter跨域攔截,這個(gè)跨域無效 * 攔截器實(shí)現(xiàn) 跨域支持 * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { log.info("addInterceptors addCorsMappings"); registry.addMapping("/**") .allowedOriginPatterns("*") //本人測(cè)試時(shí)springboot2.7版本用的是這個(gè) .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT","OPTIONS","HEAD") .allowedHeaders("*") .maxAge(3600); } }
測(cè)試同理:
三、aop切面
引入maven:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
若出現(xiàn)無法解析aspectjweaver則需要手動(dòng)加入其他版本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 //表示它是一個(gè)Spring的組件 @Aspect //表示它是一個(gè)切面 public class MyAspect { private static final Logger logger = LoggerFactory.getLogger(MyAspect.class); ThreadLocal<Long> startTime = new ThreadLocal<>(); /** * 第一個(gè)*代表返回類型不限 * 第二個(gè)*代表所有類 * 第三個(gè)*代表所有方法 * (..) 代表參數(shù)不限 * com.zhangximing.springbootinterceptor.controller 測(cè)試的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("請(qǐng)求來源: =》" + request.getRemoteAddr()); logger.info("請(qǐng)求URL:" + request.getRequestURL().toString()); logger.info("請(qǐng)求方式:" + request.getMethod()); logger.info("響應(yīng)方法:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("請(qǐng)求參數(shù):" + Arrays.toString(joinPoint.getArgs())); logger.info("連接點(diǎn)的方法簽名對(duì)象:"+joinPoint.getSignature()); logger.info("連接點(diǎn)所在的目標(biāo)對(duì)象:"+joinPoint.getTarget()); logger.info("代理對(duì)象:"+joinPoint. getThis()); logger.info("------------------------------------------------------"); startTime.set(System.currentTimeMillis()); } // 定義需要匹配的切點(diǎn)表達(dá)式,同時(shí)需要匹配參數(shù) /** * @description 要攔截修改參數(shù)的值只有使用這個(gè)方法,Around相當(dāng)于before+after * @param pjp * @param arg 類型可以根據(jù)pointCut指定切點(diǎn)類下的方法確定,也可以使用統(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ù)類型需要與測(cè)試的Controller層的返回值類型一致,否則不生效,也就是找不到 * 該測(cè)試中的AjaxResult是測(cè)試項(xiàng)目中封裝好的出參 */ @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("耗時(shí)(毫秒):" + (System.currentTimeMillis() - startTime.get())); logger.info("返回?cái)?shù)據(jù):{}", rst); logger.info("==========================================>"); } @AfterThrowing("within(com.zhangximing.springbootinterceptor.controller.*)") public void afterThrowing(){ System.out.println("異常出現(xiàn)之后...afterThrowing"); } }
實(shí)現(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切面來實(shí)現(xiàn),這是比較基本的aop攔截實(shí)現(xiàn)
最后關(guān)于aop失效補(bǔ)充下,切面只能對(duì)被spring代理的對(duì)象起作用,目前是針對(duì)的請(qǐng)求入口進(jìn)行攔截,我試了下踩坑,比如說如果要對(duì)service或dao進(jìn)行攔截,可以使用注解注入的方式生效。
總結(jié)
到此這篇關(guān)于JAVA三種攔截方式(原生過濾器Filter、springMVC攔截器、aop切面)的文章就介紹到這了,更多相關(guān)JAVA攔截方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java非遞歸實(shí)現(xiàn)之二叉樹的前中后序遍歷詳解
樹的遍歷順序大體分為三種:前序遍歷(先根遍歷、先序遍歷),中序遍歷(中根遍歷),后序遍歷(后根遍歷),本文將給大家詳細(xì)的介紹,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值2021-09-09java中springMVC獲取請(qǐng)求參數(shù)的方法
這篇文章主要介紹了java中springMVC獲取請(qǐng)求參數(shù)的方法,springmvc是spring框架的一個(gè)模塊,springmvc和spring無需通過中間整合層進(jìn)行整合,需要的朋友可以參考下2023-05-05Idea?中控制啟動(dòng)命令的詳細(xì)過程?區(qū)分環(huán)境案例詳解
這篇文章主要介紹了Idea?中控制啟動(dòng)命令的詳細(xì)過程?區(qū)分環(huán)境案例詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08JavaFX如何獲取ListView(列表視圖)的選項(xiàng)
這篇文章主要介紹了JavaFX如何獲取ListView(列表視圖)的選項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Sharding-JDBC自動(dòng)實(shí)現(xiàn)MySQL讀寫分離的示例代碼
本文主要介紹了Sharding-JDBC自動(dòng)實(shí)現(xiàn)MySQL讀寫分離,優(yōu)點(diǎn)在于數(shù)據(jù)源完全有Sharding-JDBC托管,寫操作自動(dòng)執(zhí)行master庫(kù),讀操作自動(dòng)執(zhí)行slave庫(kù),感興趣的可以了解一下2021-11-11SpringBoot @PostConstruct原理用法解析
這篇文章主要介紹了SpringBoot @PostConstruct原理用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08詳解Java如何使用責(zé)任鏈默認(rèn)優(yōu)雅地進(jìn)行參數(shù)校驗(yàn)
項(xiàng)目中參數(shù)校驗(yàn)十分重要,它可以保護(hù)我們應(yīng)用程序的安全性和合法性。這篇文章主要介紹了如何使用責(zé)任鏈默認(rèn)優(yōu)雅地進(jìn)行參數(shù)校驗(yàn),需要的可以參考一下2023-03-03Java使用FutureTask實(shí)現(xiàn)預(yù)加載的示例詳解
基于FutureTask的特性,通??梢允褂肍utureTask做一些預(yù)加載工作,比如一些時(shí)間較長(zhǎng)的計(jì)算等,本文就來和大家講講具體實(shí)現(xiàn)方法吧,感興趣的可以了解一下2023-06-06JAVA中關(guān)于Long類型返回前端精度丟失問題處理辦法
這篇文章主要介紹了后端JavaBean的id屬性從Long類型改為雪花算法后出現(xiàn)的精度丟失問題,解決方案包括將id字段類型改為字符串或使用Jackson序列化方式,需要的朋友可以參考下2024-11-11