【Spring MVC】处理静态资源

概念模型 ResourceHandlerRegistry/ResourceHandlerRegistration

ResourceHandlerRegistry/ResourceHandlerRegistration是Spring MVC的概念模型类,二者配合使用。ResourceHandlerRegistry用于保存服务静态资源图片,css文件或者其他文件的资源处理器(resource handler)的注册信息,而ResourceHandlerRegistration就表示这样的"注册信息",它还包含了对头部缓存的设置,用于优化浏览器中资源的加载效率。

ResourceHandlerRegistry/ResourceHandlerRegistration所管理的静态资源可以是web应用根路径下的资源,但不限于此,也可以是classpath上的资源,文件系统的资源或者其他。

可以认为一个ResourceHandlerRegistry组合管理了多个ResourceHandlerRegistration对象。

ResourceHandlerRegistry的典型用法是被某个WebMvcConfigurer实现类用于配置静态资源服务,使用例子如下所示 :

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
   
    /**
     * 静态资源文件映射配置
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 注意 :
        // 1. addResourceHandler 参数可以有多个
        // 2. addResourceLocations 参数可以是多个,可以混合使用 file: 和 classpath : 资源路径
        // 3. addResourceLocations 参数中资源路径必须使用 / 结尾,如果没有此结尾则访问不到

        // 映射到文件系统中的静态文件(应用运行时,这些文件无业务逻辑,但可能被替换或者修改)
        registry.addResourceHandler("/repo/**").addResourceLocations("file:/tmp/");

        // 映射到jar包内的静态文件(真正的静态文件,应用运行时,这些文件无业务逻辑,也不能被替换或者修改)
        registry.addResourceHandler("/my-static/**").addResourceLocations("classpath:/my-static/");
    }

   // ...

}

当每次调用registry.addResourceHandler()时,实际上它会创建一个ResourceHandlerRegistration对象,然后将该对象放到自己的注册表管理起来,函数参数pathPatterns 表示要映射到URL pattern, 可以传递多个要映射到的URL pattern。如下所示 :


    // ResourceHandlerRegistry 代码片段
	/**
	 * Add a resource handler for serving static resources based on the specified URL path patterns.
	 * The handler will be invoked for every incoming request that matches to one of the specified
	 * path patterns.
	 * 增加一个基于指定的URL路径模式服务静态资源的资源处理器
	 * Patterns like "/static/**" or "/css/{filename:\\w+\\.css}" are allowed.
	 * pathPatterns 表示要映射到URL pattern, 可以传递多个要映射到的 URL pattern
	 * See org.springframework.util.AntPathMatcher for more details on the syntax.
	 * @return a ResourceHandlerRegistration to use to further configure the
	 * registered resource handler
	 */
	public ResourceHandlerRegistration addResourceHandler(String... pathPatterns) {
		ResourceHandlerRegistration registration = new ResourceHandlerRegistration(pathPatterns);
		this.registrations.add(registration);
		return registration;
	}

而当调用addResourceLocations()时,ResourceHandlerRegistration对象其实接受了一组资源路径,可能来自classpath,也可能来自文件系统,然后它将这些路径记录下来。如下所示 : 

	// ResourceHandlerRegistration 代码片段
	/**
	 * Add one or more resource locations from which to serve static content.
	 * 增加一个或者多个资源位置,所服务的静态资源会来自于这些资源位置。每一个资源位置可以使
	 * classpath 路径(classpath:/开头),也可能是本地文件系统路径(file:/开头)。
	 * 这些资源位置上的静态资源都会映射到本注册器所被设置的URL路径模式(pattern)上。
	 * 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.
	 * For example, {"/", "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
	 * /META-INF/public-web-resources/ directory, with resources in the
	 * web application root taking precedence.
	 * For 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.
	 * [charset=Windows-31J]http://example.org/path.
	 * @return the same ResourceHandlerRegistration instance, for
	 * chained method invocation
	 */
	public ResourceHandlerRegistration addResourceLocations(String... resourceLocations) {
		this.locationValues.addAll(Arrays.asList(resourceLocations));
		return this;
	}

最后生成静态资源处理器(resource handler)时,会用到上面提到的这些URL pattern和资源路径resource locations。当应用开发人员通过上面的registry.addResourceHandler("/repo/**").addResourceLocations("file:/tmp/")这种方式配置完静态资源映射关系之后,这些关系会被ResourceHandlerRegistry registry对象记录下来。然后在应用启动过程中,Spring MVC配置执行阶段,具体来讲,是bean resourceHandlerMapping实例化过程中,这些信息会被使用,如下代码所示 :

	// WebMvcConfigurationSupport 代码片段
	/**
	 * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
	 * resource handlers. To configure resource handling, override
	 * #addResourceHandlers.
	 */
	@Bean
	@Nullable
	public HandlerMapping resourceHandlerMapping() {
		Assert.state(this.applicationContext != null, "No ApplicationContext set");
		Assert.state(this.servletContext != null, "No ServletContext set");

		// bean resourceHandlerMapping 实例化过程中新建一个ResourceHandlerRegistry对象
		ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
				this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper());
		// 新建的 ResourceHandlerRegistry 对象交给 WebMvcConfigurer 实现类的配置方法
		// addResourceHandlers 来处理,开发人员会在 addResourceHandlers 中提供自己的定制逻辑
		addResourceHandlers(registry);

		// 这里使用addResourceHandlers定制之后的 ResourceHandlerRegistry registry
		// 形成最终要使用的静态资源映射关系对象 handlerMapping, 其实是一个类型
		// 为 SimpleUrlHandlerMapping 的对象,所管理的映射关系可以理解为一个 Map,
		// 每一项内容是 : <URL pattern,ResourceHttpRequestHandler>
		// 注意 : ResourceHandlerRegistration 构造函数如果接收多个 URL pattern,
		// 则每一个 URL pattern 会对应面的一项,而不是整个 ResourceHandlerRegistration 对象
		// 对应上面的一项
		AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
		if (handlerMapping == null) {
			return null;
		}
		handlerMapping.setPathMatcher(mvcPathMatcher());
		handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
		handlerMapping.setInterceptors(getInterceptors());
		handlerMapping.setCorsConfigurations(getCorsConfigurations());
		return handlerMapping;
	}

从上面代码来看,通过registry.getHandlerMapping()方法调用,静态资源映射注册信息表ResourceHandlerRegistry被最终转换成最终要使用的HandlerMapping对象。其代码如下所示 :


	/**
	 * Return a handler mapping with the mapped resource handlers; or null in case
	 * of no registrations.
	 */
	@Nullable
	protected AbstractHandlerMapping getHandlerMapping() {
		if (this.registrations.isEmpty()) {
			return null;
		}

		Map<String, HttpRequestHandler> urlMap = new LinkedHashMap<>();
		for (ResourceHandlerRegistration registration : this.registrations) {
			for (String pathPattern : registration.getPathPatterns()) {
				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 handlerMapping = new SimpleUrlHandlerMapping();
		handlerMapping.setOrder(this.order);
		handlerMapping.setUrlMap(urlMap);
		return handlerMapping;
	}

原文地址【Spring MVC : 概念模型 ResourceHandlerRegistry/ResourceHandlerRegistration-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值