I have a Spring MVC 4.2.x
project. I am configuring it via XML
based configuration file:
<servlet>
<servlet-name>foo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
In the context.xml
file I have configured resources and annotation-driven
mode:
<mvc:resources mapping="/webjars/**" location="/webjars/"/>
<mvc:annotation-driven/>
Everything works fine except the one thing: webjars-locator
- Webjars locator doesn't work at all.
I've started look into the Spring MVC sources to understand what's going wrong, and found, that webjars-locator
works through WebJarsResourceResolver
class, which's object is added in the ResourceChainRegistration.getResourceResolvers()
class method.
The full sequence looks like:
WebMvcConfigurationSupport.resourceHandlerMapping()
>> ResourceHandlerRegistration.getHandlerMapping()
>> ResourceHandlerRegistration.getRequestHandler()
>>
ResourceChainRegistration.getResourceResolvers()
, where it is added as:
if (isWebJarsAssetLocatorPresent) {
result.add(new WebJarsResourceResolver());
}
The problem, is that in case of XML based configuration described above, this sequence is not invoked, neither WebMvcConfigurationSupport
class is used.
Furthermore, if I add
@EnableWebMvc
@Configuration
public class WebConfig {
}
into project, WebMvcConfigurationSupport
obviously works, but in ResourceHandlerRegistration.getHandlerMapping()
:
protected AbstractHandlerMapping getHandlerMapping() {
if (registrations.isEmpty()) {
return null;
}
...
}
registrations
is empty!
After all, the only one way how to forcibly make Spring to add WebJarsResourceResolver
into the resolver chain, is:
1) remove <mvc:resources mapping="/webjars/**" location="/webjars/"/>
from the context.xml
and
2) add addResourceHandlers
into WebConfig
:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("/webjars/")
.setCachePeriod(3600)
.resourceChain(true) // !!! very important
;
}
My question is: what am I doing wrong? Why XML bases configuration doesn't cause WebJarsResourceResolver
to be registered?
Let's say that the context root path is /ctx
. With your configuration, a resource path as /webjars/RESOURCE
is actually mapped to /ctx/webjars/RESOURCE
; in contrary to common expectation that it should be mapped to /webjars/RESOURCE
.
Based on Spring 4.2.x documentation and an example similar issue, you need to map /webjars/**
to the default dispatcher servlet as:
This allows for mapping the DispatcherServlet to "/" (thus overriding the mapping of the container’s default Servlet), while still allowing static resource requests to be handled by the container’s default Servlet. It configures a DefaultServletHttpRequestHandler with a URL mapping of "/**" and the lowest priority relative to other URL mappings.
which means adding the following should fix the issue:
<mvc:resources mapping="/webjars/**" location="/webjars/">
<mvc:resource-chain>
<mvc:resource-cache />
</mvc:resource-chain>
</mvc:resources>
<mvc:annotation-driven />
<mvc:default-servlet-handler />
Another note that is the example petclinic uses location="classpath:/META-INF/resources/webjars/"
for webjars which I am not sure is relevant here.
Hope this helps.
Added by @Andremoniy:
for Spring 4
it has slightly different markup:
<mvc:resources mapping="/webjars/**" location="/webjars/">
<mvc:resource-chain resource-cache="true"/>
</mvc:resources>