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;@Configurationpublic class WebConfig { @Bean public FilterRegistrationBean timeFilter(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); TimeFilter timeFilter = new TimeFilter(); filterRegistrationBean.setFilter(timeFilter); //指定要过滤的url Listurls = 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;@Configurationpublic 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 Listurls = new ArrayList<>(); urls.add("/*"); filterRegistrationBean.setUrlPatterns(urls); return filterRegistrationBean; }}
调用http://localhost:8080/user/1
可以看到能获取到所调用的Controller以及哪个方法:
time filter start
preHandle...com.imooc.web.controller.UserControllerpublic com.imooc.dto.User com.imooc.web.controller.UserController.getInfo(java.lang.String)afterCompletion...time interceptor 耗时:34exception:nulltime filter 耗时:50time filter endtime filter starttime filter 耗时:15time 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@Componentpublic 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$$533f819cpublic com.imooc.dto.User com.imooc.web.controller.UserController.getInfo(java.lang.String)>>>> 进入 TimeAspect start >>>>>arg is 1>>>>>>进入User Controller --> getInfo 方法1TimeAspect 调用耗时:0>>>> TimeAspect 结束 >>>>>postHandle...time interceptor 耗时:1time interceptor 耗时:1exception:null+++++ afterCompletion +++++++time filter 耗时:5=======time filter end==============time filter start======time filter 耗时:2=======time filter end=======三者调用顺序:
代码github: