文章目录
以计算服务处理时间为例,分解介绍三种拦截方式在spring boot环境下的配置,使用,以及优劣比较
Filter
Filter代码
public class TimeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("TimeFilter Init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long start = new Date().getTime();
System.out.println("TimeFilter start");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("TimeFilter end 总耗时:" + (new Date().getTime() - start));
}
@Override
public void destroy() {
System.out.println("TimeFilter destroy");
}
}
如何使Filter生效
- 方式一:可以通过@Component注解直接注入到容器中,此时该拦截器会默认拦截所有请求。
- 方式二:使用配置类,可以方便的自定义拦截路径。
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean timeFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
TimeFilter timeFilter = new TimeFilter();
registrationBean.setFilter(timeFilter);
// 自定义拦截路径到urls集合中
List<String> urls = new ArrayList<>();
urls.add("/*");
registrationBean.setUrlPatterns(urls);
return registrationBean;
}
}
拦截器特点
- 优点:Filter拦截的优先级最高
- 缺点:不能知晓要拦截的对象,以及方法信息。
Interceptor
Interceptor代码
public class TimeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("TimeInterceptor preHandle");
// 打印参数对象
System.out.println("TimeInterceptor " + o.getClass().getName());
// 将参数对象转成HandlerMethod对象即可获得拦截对象
System.out.println("TimeInterceptor " + ((HandlerMethod)o).getBean().getClass().getName());
// 获得拦截对象的方法名称
System.out.println("TimeInterceptor " + ((HandlerMethod)o).getMethod().getName());
httpServletRequest.setAttribute("startTime", new Date().getTime());
// 这里决定是否放行
return true;
}
/**
* 正常返回之后进入该方法
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
long startTime = (long) httpServletRequest.getAttribute("startTime");
System.out.println("TimeInterceptor耗时:" +
(new Date().getTime() - startTime));
}
/**
* 无论是否正常返回都会执行该对象
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("TimeInterceptor afterCompletion");
System.out.println("TimeInterceptor exception:" + e);
}
}
如何使Interceptor生效
只使用@Component注解将Interceptor注入到容器中并不能使拦截器生效。
- 使配置类继承WebMvcConfigurerAdapter类(spring boot2.0之后这个类过时,原因是2.0使用jdk8编写,接口中可以直接写default方法体,不再需要这种适配类。如果你使用2.0之后版本直接实现WebMvcConfigurer这个接口即可)
- 重写其addInterceptors方法
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
TimeInterceptor interceptor = new TimeInterceptor();
registry.addInterceptor(interceptor);
}
}
Interceptor特点
- 它的拦截顺序在Filter之后
- 优点:Interceptor可以获取拦截对象以及拦截方法
- 缺点:不能拦截到方法参数的具体值,因为在springmvc中,请求参数的封装实在preHandle之后进行的
Aspect
代码
@Aspect
@Component
public class TimeAspect {
@Around("execution(* cn.net.zhipeng.web.controller.*.*(..))")
public Object timeAspect(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long start = new Date().getTime();
System.out.println("TimeAspect Start");
for (Object arg : proceedingJoinPoint.getArgs()) {
System.out.println("TimeAspect arg: " + arg);
}
Object proceed = proceedingJoinPoint.proceed();
System.out.println("TimeAspect End 耗时:" + (new Date().getTime() - start));
return proceed;
}
}
切面的特点
- 它最后拦截到方法,因为在请求到达Controller时Aspect会生成它的的代理对象执行方法。这可以通过在拦截器处对目标类名称的打印验证。
- 优点:它可以获取到目标方法参数的具体值
- 缺点:无法获取到request和response对象
关于三种拦截方式的执行顺序
我们同时开启三种拦截,观察我们之前写的打印语句。
TimeFilter start
TimeInterceptor preHandle
TimeInterceptor org.springframework.web.method.HandlerMethod
TimeInterceptor cn.net.zhipeng.web.controller.UserController$$EnhancerBySpringCGLIB$$fad54f7
TimeInterceptor getInfo
TimeAspect Start
TimeAspect arg: 1
TimeAspect End 耗时:6
TimeInterceptor耗时:65
TimeInterceptor afterCompletion
TimeInterceptor exception:null
TimeFilter end 总耗时:86
可以发现执行顺序如下图:
- 访问时拦截顺序就是Filter->Interceptor->Aspect ->Controller。
- 目标方法执行时加入发生异常:异常的捕获顺序会加上ControllerAdvice这一层。也就是:Controller->Aspect ->ControllerAdvice->Interceptor->Filter。