当前位置: 首页 > news >正文

SpringMVC DispatcherServlet源码(6) 完结 静态资源原理

阅读源码,分析静态资源处理器相关组件:

  1. 使用SimpleUrlHandlerMapping管理url -> 处理器映射关系
  2. spring mvc使用WebMvcConfigurationSupport注入SimpleUrlHandlerMapping组件
  3. DelegatingWebMvcConfiguration可以使用WebMvcConfigurer的配置静态资源url pattern
  4. spring boot使用EnableWebMvcConfiguration类读取配置参数,然后ResourceHandlerRegistry添加静态资源url pattern
  5. SimpleUrlHandlerMapping中封装ResourceHttpRequestHandler对象作为请求处理器
  6. 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添加静态资源路径patternaddResourceHandlers(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调用扩展逻辑,添加静态资源路径patterndelegate.addResourceHandlers(registry);}
}

例如:

@Component
public class AppConfig implements WebMvcConfigurer {// ...@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/static/**").addResourceLocations("/static/");}// ...
}

EnableWebMvcConfiguration的addResourceHandlers方法

protected void addResourceHandlers(ResourceHandlerRegistry registry) {// 调用父类方法,也就是使用WebMvcConfigurer添加静态资源patternsuper.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;}// 添加静态资源patternResourceHandlerRegistration 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 {// 配置PathMatchConfigurerdefault void configurePathMatch(PathMatchConfigurer configurer) {}// Configure content negotiation optionsdefault void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}// Configure asynchronous request handling optionsdefault void configureAsyncSupport(AsyncSupportConfigurer configurer) {}// 配置DefaultServletHttpRequestHandlerdefault void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}// Add Converters and Formatters in addition to the ones registered by defaultdefault void addFormatters(FormatterRegistry registry) {}// 添加拦截器default void addInterceptors(InterceptorRegistry registry) {}// 添加静态资源处理器default void addResourceHandlers(ResourceHandlerRegistry registry) {}// cors配置default void addCorsMappings(CorsRegistry registry) {}// 添加ViewControllerdefault void addViewControllers(ViewControllerRegistry registry) {}// 配置ViewResolverdefault 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()) {// * 请注意这里使用的是ResourceHttpRequestHandlerResourceHttpRequestHandler 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);}}// 创建SimpleUrlHandlerMappingreturn 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) {// 404response.sendError(HttpServletResponse.SC_NOT_FOUND);return;}if (HttpMethod.OPTIONS.matches(request.getMethod())) {response.setHeader("Allow", getAllowHeader());return;}// Supported methods and required sessioncheckRequest(request);// Header phaseif (new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) {return;}// Apply cache settings, if anyprepareResponse(response);// Check the media type for the resourceMediaType mediaType = getMediaType(request, resource);setHeaders(response, resource, mediaType);// Content phaseServletServerHttpResponse 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 {@Overridepublic boolean supports(Object handler) {return (handler instanceof HttpRequestHandler);}@Override@Nullablepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {((HttpRequestHandler) handler).handleRequest(request, response);return null;}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {if (handler instanceof LastModified) {return ((LastModified) handler).getLastModified(request);}return -1L;}
}

这个组件在WebMvcConfigurationSupport的httpRequestHandlerAdapter方法注入。

小结

至此,我们用了6篇文章详细介绍了SpringMVC DispatcherServlet的注册、请求转发、消息转换等内容,DispatcherServlet的源码分析到此可以结束了。

http://www.lryc.cn/news/25243.html

相关文章:

  • 2023年全国最新会计专业技术资格精选真题及答案9
  • Web3中文|把Web3装进口袋,Solana手机Saga有何魔力?
  • 【配电网优化】基于串行和并行ADMM算法的配电网优化研究(Matlab代码实现)
  • 数据结构初阶 -- 顺序表
  • uniapp:3分钟搞定在线推送uni.createPushMessage,uni.onPushMessage
  • C/C++开发,无可避免的多线程(篇一).跨平台并行编程姗姗来迟
  • 如何把照片的底色修改为想要的颜色
  • 【高效办公】批量生成固定模板的文件夹名称
  • redis的集群方式
  • 温控负荷的需求响应潜力评估及其协同优化管理研究(Matlab代码实现)
  • 模电学习9. MOS管使用入门
  • 【算法】【数组与矩阵模块】正数组中累加和为给定值的最长子数组长度,空间复杂度O(1)解法
  • 3.1.2 创建表
  • 使用netlify实现自动化部署前端项目(无服务器版本)
  • MATLAB点云数据处理(二十九):可视化点云之pcshow参数详解与快捷键操作
  • 顺序表——重置版
  • PyQt5自然语言处理入门案例笔记
  • 使用 CSS 替换表行颜色?
  • 智能家居控制系统
  • Linux 进程:fork()与vfork()的对比
  • 环境搭建02-Ubuntu16.04 安装CUDA和CUDNN、CUDA多版本替换
  • HOT100--(3)无重复字符的最长子串
  • vue keep-alive多层级路由支持
  • 从源码角度看React-Hydrate原理
  • ARM基础 -- 2
  • Java 类型转换
  • 【Java开发】JUC基础 05:线程通信/协作
  • 哪些工具可以实现在线ps的需求
  • 如何使用C2concealer生成随机化的C2 Malleable配置文件
  • 网络基础之IP地址和子网掩码