先来看WebMvc的自动配置类:WebMvcAutoConfiguration,它里面有个内部类:WebMvcAutoConfigurationAdapter
,其中有个方法addResourceHandlers
,实现如下
@Configuration
//引入EnableWebMvcConfiguration对象到容器中
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter
implements WebMvcConfigurer, ResourceLoaderAware {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//是否有注册"/webjars/**"这个路径
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
//是否有注册静态资源路径
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(
this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
}
}
首先,它将/webjars/**
注册,其次将默认的静态资源路径注册。在代码第20行,追踪发现变量staticPathPattern
就等于/**
,再看代码第26行,追踪对应的本地路径(见如下代码),就是springboot默认配置的资源路径。
public class WebMvcProperties {
private String staticPathPattern = "/**";
public String getStaticPathPattern() {
return this.staticPathPattern;
}
}
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
public String[] getStaticLocations() {
return this.staticLocations;
}
}
回到上面讲的WebMvcAutoConfigurationAdapter
类,它使用Import
注解引入EnableWebMvcConfiguration
对象到容器中。
EnableWebMvcConfiguration
也是WebMvcAutoConfiguration
的一个内部类,查看它的父类如下。
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
直接看EnableWebMvcConfiguration
的间接父类WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
/**
* Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
* resource handlers. To configure resource handling, override
* 返回一个优先级为Integer.MAX_VALUE-1,映射静态资源处理器的HandlerMapping
*/
@Bean
@Nullable
public HandlerMapping resourceHandlerMapping() {
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper());
//对静态资源路径进行注册
addResourceHandlers(registry);
//返回映射有静态资源路径的HandlerMapping
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
if (handlerMapping == null) {
return null;
}
handlerMapping.setPathMatcher(mvcPathMatcher());
handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
//添加所有拦截器
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setCorsConfigurations(getCorsConfigurations());
return handlerMapping;
}
}
类中有一个方法resourceHandlerMapping()
,它返回一个专门处理静态资源路径的处理器映射器。
见代码第14行addResourceHandlers(registry)
,其实最终它就是调用类WebMvcAutoConfigurationAdapter
中的方法addResourceHandlers
将资源路径注册。
见代码第17行,调用getHandlerMapping()
,查看该方法发现其将注册的资源路径形成映射放在handlerMapping
中,并返回该handlerMapping
。
见代码第23行,将所有拦截器(包括我们自定义的拦截器)添加到该handlerMapping
,这就是为什么我们自定义拦截器会拦截静态资源。而在springboot1.5中添加拦截器是这样子的
handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
并没有添加所有拦截器到该handlerMapping
,这也是springboot1.5不拦截静态资源的原因。
设置拦截路径时调用excludePathPatterns
方法进行排除。例如:
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/webjars/**","/index.html");
}
}