阅读源码,分析静态资源处理器相关组件:
- 使用SimpleUrlHandlerMapping管理url -> 处理器映射关系
- spring mvc使用WebMvcConfigurationSupport注入SimpleUrlHandlerMapping组件
- DelegatingWebMvcConfiguration可以使用WebMvcConfigurer的配置静态资源url pattern
- spring boot使用EnableWebMvcConfiguration类读取配置参数,然后ResourceHandlerRegistry添加静态资源url pattern
- SimpleUrlHandlerMapping中封装ResourceHttpRequestHandler对象作为请求处理器
- HttpRequestHandlerAdapter支持HttpRequestHandler类型的请求处理器
本文将对上述6点内容展开分析。
注入HandlerMapping
WebMvcConfigurationSupport注入SimpleUrlHandlerMapping组件
在spring mvc中,静态资源也使用HandlerMapping管理映射关系,只是使用的实现类不一样:
- 业务请求使用的HandlerMapping实现类是RequestMappingHandlerMapping类
- 静态资源使用的HandlerMapping实现类是SimpleUrlHandlerMapping类
注入SimpleUrlHandlerMapping对象的入口在WebMvcConfigurationSupport的resourceHandlerMapping方法中:
// Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped resource handlers.
// To configure resource handling, override addResourceHandlers.
@Bean
public HandlerMapping resourceHandlerMapping(
@Qualifier("mvcUrlPathHelper") UrlPathHelper urlPathHelper,
@Qualifier("mvcPathMatcher") PathMatcher pathMatcher,
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
this.servletContext, contentNegotiationManager, urlPathHelper);
// Add resource handlers for serving static resources
// 这是一个抽象方法,用于向registry添加静态资源路径pattern
// * 一个是spring mvc的DelegatingWebMvcConfiguration类
// * 一个是spring boot的WebMvcAutoConfiguration.EnableWebMvcConfiguration类
// * 两者的本质就是使用registry.addResourceHandler添加静态资源路径pattern
addResourceHandlers(registry);
// 根据添加的静态资源路径pattern创建HandlerMapping
// 里面创建的是SimpleUrlHandlerMapping类型对象
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
if (handlerMapping == null) {
return null;
}
handlerMapping.setPathMatcher(pathMatcher);
handlerMapping.setUrlPathHelper(urlPathHelper);
handlerMapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
handlerMapping.setCorsConfigurations(getCorsConfigurations());
return handlerMapping;
}
下文将介绍:
- addResourceHandlers的实现
- registry创建SimpleUrlHandlerMapping的方式
DelegatingWebMvcConfiguration的addResourceHandlers方法
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
this.configurers.addResourceHandlers(registry);
}
configurers是WebMvcConfigurerComposite对象,内部封装这容器里面的所有WebMvcConfigurer组件集:
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
当调用this.configurers.addResourceHandlers时,WebMvcConfigurerComposite又会通过WebMvcConfigurer添加静态资源路径pattern:
public void addResourceHandlers(ResourceHandlerRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
// 这里通过WebMvcConfigurer调用扩展逻辑,添加静态资源路径pattern
delegate.addResourceHandlers(registry);
}
}
例如:
@Component
public class AppConfig implements WebMvcConfigurer {
// ...
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
// ...
}
EnableWebMvcConfiguration的addResourceHandlers方法
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 调用父类方法,也就是使用WebMvcConfigurer添加静态资源pattern
super.addResourceHandlers(registry);
if (!this.resourceProperties.isAddMappings()) {
return;
}
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
// spring boot的spring.mvc.staticPathPattern配置参数
// spring boot的spring.resources.staticLocations配置参数
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(),
this.resourceProperties.getStaticLocations());
}
private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) {
if (registry.hasMappingForPattern(pattern)) {
return;
}
// 添加静态资源pattern
ResourceHandlerRegistration registration = registry.addResourceHandler(pattern);
registration.addResourceLocations(locations);
registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
customizeResourceHandlerRegistration(registration);
this.autoConfiguredResourceHandlers.add(pattern);
}
WebMvcConfigurer接口
定义callback方法,Spring MVC通过这些方法扩展组件:
public interface WebMvcConfigurer {
// 配置PathMatchConfigurer
default void configurePathMatch(PathMatchConfigurer configurer) {}
// Configure content negotiation options
default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}
// Configure asynchronous request handling options
default void configureAsyncSupport(AsyncSupportConfigurer configurer) {}
// 配置DefaultServletHttpRequestHandler
default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}
// Add Converters and Formatters in addition to the ones registered by default
default void addFormatters(FormatterRegistry registry) {}
// 添加拦截器
default void addInterceptors(InterceptorRegistry registry) {}
// 添加静态资源处理器
default void addResourceHandlers(ResourceHandlerRegistry registry) {}
// cors配置
default void addCorsMappings(CorsRegistry registry) {}
// 添加ViewController
default void addViewControllers(ViewControllerRegistry registry) {}
// 配置ViewResolver
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) {}
}
创建SimpleUrlHandlerMapping对象
protected AbstractHandlerMapping getHandlerMapping() {
Map<String, HttpRequestHandler> urlMap = new LinkedHashMap<>();
for (ResourceHandlerRegistration registration : this.registrations) {
for (String pathPattern : registration.getPathPatterns()) {
// * 请注意这里使用的是ResourceHttpRequestHandler
ResourceHttpRequestHandler handler = registration.getRequestHandler();
if (this.pathHelper != null) {
handler.setUrlPathHelper(this.pathHelper);
}
if (this.contentNegotiationManager != null) {
handler.setContentNegotiationManager(this.contentNegotiationManager);
}
handler.setServletContext(this.servletContext);
handler.setApplicationContext(this.applicationContext);
try {
handler.afterPropertiesSet();
} catch (Throwable ex) {
throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex);
}
urlMap.put(pathPattern, handler);
}
}
// 创建SimpleUrlHandlerMapping
return new SimpleUrlHandlerMapping(urlMap, this.order);
}
SimpleUrlHandlerMapping类
private final Map<String, Object> handlerMap = new LinkedHashMap<>();
使用一个Map<String, Object>管理url pattern -> 处理器对象的映射关系。
从上文可以知道,静态资源映射使用的是ResourceHttpRequestHandler类型的处理器。
ResourceHttpRequestHandler类
这个类实现了HttpRequestHandler接口:
@FunctionalInterface
public interface HttpRequestHandler {
void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
}
ResourceHttpRequestHandler的代码实现:
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Resource resource = getResource(request);
if (resource == null) {
// 404
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return;
}
// Supported methods and required session
checkRequest(request);
// Header phase
if (new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) {
return;
}
// Apply cache settings, if any
prepareResponse(response);
// Check the media type for the resource
MediaType mediaType = getMediaType(request, resource);
setHeaders(response, resource, mediaType);
// Content phase
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
// 不是range请求
if (request.getHeader(HttpHeaders.RANGE) == null) {
// 把资源写出去
this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);
} else {
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);
try {
// range请求
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
// 把资源写出去
this.resourceRegionHttpMessageConverter.write(
HttpRange.toResourceRegions(httpRanges, resource), mediaType, outputMessage);
} catch (IllegalArgumentException ex) {
response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
}
}
}
HttpRequestHandlerAdapter类
前面的文章已经介绍,HttpRequestHandlerAdapter负责处理HttpRequestHandler类型的处理器:
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
这个组件在WebMvcConfigurationSupport的httpRequestHandlerAdapter方法注入。
小结
至此,我们用了6篇文章详细介绍了SpringMVC DispatcherServlet的注册、请求转发、消息转换等内容,DispatcherServlet的源码分析到此可以结束了。