過濾器、攔截器與AOP的區別
阿新 • • 發佈:2021-01-09
技術標籤:javawebspringspringmvcjavaaopspringfilter過濾器
侵刪,轉載自:SpringBoot 三種攔截http請求方式Filter,interceptor和aop
SpringBoot 三種攔截http請求方式Filter,interceptor和aop。
這三種攔截方式的攔截順序是:filter—>Interceptor–>ControllerAdvice–>@Aspect -->Controller;
這三種方式的區別:
1.過濾器Filter可以拿到原始的HTTP請求和響應的資訊, 但是拿不到你真正處理請求方法的資訊,也就是方法的資訊。
3.切片Aspect,既然Spring那麼支援AOP,可以拿到原始的HTTP請求和響應的資訊, 也可以拿到你真正處理請求方法的資訊,也可以傳進引數的那個值。
第一種方式:Filter
實現Filter介面
/**
* 自定義Filter
* 對請求的header 過濾token
*
* 過濾器Filter可以拿到原始的HTTP請求和響應的資訊,
* 但是拿不到你真正處理請求方法的資訊,也就是方法的資訊
*
* @Component 註解讓攔截器注入Bean,從而讓攔截器生效
* @WebFilter 配置攔截規則
*
* 攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller
*
*/
@Slf4j
@Component
@WebFilter(urlPatterns = {"/**"},filterName = "tokenAuthorFilter")
public class TokenFilter 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 {
log.info("TokenFilter doFilter 我攔截到了請求");
// log.info("TokenFilter doFilter",((HttpServletRequest)request).getHeader("token"));
chain.doFilter(request,response);//到下一個鏈
}
@Override
public void destroy() {
log.info("TokenFilter destroy");
}
}
第二種方式:攔截器 Interceptor
實現 HandlerInterceptor 介面,然後配置進Spring。
/**
* 自定義攔截器
* 自定義攔截器後,需要配置進Spring
*
* 攔截器Interceptor可以拿到原始的HTTP請求和響應的資訊,
* 也可以拿到你真正處理請求方法的資訊,但是拿不到傳進引數的那個值。
*
*攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller
*/
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
/**
* 在訪問Controller某個方法之前這個方法會被呼叫。
* @param request
* @param response
* @param handler
* @return false則表示不執行postHandle方法,true 表示執行postHandle方法
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("Token Interceptor preHandle {}","");
String token = request.getHeader("token");
log.info("Token Interceptor preHandle token :{}",token);
log.info("Token Interceptor preHandle uri {}",request.getRequestURL().toString());
//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());
}
//false則表示不執行postHandle方法,不執行下一步chain鏈,直接返回response
return true;
}
/**
* 請求處理之後進行呼叫,但是在檢視被渲染之前(Controller方法呼叫之後)
* preHandle方法處理之後這個方法會被呼叫,如果控制器Controller出現了異常,則不會執行此方法
* @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("Token Interceptor postHandle");
}
/**
* 不管有沒有異常,這個afterCompletion都會被呼叫
* @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("Token Interceptor afterCompletion");
}
}
配置進spring
/**
* TokenInterceptor 自定義攔截器後,需要配置進Spring
* 也可以mapping,跨域設定
*/
@Slf4j
@Configuration
public class TokenConfig implements WebMvcConfigurer {
@Autowired
TokenInterceptor tokenInterceptor;
/**
* 新增攔截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("TokenConfig addInterceptors tokenInterceptor");
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")//指定該類攔截的url
.excludePathPatterns( "/static/**");//過濾靜態資源
}
/**
* 如果實現了Filter跨域攔截,這個跨域無效
* 攔截器實現 跨域支援
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
log.info("TokenConfig addInterceptors addCorsMappings");
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT","OPTIONS","HEAD")
.allowedHeaders("*")
.maxAge(3600);
}
}
第三種方式 : aop攔截
pom.xml 新增Aop支援
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
/**
* pom.xml 新增Aop支援
* <dependency>
* <groupId>org.springframework.boot</groupId>
* <artifactId>spring-boot-starter-aop</artifactId>
* </dependency>
*
* 切片Aspect,既然Spring那麼支援AOP,可以拿到原始的HTTP請求和響應的資訊,
* 也可以拿到你真正處理請求方法的資訊,也可以傳進引數的那個值。
*
* 攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller
*/
@Slf4j
@Component //表示它是一個Spring的元件
@Aspect //表示它是一個切面
public class HttpAspect {
/**
* 通過ProceedingJoinPoint物件的getArgs()我們可以得到傳進來的引數。
* 通過ProceedingJoinPoint物件的proceed()我們可以得到拿到切面方法返回值的物件。
* @param pjp
* @return
* 環繞通知 首先是:包名 然後是: 類名 然後是方法名:方法名 括號內是:引數
*/
@Around("execution(* com.learn.jwttoken.controller.*.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
log.info("HttpAspect handleControllerMethod filter start");
//原始的HTTP請求和響應的資訊
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
//獲取當前執行的方法
Method targetMethod = methodSignature.getMethod();
log.info("當前執行的方法:{}",targetMethod.getName());
//獲取引數
Object[] objs = pjp.getArgs();
for (Object obj:objs){
log.info("引數:"+obj);
}
//獲取返回物件
Object object = pjp.proceed();
log.info("獲得返回物件 :{}",object);
log.info("HttpAspect handleControllerMethod filter end");
return pjp.proceed();//代理方法的返回值
}
}