若要实现对请求的过滤,有三种方式可供选择:filter、interceptort和aop。本文主要讨论三种拦截器的使用场景与使用方式。
下文中的举例功能是计算每个请求的从开始到结束的时间,例子来源是慕课网。
一、filter
特点:可以获取原始的ServletRequest,但无法获取具体方法
实现:
1.继承javax.servlet.Filter类,
2.@Component注解将其注入到框架中
3.实现其中的dofilter方法,所有的请求都会经过该方法,可以在此计算出每个请求的耗时,代码如下:
1 package com.zzy.web.filter; 2 3 import java.io.IOException; 4 import java.util.Date; 5 6 import javax.servlet.Filter; 7 import javax.servlet.FilterChain; 8 import javax.servlet.FilterConfig; 9 import javax.servlet.ServletException;10 import javax.servlet.ServletRequest;11 import javax.servlet.ServletResponse;12 13 import org.springframework.stereotype.Component;14 15 @Component16 public class TimeFilter implements Filter{17 18 @Override19 public void init(FilterConfig filterConfig) throws ServletException {20 // TODO Auto-generated method stub21 System.out.println("filter init");22 23 }24 25 @Override26 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)27 throws IOException, ServletException {28 // TODO Auto-generated method stub29 Long startTime = new Date().getTime();30 System.out.println("filter 请求开始时间:"+ startTime);31 chain.doFilter(request, response);32 Long endTime = new Date().getTime();33 System.out.println("filter 请求结束时间:" + endTime +",请求耗时:" + (endTime - startTime));34 35 }36 37 @Override38 public void destroy() {39 // TODO Auto-generated method stub40 41 }42 43 }
注:如果有的框架没有@Component 这个注解,可以自己写一个配置类,在该类中指定过滤器,而且还可以指定过滤的url,配置类如下:
1 package com.zzy.web.config; 2 3 import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; 4 5 import java.util.ArrayList; 6 import java.util.List; 7 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.boot.web.servlet.FilterRegistrationBean;10 import org.springframework.context.annotation.Bean;11 import org.springframework.context.annotation.Configuration;12 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;13 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;14 15 import com.zzy.web.filter.TimeFilter;16 import com.zzy.web.interceptor.TimeInterceptor;17 18 @Configuration19 public class WebConfig extends WebMvcConfigurerAdapter {20 21 @Autowired22 private TimeInterceptor timeInterceptor;23 24 // @Override25 // public void addInterceptors(InterceptorRegistry registry) {26 // // TODO Auto-generated method stub27 // registry.addInterceptor(timeInterceptor);28 // }29 30 @Bean31 public FilterRegistrationBean timeFilter() {32 FilterRegistrationBean registrationBean = new FilterRegistrationBean();33 TimeFilter timeFilter = new TimeFilter();34 registrationBean.setFilter(timeFilter);35 Listurls = new ArrayList<>();36 urls.add("/user/*");37 registrationBean.setUrlPatterns(urls);38 return registrationBean;39 }40 41 }
二、interceptor
特点:可以获取到原始的request和请求的方法,但无法获取方法的具体参数的值。
实现:
1.继承HandlerInterceptor接口
2.请求前的逻辑写在prehandle(请求前调用)
3.请求后的逻辑写在posthandle(请求成功后调用,失败则不调用)
4.请求后,不管成功失败都会调用aftercompletion。
5.intceptor方式继承了之后还没起作用,还需要在配置类里面加一下,把刚声明的拦截器注册一下。
代码示例:
1 package com.zzy.web.interceptor; 2 3 import java.util.Arrays; 4 import java.util.Date; 5 6 import javax.servlet.http.HttpServletRequest; 7 import javax.servlet.http.HttpServletResponse; 8 9 import org.springframework.stereotype.Component;10 import org.springframework.web.method.HandlerMethod;11 import org.springframework.web.servlet.HandlerInterceptor;12 import org.springframework.web.servlet.ModelAndView;13 @Component14 public class TimeInterceptor implements HandlerInterceptor {15 16 @Override17 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)18 throws Exception {19 // TODO Auto-generated method stub20 System.out.println("interceptor 执行preHandle");21 22 request.setAttribute("startTime", new Date().getTime());23 return true;24 }25 26 @Override27 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,28 ModelAndView modelAndView) throws Exception {29 // TODO Auto-generated method stub30 Long startTime = Long.parseLong(request.getAttribute("startTime").toString());31 Long endTime = new Date().getTime();32 System.out.println("interceptor 执行postHandle");33 System.out.println("interceptor 请求类:"+((HandlerMethod)handler).getBean().getClass().getName());34 System.out.println("interceptor 请求方法:"+((HandlerMethod)handler).getMethod());35 // System.out.println("interceptor 请求参数:");36 // Arrays.asList(((HandlerMethod)handler).getMethodParameters()).stream().forEach(arg->System.out.println(arg));37 System.out.println("interceptor 请求耗时:" + (endTime - startTime));38 39 }40 41 @Override42 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)43 throws Exception {44 // TODO Auto-generated method stub45 Long startTime = Long.parseLong(request.getAttribute("startTime").toString());46 Long endTime = new Date().getTime();47 System.out.println("interceptor 执行afterCompletion");48 System.out.println("interceptor 请求类:"+((HandlerMethod)handler).getBean().getClass().getName());49 System.out.println("interceptor 请求方法:"+((HandlerMethod)handler).getMethod());50 System.out.println("interceptor 请求耗时:" + (endTime - startTime));51 System.out.println("interceptor 请求异常:" + ex);52 53 }54 55 }
配置类如下:
1 package com.zzy.web.config; 2 3 import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; 4 5 import java.util.ArrayList; 6 import java.util.List; 7 8 import org.springframework.beans.factory.annotation.Autowired; 9 import org.springframework.boot.web.servlet.FilterRegistrationBean;10 import org.springframework.context.annotation.Bean;11 import org.springframework.context.annotation.Configuration;12 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;13 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;14 15 import com.zzy.web.filter.TimeFilter;16 import com.zzy.web.interceptor.TimeInterceptor;17 18 @Configuration19 public class WebConfig extends WebMvcConfigurerAdapter {20 21 @Autowired22 private TimeInterceptor timeInterceptor;23 24 @Override25 public void addInterceptors(InterceptorRegistry registry) {26 // TODO Auto-generated method stub27 registry.addInterceptor(timeInterceptor);28 }29 30 31 }
三、aop
特点:能拿到方法和具体参数的值,但是拿不到原始的servletrequest的信息。
实现:
1.使用@aspect注解
2.@execution声明切面,声明切面的语法可参考官网https://docs.spring.io/spring/docs/4.3.18.RELEASE/spring-framework-reference/htmlsingle/#aop-pointcuts
3.使用@Around(方法前和方法后),@Before(方法前)或@After(方法后)
以下为@Around举例,代码如下:
1 package com.zzy.web.aspect; 2 3 import java.util.Date; 4 5 import org.aspectj.lang.ProceedingJoinPoint; 6 import org.aspectj.lang.annotation.Around; 7 import org.aspectj.lang.annotation.Aspect; 8 import org.codehaus.jackson.map.ObjectMapper; 9 import org.springframework.stereotype.Component;10 11 @Aspect12 @Component13 public class TimeAspect {14 15 @Around("execution(* com.zzy.web.controller.UserController.*(..))")16 public Object test(ProceedingJoinPoint pjp) throws Throwable {17 Long startTime = new Date().getTime();18 Object[] args = pjp.getArgs();19 for (Object arg : args) {20 System.out.println("aspect 参数:" + arg);21 }22 Object object = pjp.proceed();23 System.out.println("aspect 请求耗时:" + (new Date().getTime() - startTime));24 System.out.println("aspect 请求结果:" + new ObjectMapper().writeValueAsString(object));25 26 return object;27 28 }29 }
下图是三种方式的拦截顺序,图片来自慕课网: