SpringMVC源码情操陶冶-ResourcesBeanDefinitionParser静态资源解析器
解析mvc:resources节点,控制对静态资源的映射访问
查看官方注释
/**
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} that parses a
* {@code resources} element to register a {@link ResourceHttpRequestHandler} and
* register a {@link SimpleUrlHandlerMapping} for mapping resource requests,
* and a {@link HttpRequestHandlerAdapter}. Will also create a resource handling
* chain with {@link ResourceResolver}s and {@link ResourceTransformer}s.
*
* @author Keith Donald
* @author Jeremy Grelle
* @author Brian Clozel
* @since 3.0.4
*/
根据注释我们得知该解析器的作用是将mvc:resources
节点解析为
- ResourceHttpRequestHandler/SimpleUrlHandlerMapping-匹配mapping属性对应的访问请求
- HttpRequestHandlerAdapter-http请求适配器
- ResourceResolver/ResourceTransformer-访问的资源查询处理器,针对于location属性
ResourcesBeanDefinitionParser#parse()-解析逻辑
解析涉及的内容偏多,得多花点小心思,源码如下
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
//注册ResourceUrlProvider对象,主要是设置对每个请求都设置上RESOURCE_URL_PROVIDER_ATTR属性,供获取此对象
//在ResourceResolver中会使用
registerUrlProvider(parserContext, source);
//解析location属性,注册为ResourceHttpRequestHandler对象
String resourceHandlerName = registerResourceHandler(parserContext, element, source);
if (resourceHandlerName == null) {
return null;
}
//解析mapping属性,注册SimpleUrlHandlerMapping对象
Map<String, String> urlMap = new ManagedMap<String, String>();
String resourceRequestPath = element.getAttribute("mapping");
if (!StringUtils.hasText(resourceRequestPath)) {
parserContext.getReaderContext().error("The 'mapping' attribute is required.", parserContext.extractSource(element));
return null;
}
//mapping对应的值与ResourceHttpRequestHandler匹配
urlMap.put(resourceRequestPath, resourceHandlerName);
RuntimeBeanReference pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(null, parserContext, source);
RuntimeBeanReference pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(null, parserContext, source);
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef).add("urlPathHelper", pathHelperRef);
//order属性-执行顺序,越大优先级越低
String order = element.getAttribute("order");
// Use a default of near-lowest precedence, still allowing for even lower precedence in other mappings
handlerMappingDef.getPropertyValues().add("order", StringUtils.hasText(order) ? order : Ordered.LOWEST_PRECEDENCE - 1);
//SimpleUrlHandlerMapping添加corsConfigurations属性
RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
//注册SimpleUrlHandlerMapping到spring bean工厂中
String beanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
parserContext.getRegistry().registerBeanDefinition(beanName, handlerMappingDef);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, beanName));
//注册BeanNameUrlHandlerMapping/HttpRequestHandlerAdapter/SimpleControllerHandlerAdapter到bean工厂
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
return null;
}
以上主要涉及location
、mapping
属性的解析以及注册默认的bean,下面将从这三块来进行主要的分析
ResourcesBeanDefinitionParser#registerResourceHandler()-解析location属性
简要分析下源码
private String registerResourceHandler(ParserContext parserContext, Element element, Object source) {
//获取location属性,此属性不可为空
String locationAttr = element.getAttribute("location");
if (!StringUtils.hasText(locationAttr)) {
parserContext.getReaderContext().error("The 'location' attribute is required.", parserContext.extractSource(element));
return null;
}
//支持location多路径和classpath前缀,其中以,分隔
ManagedList<String> locations = new ManagedList<String>();
locations.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(locationAttr)));
//创建ResourceHttpRequestHandler bean
RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
resourceHandlerDef.setSource(source);
resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//添加locations属性
MutablePropertyValues values = resourceHandlerDef.getPropertyValues();
values.add("locations", locations);
//添加cacheSeconds属性-cache有效时间
String cacheSeconds = element.getAttribute("cache-period");
if (StringUtils.hasText(cacheSeconds)) {
values.add("cacheSeconds", cacheSeconds);
}
//解析子节点mvc:cache-control cache控制器
Element cacheControlElement = DomUtils.getChildElementByTagName(element, "cache-control");
if (cacheControlElement != null) {
CacheControl cacheControl = parseCacheControl(cacheControlElement);
values.add("cacheControl", cacheControl);
}
//解析mvc:resource-chain 包含mvc:resolver/mvc:transformers
//对应ResourceHttpRequestHandler#resourceResolvers/resourceTransformers属性
Element resourceChainElement = DomUtils.getChildElementByTagName(element, "resource-chain");
if (resourceChainElement != null) {
parseResourceChain(resourceHandlerDef, parserContext, resourceChainElement, source);
}
//处理请求中的media type
Object manager = MvcNamespaceUtils.getContentNegotiationManager(parserContext);
if (manager != null) {
values.add("contentNegotiationManager", manager);
}
//注册ResourceHttpRequestHandler
String beanName = parserContext.getReaderContext().generateBeanName(resourceHandlerDef);
parserContext.getRegistry().registerBeanDefinition(beanName, resourceHandlerDef);
parserContext.registerComponent(new BeanComponentDefinition(resourceHandlerDef, beanName));
return beanName;
}
注册ResourceHttpRequestHandler,作用是对静态资源的location路径进行保存
其会被
HttpRequestHandlerAdapter
通过handle()
方法调用支持location多路径和classpath前缀,其中以,分隔
默认的resourceResolvers集合只有PathResourceResolver,可通过
mvc:resolver
指定,用于静态资源的获取默认resourceTransformers集合为空,可通过
mvc:transformer
指定
mapping属性绑定ResourceHttpRequestHandler资源获取类
Map<String, String> urlMap = new ManagedMap<String, String>();
String resourceRequestPath = element.getAttribute("mapping");
if (!StringUtils.hasText(resourceRequestPath)) {
parserContext.getReaderContext().error("The 'mapping' attribute is required.", parserContext.extractSource(element));
return null;
}
urlMap.put(resourceRequestPath, resourceHandlerName);
源码中只是保存在urlMap
集合中,此为SimpleUrlHandlerMapping
的一个内部属性,所以SimpleUrlHandlerMapping
是最终保存mvc:resource
信息的处理逻辑类
MvcNamespaceUtils.registerDefaultComponents()-默认的组件注册
具体的代码就不展开了,有兴趣的自行去查阅
public static void registerDefaultComponents(ParserContext parserContext, Object source) {
registerBeanNameUrlHandlerMapping(parserContext, source);
registerHttpRequestHandlerAdapter(parserContext, source);
registerSimpleControllerHandlerAdapter(parserContext, source);
}
小结
注册
ResourceHttpRequestHandler
,作用为处理location对应的服务端资源。其中location支持多路径配置,"/"相对于工程根目录,也支持classpath:
前缀注册
SimpleUrlHandlerMapping
处理类,其为AbstractHandlerMapping
的实现类,关注getHandler()
方法,主要供springmvc响应请求调用
SimpleUrlHandlerMapping
的内部属性urlMap
,用于关联mapping
配置与location
配置处理器ResourceHttpRequestHandler
默认会注册
BeanNameUrlHandlerMapping
、HttpRequestHandlerAdapter
、SimpleControllerHandlerAdapter
对象,供springmvc调用mvc:resources最终会注册为SimpleUrlHandlerMapping对象,其处理逻辑是根据请求的路径是否匹配mapping属性指定的ant-style路径,是则通过ResourceHttpRequestHandler解析对应的location获取相应的服务器资源直接响应给客户端