下面以记录方法执行时间为例,分别演示几种拦截方式。
Filter是Web开发中一个十分常用的组件,一个Web应用可以注册多个Filter,它会按照配置的路径拦截Http请求,并进行相应的处理。
首先,编写TimeFilter类,实现过滤器逻辑:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/26 14:56
* @Description:
*/
public class TimeFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.err.println("TimeFilter拦截Rest服务");
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
System.err.println("方法执行时间: " + (endTime - startTime));
}
@Override
public void destroy() {
}
}
然后,注册该TimeFilter。在传统的JavaWeb开发中,一般是通过web.xml文件来配置Filter。而基于SpringBoot开发后,SpringBoot提供了FilterRegistrationBean来注册Filter,代码如下:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/26 14:59
* @Description:Filter配置
*/
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new TimeFilter());
String[] urlPatterns = {"/*"};
filterRegistrationBean.addUrlPatterns(urlPatterns);
return filterRegistrationBean;
}
}
访问服务,可以看到控制台输出如下:
TimeFilter拦截Rest服务
方法执行时间: 66
Interceptor,顾名思义,是一种拦截器,SpringMVC提供了Interceptor拦截Http访问的执行,并在Controller处理前后增加自定义的逻辑。SpringMVC推荐使用HandlerInterceptor进行拦截。
HandlerInterceptor接口包含三个方法,具体见源码:
public interface HandlerInterceptor {
/**
* 在Handler的方法执行前置处理
* @param request
* @param response
* @param handler chosen handler to execute, for type and/or instance evaluation
* @return 如果返回true,则继续执行Handler的目标方法,否则直接返回。
* @throws Exception 如果抛出异常,则目标方法无法继续执行。
*/
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* 目标方法执行后置处理
*/
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
/**
* 最终处理,无论目标方法执行成功还是失败,都会回调该方法
*/
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
首先,开发TimeInterceptor,实现HandlerInterceptor接口:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/26 15:50
* @Description:自定义Interceptor拦截器
*/
public class TimeInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.err.println("TimeInterceptor:拦截方法执行");
request.setAttribute("startTime",System.currentTimeMillis());
if (handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod)handler;
System.err.println("拦截目标对象: " + handlerMethod.getBean() + ",目标方法: " + handlerMethod.getMethod().getName());
}
System.err.println(handler);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
Long startTime = (Long)request.getAttribute("startTime");
System.err.println("方法执行时间: " + (System.currentTimeMillis() - startTime));
}
}
下面,注册TimeInterceptor:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/26 16:10
* @Description:Interceptor配置
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TimeInterceptor());
}
}
访问REST服务,可看到控制台输出:
TimeInterceptor:拦截方法执行
拦截目标对象: william.security.demo.controller.UserController@5cb2fc03,目标方法: getById
public william.security.demo.dto.UserDto william.security.demo.controller.UserController.getById(long)
方法执行时间: 146
Aspect基于Spring提供的AOP功能,提供了强大的面向切面编程的支持。AOP的思想和Spring AOP的原理这里不展开叙述,仅演示下怎么使用Aspect拦截REST服务。
首先,开发Aspect切面类,并指定切入点表达式:
@Aspect
@Component
public class TimeAspect {
@Around("execution(* william.security.demo.controller.UserController.*(..))")
public Object interceptMethodRuntime(ProceedingJoinPoint joinPoint) throws Throwable {
System.err.println("TimeAspect:拦截方法执行,目标对象: " + joinPoint.getTarget() +
",目标方法: " + joinPoint.getSignature().getName() + ",方法参数: " + joinPoint.getArgs());
long startTime = System.currentTimeMillis();
Object retVal = joinPoint.proceed();
System.err.println("方法执行时间: " + (System.currentTimeMillis() - startTime));
return retVal;
}
}
这里使用了@Around环绕通知,可以在目标方法前后都织入我们自定义的处理。
访问REST服务,查看控制台:
TimeAspect:拦截方法执行,目标对象: william.security.demo.controller.UserController@3842f7e0,目标方法: getById,方法参数: [Ljava.lang.Object;@4641d47c
方法执行时间: 7