在spring boot 1.5.x中,resources/static目录下的静态资源能够直接访问,而且访问路径上不用带static,好比静态资源放置位置以下图所示:html
那么访问静态资源的路径能够是:java
当有配置自定义HandlerInterceptor拦截器时,请求以上静态资源路径不会被拦截。自定义HandlerInterceptor拦截器源码以下:spring
package com.itopener.demo.config; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; public class LoginRequiredInterceptor extends HandlerInterceptorAdapter { private final Logger logger = LoggerFactory.getLogger(LoginRequiredInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.info(request.getRequestURI()); return super.preHandle(request, response, handler); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info(request.getRequestURI()); super.afterCompletion(request, response, handler, ex); } }
配置以下:mvc
package com.itopener.demo.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class WebMvcConfiguration extends WebMvcConfigurerAdapter { private final Logger logger = LoggerFactory.getLogger(WebMvcConfiguration.class); @Override public void addInterceptors(InterceptorRegistry registry) { logger.info("add interceptors"); registry.addInterceptor(new LoginRequiredInterceptor()); } }
访问静态资源时路径上不用加static目录:app
当spring boot版本升级为2.x时,访问静态资源就会被HandlerInterceptor拦截ide
这样对于利用HandlerInterceptor来处理访问权限或其余相关的功能就会受影响,跟踪源码查看缘由,是由于spring boot 2.x依赖的spring 5.x版本,相对于spring boot 1.5.x依赖的spring 4.3.x版本而言,针对资源的拦截器初始化时有区别,具体源码在WebMvcConfigurationSupport中,spring 4.3.x源码以下:spring-boot
/** * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped * resource handlers. To configure resource handling, override * {@link #addResourceHandlers}. */ @Bean public HandlerMapping resourceHandlerMapping() { ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, this.servletContext, mvcContentNegotiationManager()); addResourceHandlers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); if (handlerMapping != null) { handlerMapping.setPathMatcher(mvcPathMatcher()); handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); // 此处固定添加了一个Interceptor handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); handlerMapping.setCorsConfigurations(getCorsConfigurations()); } else { handlerMapping = new EmptyHandlerMapping(); } return handlerMapping; }
而spring 5.x的源码以下:性能
/** * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped * resource handlers. To configure resource handling, override * {@link #addResourceHandlers}. */ @Bean public HandlerMapping resourceHandlerMapping() { Assert.state(this.applicationContext != null, "No ApplicationContext set"); Assert.state(this.servletContext != null, "No ServletContext set"); ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper()); addResourceHandlers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); if (handlerMapping != null) { handlerMapping.setPathMatcher(mvcPathMatcher()); handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); // 此处是将全部的HandlerInterceptor都添加了(包含自定义的HandlerInterceptor) handlerMapping.setInterceptors(getInterceptors()); handlerMapping.setCorsConfigurations(getCorsConfigurations()); } else { handlerMapping = new EmptyHandlerMapping(); } return handlerMapping; } /** * Provide access to the shared handler interceptors used to configure * {@link HandlerMapping} instances with. This method cannot be overridden, * use {@link #addInterceptors(InterceptorRegistry)} instead. */ protected final Object[] getInterceptors() { if (this.interceptors == null) { InterceptorRegistry registry = new InterceptorRegistry(); // 此处传入新new的registry对象,在配置类当中设置自定义的HandlerInterceptor后便可获取到 addInterceptors(registry); registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); this.interceptors = registry.getInterceptors(); } return this.interceptors.toArray(); }
从源码当中能够看出,使用spring 5.x时,静态资源也会执行自定义的拦截器,所以在配置拦截器的时候须要指定排除静态资源的访问路径,即配置改成以下便可:
package com.itopener.demo.config; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @author fuwei.deng * @date 2018年4月13日 下午3:32:54 * @version 1.0.0 */ @Configuration public class WebMvcConfiguration implements WebMvcConfigurer { private final Logger logger = LoggerFactory.getLogger(WebMvcConfiguration.class); @Override public void addInterceptors(InterceptorRegistry registry) { logger.info("add interceptors"); registry.addInterceptor(new LoginRequiredInterceptor()).excludePathPatterns(Arrays.asList("/views/**", "/res/**")); } }
这样就能够和spring boot 1.5.x同样的方式使用了。不过从源码当中能够看出,每一个静态资源的请求都会被自定义Interceptor拦截,只是经过访问路径判断后不会执行拦截器的内容,因此spring 5.x相对于spring 4.3.x而言,这部分处理的性能会更低一些
说明:
本文中测试使用的具体版本:
spring-boot-1.5.3.RELEASE(相对应的spring版本是spring-webmvc-4.3.8.RELEASE)
spring-boot-2.0.1.RELEASE(相对应的spring版本是spring-webmvc-5.0.5.RELEASE)
关于配置类,在spring boot 2.x已经改成最低支持jdk8版本,而jdk8中的接口容许有默认实现,因此已经废弃掉WebMvcConfigurerAdapter适配类,而改成直接实现WebMvcConfigurer接口