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
欢迎关注个人公众号一起交流学习: