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

29 篇文章 1 订阅
6 篇文章 0 订阅

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

  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添加静态资源路径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的源码分析到此可以结束了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值