Restful API的拦截:

  1,过滤器(Filter)

  2,拦截器(Interceptor)

  3,切片(Aspect)

1,过滤器

和传统javaweb一鸟样,例,记录controller执行时间过滤器,会过滤所有url:

/**
 * 记录执行时间过滤器
 * ClassName: TimeFilter 
 * @Description: TODO
 * @author lihaoyang
 * @date 2018年2月26日
 */
@Component //声明为spring组件,springboot项目没有web.xml直接声明为组件
public class TimeFilter implements Filter {

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.err.println("time filter start");
        long startTime = new Date().getTime();
        chain.doFilter(request, response); //执行其他过滤器链
        System.err.println("time filter 耗时:"+(new Date().getTime()-startTime));
        System.err.println("time filter end");
    }

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

}

在传统javaweb我们需要在web.xml配置过滤器,springboot没有web.xml,只需要在类上加上@Component注解告诉spring这是一个组件即可。如果我们需要引入第三方的一些过滤器,是没办法加注解的,此时就需要使用java来代替配置文件了:

假设自定义的TimeFilter即为第三方Filter,注释掉@Component注解

只需加上一个配置类,相当于xml配置即可:

package com.imooc.web.config;

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

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

import com.imooc.web.filter.TimeFilter;


@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean timeFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        
        TimeFilter timeFilter = new TimeFilter();
        filterRegistrationBean.setFilter(timeFilter);
        //指定要过滤的url
        List<String> urls = new ArrayList<>();
        urls.add("/*");
        filterRegistrationBean.setUrlPatterns(urls);
        return filterRegistrationBean;
    }
}

filter只能对request和response进行操作,因为他是J2EE的规范,所以这个请求最终是哪一个controller的哪个方法来处理的是不知道的。因为controller是spring的概念。如果需要知道这些信息,就需要用拦截器。

2,拦截器

自定义拦截器,加上@Component 声明为spring组件,记录调用耗时:

package com.imooc.web.interceptor;

import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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

/**
 * 记录调用耗时的拦截器
 * ClassName: TimeInterceptor 
 * @Description: TODO
 * @author lihaoyang
 * @date 2018年2月26日
 */
@Component //声明为spring组件
public class TimeInterceptor implements HandlerInterceptor{

    //进入controller之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.err.println("preHandle...");
        
        System.err.println(((HandlerMethod)handler).getBean().getClass().getName());//哪个类
        System.err.println(((HandlerMethod)handler).getMethod()); //哪个方法
        request.setAttribute("startTime", new Date().getTime());//调用开始计时
        return true;
    }

    //进入controller之中执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
        System.err.println("postHandle...");
        Long start = (Long) request.getAttribute("startTime");
        System.err.println("time interceptor 耗时:"+(new Date().getTime()-start));
    }

    //controller执行完之后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        
        System.err.println("afterCompletion...");
        Long start = (Long) request.getAttribute("startTime");
        System.err.println("time interceptor 耗时:"+(new Date().getTime()-start));
        //如果调用有异常,这个Exception会有信息
        System.err.println("exception:"+ex);
    }

}

java配置,继承WebMvcConfigurerAdapter类重写addInterceptors方法,定义自己的拦截器

package com.imooc.web.config;

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

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

import com.imooc.web.filter.TimeFilter;
import com.imooc.web.interceptor.TimeInterceptor;


@Configuration
public class WebConfig extends WebMvcConfigurerAdapter{

    //由于TimeInterceptor声明为了spring组件,直接注入进来
    @Autowired
    private TimeInterceptor timeInterceptor;
    /**
     * 实现WebMvcConfigurerAdapter,重写addInterceptors方法添加拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(timeInterceptor);
    }

    @Bean
    public FilterRegistrationBean timeFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        
        TimeFilter timeFilter = new TimeFilter();
        filterRegistrationBean.setFilter(timeFilter);
        //指定要过滤的url
        List<String> urls = new ArrayList<>();
        urls.add("/*");
        filterRegistrationBean.setUrlPatterns(urls);
        return filterRegistrationBean;
    }
}

调用http://localhost:8080/user/1

 

可以看到能获取到所调用的Controller以及哪个方法:

time filter start
preHandle...
com.imooc.web.controller.UserController
public com.imooc.dto.User com.imooc.web.controller.UserController.getInfo(java.lang.String)
afterCompletion...
time interceptor 耗时:34
exception:null
time filter 耗时:50
time filter end
time filter start
time filter 耗时:15
time filter end

 注意,如果是抛出了自定义异常,做过了处理,afterCompletion里就不会打印异常信息了。

拦截器能获取到所要处理的控制器类和方法,但是没办法获取到具体要处理的参数,查看DispatcherServlet源码的doDispatch方法就可以看到,执行拦截器的时候,还没有组装参数,把请求的参数映射成为contrller里的参数,所以取不到具体的参数值,如果想获取参数值,就需要用到第三个拦截机制spring的AOP,自定义切面

3,切片

TimeAspect切面类:

package com.imooc.web.aspect;

import java.util.Date;

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

/**
 * 调用耗时切片
 * ClassName: TimeAspect 
 * @Description: TODO
 * @author lihaoyang
 * @date 2018年2月26日
 */
@Aspect
@Component
public class TimeAspect {

    /**
     * 
     */
    @Around("execution(* com.imooc.web.controller.UserController.*(..))")
    public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable{
        System.err.println(">>>> 进入  TimeAspect start  >>>>>");
        
        Object[] args = pjp.getArgs();
        if(args.length > 0){
            for (Object arg : args) {
                System.err.println("arg is "+arg);
            }
        }
        long start = new Date().getTime();
        
        Object object = pjp.proceed();
        System.err.println("TimeAspect 调用耗时:"+(new Date().getTime()-start));
        System.err.println(">>>>   TimeAspect 结束  >>>>>");
        
        return object;
    }
}

访问:http://localhost:8080/user/1可以看到能够取到参数1

=======time filter start======
++++++ 进入 preHandle...+++++++
com.imooc.web.controller.UserController$$EnhancerBySpringCGLIB$$533f819c
public com.imooc.dto.User com.imooc.web.controller.UserController.getInfo(java.lang.String)
>>>> 进入 TimeAspect start >>>>>
arg is 1
>>>>>>进入User Controller --> getInfo 方法
1
TimeAspect 调用耗时:0
>>>> TimeAspect 结束 >>>>>
postHandle...
time interceptor 耗时:1
time interceptor 耗时:1
exception:null
+++++ afterCompletion +++++++
time filter 耗时:5
=======time filter end=======
=======time filter start======
time filter 耗时:2
=======time filter end=======

三者调用顺序:

 代码github:https://github.com/lhy1234/spring-security

 

欢迎关注个人公众号一起交流学习: