SpringBoot静态资源访问URL映射的初始化过程及请求响应流程浅析

本文深入解析了SpringBoot中静态资源URL映射的初始化过程,包括DelegatingWebMvcConfiguration如何添加ResourceHandlerRegistry,如何初始化HandlerMapping,并注册到SimpleUrlHandlerMapping。此外,还探讨了SpringBoot响应静态资源的处理流程,以及如何拓展SpringMVC的静态资源处理配置,包括自定义WebMvcConfigurer,以及如何配置将静态资源交由WEB应用服务器处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringBoot对静态资源URL映射的初始化

DelegatingWebMvcConfiguration是一个springboot的MVC配置管理类,继承父类WebMvcConfigurationSupport

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

        // 自动装配实现了WebMvcConfigurer的配置bean
	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
                    // 装配到WebMvcConfigurerComposite
                    this.configurers.addWebMvcConfigurers(configurers);
		}
	}
    
        /** ...省略若干 */
	
        @Override
	protected void addResourceHandlers(ResourceHandlerRegistry registry) {
		this.configurers.addResourceHandlers(registry);
	}

        /** ...省略若干 */

}

DelegatingWebMvcConfiguration在初始化时将所有实现了WebMvcConfigurer接口的配置bean装配到WebMvcConfigurerComposite中,在各个配置方法中均间接调用了WebMvcConfigurerComposite的配置方法。由于其继承了WebMvcConfigurationSupport所以在初始化时也会产生各种配置bean其中就包含了一个对静态资源处理的bean:

/**
 * 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());
        // 此处初始化对静态资源处理的ResourceHandlerRegistry实例(在父类中是抽象方法,此处调用的是DelegatingWebMvcConfiguration自己的实现)
        addResourceHandlers(registry);
        // 此处初始化url -> handler映射的HandlerMapping实例(实际是SimpleUrlHandlerMapping)
	AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
	if (handlerMapping != null) {
		handlerMapping.setPathMatcher(mvcPathMatcher());
		handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
		handlerMapping.setInterceptors(getInterceptors());
		handlerMapping.setCorsConfigurations(getCorsConfigurations());
	}
	else {
            // 如果registry中没有注册任何url映射则会返回默认HandlerMapping实例(即EmptyHandlerMapping)
            // 这个EmptyHandlerMapping不会处理任何url均映射
            handlerMapping = new EmptyHandlerMapping();
	}
	return handlerMapping;
}

下面主要从3个点来分析一下resourceHandlerMapping构建HandlerMapping实例的过程:

初始化ResourceHandlerRegistry

DelegatingWebMvcConfiguration初始化ResourceHandlerRegistry实例是通过addResourceHandlers()方法,实际是调用了WebMvcConfigurerComposite的addResourceHandlers()方法并对所有WebMvcConfigurer实例调用addResourceHandlers()方法,如下:

/**
 * A {@link WebMvcConfigurer} that delegates to one or more others.
 *
 * @author Rossen Stoyanchev
 * @since 3.1
 */
class WebMvcConfigurerComposite implements WebMvcConfigurer {

	private final List<WebMvcConfigurer> delegates = new ArrayList<>();


	public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			this.delegates.addAll(configurers);
		}
	}

        /** 省略若干... */

	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		for (WebMvcConfigurer delegate : this.delegates) {
			delegate.addResourceHandlers(registry);
		}
	}
        /** 省略若干... */
}

springboot提供了一个WebMvcAutoConfiguration配置bean,对springMVC做了默认配置定制,其中的静态内部类WebMvcAutoConfigurationAdapter实现了WebMvcConfigurer并在addResourceHandlers方法中对静态资源的ResourceHandlerRegistry进行了初始化:

// Defined as a nested config to ensure WebMvcConfigurer is not read when not on the classpath
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {

	private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);

	private final ResourceProperties resourceProperties;

	private final WebMvcProperties mvcProperties;

	private final ListableBeanFactory beanFactory;

	private final HttpMessageConverters messageConverters;

	final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;

	private ResourceLoader resourceLoader;

	public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
			WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
			@Lazy HttpMessageConverters messageConverters,
			ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
		this.resourceProperties = resourceProperties;
		this.mvcProperties = mvcProperties;
		this.beanFactory = beanFactory;
		this.messageConverters = messageConverters;
		this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
	}

	/** ...省略若干 */

	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		if (!this.resourceProperties.isAddMappings()) {
			logger.debug("Default resource handling disabled");
			return;
		}
		Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
		CacheControl cacheControl = this.resourceProperties.getCache()
				.getCachecontrol().toHttpCacheControl();
                // 添加对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)
					// 映射静态资源位置:locations:classpath:/META-INF/resources/, 
					// classpath:/resources/, classpath:/static/, classpath:/public/, /
					.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
					.setCachePeriod(getSeconds(cachePeriod))
					.setCacheControl(cacheControl));
		}
	}
	/** ...省略 */
}
初始化HandlerMapping

主要是通过ResourceHandlerRegistry的getHandlerMapping()方法,将每一个注册在ResourceHandlerRegistry的url映射转换为<url, ResourceHttpRequestHandler>映射交给SimpleUrlHandlerMapping的实例管理,其中ResourceHttpRequestHandler封装了响应url的静态资源的位置。

/**
 * Encapsulates information required to create a resource handler.
 *
 * @author Rossen Stoyanchev
 * @author Keith Donald
 * @author Brian Clozel
 * @since 3.1
 */
public class ResourceHandlerRegistration {

	private final String[] pathPatterns;

	private final List<String> locationValues = new ArrayList<>();

	@Nullable
	private Integer cachePeriod;

	@Nullable
	private CacheControl cacheControl;

	@Nullable
	private ResourceChainRegistration resourceChainRegistration;

	/**
	 * Create a {@link ResourceHandlerRegistration} instance.
	 * @param pathPatterns one or more resource URL path patterns
	 */
	public ResourceHandlerRegistration(String... pathPatterns) {
		Assert.notEmpty(pathPatterns, "At least one path pattern is required for resource handling.");
		this.pathPatterns = pathPatterns;
	}

	/**
	 * Add one or more resource locations from which to serve static content.
	 * Each location must point to a valid directory. Multiple locations may
	 * be specified as a comma-separated list, and the locations will be checked
	 * for a given resource in the order specified.
	 * <p>For example, {
  {@code "/"}, {@code "classpath:/META-INF/public-web-resources/"}}
	 * allows resources to be served both from the web application root and
	 * from any JAR on the classpath that contains a
	 * {@code /META-INF/public-web-resources/} directory, with resources in the
	 * web application root taking precedence.
	 * <p>For {@link org.springframework.core.io.UrlResource URL-based resources}
	 * (e.g. files, HTTP URLs, etc) this method supports a special prefix to
	 * indicate the charset associated with the URL so that relative paths
	 * appended to it can be encoded correctly, e.g.
	 * {@code [charset=Windows-31J]http://example.org/path}.
	 * @return the same {@link ResourceHandlerRegistration} instance, for
	 * chained method invocation
	 */
	public ResourceHandlerRegistration addResourceLocations(String... resourceLocations) {
		this.locationValues.addAll(Arrays.asList(resourceLocations));
		return this;
	}

	/**
	 * Specify the cache period for the resources served by the resource handler, in seconds. The default is to not
	 * send any cache headers bu
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值