1. 程式人生 > >Java實現攔截HTTP請求的幾種方式

Java實現攔截HTTP請求的幾種方式

在Java的服務端開發當中,攔截器是很常見的業務場景,這裡對Java開發當中幾種常見的攔截器的實現方式進行記錄和分析。案例說明基於Spring Boot環境。

一:實現javax.servlet.Filter介面(使用過濾器方式攔截請求)

import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;
import java.util.Date;

@Component
public class TimeInterceptor implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("time filter init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("time filter start");
        long start = new Date().getTime();
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("time filter 耗時:"+(new Date().getTime()-start));
        System.out.println("time filter finish");
    }

    @Override
    public void destroy() {
        System.out.println("time filter destroy");
    }
}

如使用@Compent註解宣告不需要加入其它配置即可使得攔截器生效,但是預設攔截/*,會攔截所有請求。

二:使用@Bean注入自定義攔截器,依然上面的程式碼,去掉@Compent註解,建立TimeWebConfig配置類:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class TimeWebConfig {

    @Bean
    public FilterRegistrationBean timeFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        TimeInterceptor interceptor = new TimeInterceptor();
        registrationBean.setFilter(interceptor);
        List<String> urls = new ArrayList<>();
        urls.add("/user/*");
        registrationBean.setUrlPatterns(urls);
        return registrationBean;
    }
}

    上面這兩種攔截請求的實現是基於JavaEE提供的Filter介面實現的,缺點在於,該攔截器實際上是一個過濾器,執行程式碼的方法doFilter只提供了request,response等引數,當請求進入被過濾器攔截的時候,我們並不知道這個請求是由哪個控制器的哪個方法來執行的。

三:使用springMVC提供的攔截器,實現org.springframework.web.servlet.HandlerInterceptor介面:

建立自定義的攔截器:

import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

@Component
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        System.out.println("preHandler");
        System.out.println(((HandlerMethod) handler).getBean().getClass().getName());
        System.out.println(((HandlerMethod) handler).getMethod().getName());
        httpServletRequest.setAttribute("start", new Date().getTime());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandler");
        Long start = (Long) httpServletRequest.getAttribute("start");
        System.out.println("time interceptor 耗時:"+(new Date().getTime()-start));
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("afterCompletion");
        Long start = (Long) httpServletRequest.getAttribute("start");
        System.out.println("time interceptor 耗時:"+(new Date().getTime()-start));
        System.out.println("ex is:"+e);
    }
}

建立配置類:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private MyInterceptor interceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor).addPathPatterns("/user/*").excludePathPatterns("/blog/*");
    }
}

此種方式的攔截器當中我們能夠獲取攔截的請求對應的類和方法的相關資訊,缺點在於該handler物件無法獲取具體執行方法的引數資訊。

四:利用Spring的切面(AOP)實現攔截器:

引入jar包:

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

建立切片類:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.Date;

@Aspect
@Component
public class TimeAspect {

    @Around("execution(* com.qinker.controller.UserController.*(..))")
    public Object handlerControllerMethod(ProceedingJoinPoint point) throws Throwable {
        System.out.println("time aspect start");
        long start = new Date().getTime();
        Object[] args = point.getArgs();
        for (Object obj : args) {
            System.out.println("arg is:"+obj);
        }
        Object obj = point.proceed();//具體方法的返回值
        System.out.println("aspect 耗時:"+(new Date().getTime()-start));
        System.out.println("time aspect end");
        return obj;
    }

}
aspectj基於AOP實現的攔截器功能十分強大,具體詳解請參考spring官網網站的文件。