1. 程式人生 > 其它 >過濾器、攔截器與AOP的區別

過濾器、攔截器與AOP的區別

技術標籤:javawebspringspringmvcjavaaopspringfilter過濾器

侵刪,轉載自:SpringBoot 三種攔截http請求方式Filter,interceptor和aop

SpringBoot 三種攔截http請求方式Filter,interceptor和aop。

這三種攔截方式的攔截順序是:filter—>Interceptor–>ControllerAdvice–>@Aspect -->Controller;

這三種方式的區別:
1.過濾器Filter可以拿到原始的HTTP請求和響應的資訊, 但是拿不到你真正處理請求方法的資訊,也就是方法的資訊。

2.攔截器Interceptor可以拿到原始的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();//代理方法的返回值
    }
}