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-09
java中springMVC獲取請(qǐng)求參數(shù)的方法
這篇文章主要介紹了java中springMVC獲取請(qǐng)求參數(shù)的方法,springmvc是spring框架的一個(gè)模塊,springmvc和spring無需通過中間整合層進(jìn)行整合,需要的朋友可以參考下2023-05-05
Idea?中控制啟動(dòng)命令的詳細(xì)過程?區(qū)分環(huán)境案例詳解
這篇文章主要介紹了Idea?中控制啟動(dòng)命令的詳細(xì)過程?區(qū)分環(huán)境案例詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08
JavaFX如何獲取ListView(列表視圖)的選項(xiàng)
這篇文章主要介紹了JavaFX如何獲取ListView(列表視圖)的選項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
Sharding-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庫,讀操作自動(dòng)執(zhí)行slave庫,感興趣的可以了解一下2021-11-11
SpringBoot @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-03
Java使用FutureTask實(shí)現(xiàn)預(yù)加載的示例詳解
基于FutureTask的特性,通??梢允褂肍utureTask做一些預(yù)加載工作,比如一些時(shí)間較長的計(jì)算等,本文就來和大家講講具體實(shí)現(xiàn)方法吧,感興趣的可以了解一下2023-06-06
JAVA中關(guān)于Long類型返回前端精度丟失問題處理辦法
這篇文章主要介紹了后端JavaBean的id屬性從Long類型改為雪花算法后出現(xiàn)的精度丟失問題,解決方案包括將id字段類型改為字符串或使用Jackson序列化方式,需要的朋友可以參考下2024-11-11

