HandlerInterceptor
是SpringFramework为我们提供的拦截器,一般我们可以用来鉴权或者日志记录等。
它是一个interface,主要方法有:
/**
* Intercept the execution of a handler. Called after HandlerMapping determined
* an appropriate handler object, but before HandlerAdapter invokes the handler.
* <p>DispatcherServlet processes a handler in an execution chain, consisting
* of any number of interceptors, with the handler itself at the end.
* With this method, each interceptor can decide to abort the execution chain,
* typically sending a HTTP error or writing a custom response.
* @param request current HTTP request
* @param response current HTTP response
* @param handler chosen handler to execute, for type and/or instance evaluation
* @return {@code true} true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断
*(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应
* @throws Exception in case of errors
*/
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
/**
* 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
* Intercept the execution of a handler. Called after HandlerAdapter actually
* invoked the handler, but before the DispatcherServlet renders the view.
* Can expose additional model objects to the view via the given ModelAndView.
* <p>DispatcherServlet processes a handler in an execution chain, consisting
* of any number of interceptors, with the handler itself at the end.
* With this method, each interceptor can post-process an execution,
* getting applied in inverse order of the execution chain.
* @param request current HTTP request
* @param response current HTTP response
* @param handler handler (or {@link HandlerMethod}) that started async
* execution, for type and/or instance examination
* @param modelAndView the {@code ModelAndView} that the handler returned
* (can also be {@code null})
* @throws Exception in case of errors
*/
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
/**
* Callback after completion of request processing, that is, after rendering
* the view. Will be called on any outcome of handler execution, thus allows
* for proper resource cleanup.
* <p>Note: Will only be called if this interceptor's {@code preHandle}
* method has successfully completed and returned {@code true}!
* <p>As with the {@code postHandle} method, the method will be invoked on each
* interceptor in the chain in reverse order, so the first interceptor will be
* the last to be invoked.
* @param request current HTTP request
* @param response current HTTP response
* @param handler handler (or {@link HandlerMethod}) that started async
* execution, for type and/or instance examination
* @param ex exception thrown on handler execution, if any
* @throws Exception in case of errors
*/
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
-
preHandle 预处理回调,在处理器处理之前回调
-
postHandle 处理器处理完毕,在渲染之前回调,可以计算处理器处理时间
-
afterCompletion 请求渲染结束后回调 这边我们可以计算渲染时间,或者整个请求的执行时间
拦截器适配器HandlerInterceptorAdapter
有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor接口的话,三个方法必须实现,不管你需不需要,此时spring提供了一个HandlerInterceptorAdapter适配器(适配器设计模式的实现),允许我们只实现需要的回调方法。
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
/**
* This implementation always returns {@code true}.
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* This implementation is empty.
*/
@Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
/**
* This implementation is empty.
*/
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
/**
* This implementation is empty.
* 当Controller中有异步请求方法的时候会触发该方法。 楼主做过测试,异步请求先支持preHandle、然后执行afterConcurrentHandlingStarted
*/
@Override
public void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
}
}
运行流程总结如下:
1、拦截器执行顺序是按照Spring配置文件中定义的顺序而定的。
2、会先按照顺序执行所有拦截器的preHandle方法,一直遇到return false为止,比如第二个preHandle方法是return false,则第三个以及以后所有拦截器都不会执行。若都是return true,则按顺序加载完preHandle方法。
3、然后执行主方法(自己的controller接口),若中间抛出异常,则跟return false效果一致,不会继续执行postHandle,只会倒序执行afterCompletion方法。
4、在主方法执行完业务逻辑(页面还未渲染数据)时,按倒序执行postHandle方法。若第三个拦截器的preHandle方法return false,则会执行第二个和第一个的postHandle方法和afterCompletion(postHandle都执行完才会执行这个,也就是页面渲染完数据后,执行after进行清理工作)方法。(postHandle和afterCompletion都是倒序执行
接口适配器模式
其实从上面的HandlerInterceptor到HandlerInterceptorAdapter,我们可以看出这是使用了接口适配器模式,接口Adapter对接口进行实现,但是又不做具体的逻辑实现。
这样我们在使用的时候只要继承对应的Adapter复写需要的方法即可
Java8 default interface
上面那种在Java8之前经常会用到,可是在Java8新增default方法后,就很少用到了。前提最低支持Java8
Java8的新特性中有一个新特性为接口默认方法,该新特性允许我们在接口中添加一个非抽象的方法实现,而这样做的方法只需要使用关键字default修饰该默认实现方法即可。该特性又叫扩展方法
public interface Formula {
double calculate(int a);
default double sqrt(int a){
return Math.sqrt(a);
}
}
通过该特性,我们将能够很方便的实现接口默认实现类。这个特性在编译器实现的角度来说更接近于Scala的trait
SpringBoot2.0因为最低支持Java8后一些改变
如果大家有用过SpringBoot2.0会发现有大量的方法或者类被重新设计,当然一部分是对1.0的接口优化或者功能优化。
其中一个很重要的是对JDK的要求,也就是说Spring Boot2.0的最低版本要求为JDK8。从代码中我们可以看出,很多地方使用了default方法,对于老的接口Adapter进行了舍弃。
比如我们以前在配置相应的HandlerInterceptor使用的WebMvcConfigurerAdapter,就被标记为Deprecated
/**
* An implementation of {@link WebMvcConfigurer} with empty methods allowing
* subclasses to override only the methods they're interested in.
*
* @author Rossen Stoyanchev
* @since 3.1
* @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made
* possible by a Java 8 baseline) and can be implemented directly without the
* need for this adapter
*/
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer
接下来我们来看下WebMvcConfigurer在SpringBoot2.0和SpringBoot1.0中的代码区别:
SpringBoot2.0中使用的SpringFramework5
public interface WebMvcConfigurer {
default void configurePathMatch(PathMatchConfigurer configurer) {
}
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
}
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {
}
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
}
default void addFormatters(FormatterRegistry registry) {
}
default void addInterceptors(InterceptorRegistry registry) {
}
default void addResourceHandlers(ResourceHandlerRegistry registry) {
}
default void addCorsMappings(CorsRegistry registry) {
}
default void addViewControllers(ViewControllerRegistry registry) {
}
default void configureViewResolvers(ViewResolverRegistry registry) {
}
default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
}
default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
}
default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
}
@Nullable
default Validator getValidator() {
return null;
}
@Nullable
default MessageCodesResolver getMessageCodesResolver() {
return null;
}
}
SpringBoot1.0中使用的SpringFramework4
public interface WebMvcConfigurer {
void addFormatters(FormatterRegistry registry);
void configureMessageConverters(List<HttpMessageConverter<?>> converters);
void extendMessageConverters(List<HttpMessageConverter<?>> converters);
Validator getValidator();
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
void configureAsyncSupport(AsyncSupportConfigurer configurer);
void configurePathMatch(PathMatchConfigurer configurer);
void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers);
void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers);
void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers);
void addInterceptors(InterceptorRegistry registry);
MessageCodesResolver getMessageCodesResolver();
void addViewControllers(ViewControllerRegistry registry);
void configureViewResolvers(ViewResolverRegistry registry);
void addResourceHandlers(ResourceHandlerRegistry registry);
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
}
其中唯一的区别就是在SpringFramework5中使用了default关键字,导致的结果就是WebMvcConfigurerAdapter可以被舍弃不使用了。
当然在Spring中的例子还有很多,比如HandlerInterceptor和HandlerInterceptorAdapter等,大家在使用的时候除了要看相应的接口变化,更重要的是要去了解为什么会有这种变化
结果导致SpringBoot2.0中WebMvcConfigurer配置方式变化
@Configuration
public class WebConf implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AsyncInterceptor()).addPathPatterns("/*");
}
}
来源:oschina
链接:https://my.oschina.net/u/2365112/blog/2087975