本博客只作为自己学习记录使用,如有错误,希望多指点指点
Spring使用版本3.2,在项目配置Spring-mvc.xml中使用RequestMappingHandlerMapping时候(Spring3.1之前使用DefaultAnnotationHandlerMapping),但是没有配置order属性,同时项目配置<mvc:default-servlet-handler />
<mvc:default-servlet-handler />
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
</bean>
在运行之后发现,部分请求没有映射到RequestMappingHandlerMapping,就觉得很奇怪,因为感觉值配置了一个handlermapping,怎么会没有在当前的handlermapping中。
容器中bean解析后存在DefaultListableBeanFactory容器中的beanDefinitionMap中,
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
就调试查找解析配置文件流程,发现在解析<mvc:default-servlet-handler />时候
通过DefaultServletHandlerBeanDefinitionParser解析element节点<mvc:default-servlet-handler />
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
// 如果 <mvc:default-servlet-handler /> 配置属性default-servlet-name
String defaultServletName = element.getAttribute("default-servlet-name");
// 创建DefaultServletHttpRequestHandler
RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
defaultServletHandlerDef.setSource(source);
defaultServletHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
if (StringUtils.hasText(defaultServletName)) {
defaultServletHandlerDef.getPropertyValues().add("defaultServletName", defaultServletName);
}
String defaultServletHandlerName = parserContext.getReaderContext().generateBeanName(defaultServletHandlerDef);
parserContext.getRegistry().registerBeanDefinition(defaultServletHandlerName, defaultServletHandlerDef);
parserContext.registerComponent(new BeanComponentDefinition(defaultServletHandlerDef, defaultServletHandlerName));
Map<String, String> urlMap = new ManagedMap<String, String>();
urlMap.put("/**", defaultServletHandlerName);
// SimpleUrlHandlerMapping
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
String handlerMappingBeanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
parserContext.getRegistry().registerBeanDefinition(handlerMappingBeanName, handlerMappingDef);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, handlerMappingBeanName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
// BeanNameUrlHandlerMapping
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
return null;
}
源码中可以看到在解析节点<mvc:default-servlet-handler />时候同时获取三个spring的系统bean
分别是:
DefaultServletHttpRequestHandler
SimpleUrlHandlerMapping
BeanNameUrlHandlerMapping
同时对SimpleUrlHandlerMapping配饰urlmap为/**,即为任意请求映射,映射到defaultServletHandlerName。
其中在上面的源码中可以看到,实际的为defaultServletHandlerName为
RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
实际中也可以自己定义defaultServletHandlerName,通过
<mvc:default-servlet-handler default-servlet-name="CustomDefaultServlet"/>
在源码中可以看到实际映射的匹配在解析的时候就存放在handlermapping中的
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
</span>// 存放映射url规则
private final Map<String, Object> urlMap = new HashMap<String, Object>();
然后关注下该url的order设置属性,调试发现默认设置时2.
因为在加载晚handlermapping之后会sort排序,根据order。所以RequestMappingHandlerMapping如果没有设置order是大于2的。所以再寻找handlermapping会优先匹配simpleurlhandlermapping 。
所以如果需要优先考虑handlermapping的话需要设置属性order等于0。
顺便也看下<mvc:default-servlet-handler />对于静态资源的处理方法:
DefaultServletHandlerBeanDefinitionParser在解析parse bean时
<span style="font-size:18px;">// DefaultServletHttpRequestHandler,spring中专门处理静态url
RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
defaultServletHandlerDef.setSource(source);
defaultServletHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 如果配置defaultServletName属性,就使用用户自定义的defaultServletName
if (StringUtils.hasText(defaultServletName)) {
defaultServletHandlerDef.getPropertyValues().add("defaultServletName", defaultServletName);
}</span>
同时给simpleurlhandlermapping 加入了url映射,也就是在simpleurlhandlermapping 中匹配时候,/**的的请求url对应的handler处理器都是defaultServletHandlerName。而defaultServletHandlerName是专门处理静态资源的。
Map<String, String> urlMap = new ManagedMap<String, String>();
urlMap.put("/**", defaultServletHandlerName);
在Spring中处理静态资源的映射有三种方式。
1、使用容器的defaultServlet处理静态资源,但是请求servlet的顺序需要在DispatcherServlet之前。
每种容器的defaultServlet名称不一样,可以看下DefaultServletHttpRequestHandler这个源码。
<span style="font-size:18px;">public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware {
/** Default Servlet name used by Tomcat, Jetty, JBoss, and GlassFish */
private static final String COMMON_DEFAULT_SERVLET_NAME = "default";
/** Default Servlet name used by Google App Engine */
private static final String GAE_DEFAULT_SERVLET_NAME = "_ah_default";
/** Default Servlet name used by Resin */
private static final String RESIN_DEFAULT_SERVLET_NAME = "resin-file";
/** Default Servlet name used by WebLogic */
private static final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet";
/** Default Servlet name used by WebSphere */
private static final String WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet</span>
可以看到每种容器定义的defaultServlet的名称不一样。
tomcat中可以再web.xml中使用如下方式:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
2、使用
<mvc:resources location="/js/" mapping="/js/**" />
3、就是上面的方式使用
<mvc:default-servlet-handler default-servlet-name="CustomDefaultServlet"/>
在中可以看到DefaultServletHttpRequestHandler中可以制定defaultServletName覆盖默认属性。
如果不指定,就按照上面的容器默认方式。
private String defaultServletName;
private ServletContext servletContext;
/**
* Set the name of the default Servlet to be forwarded to for static resource requests.
*/
public void setDefaultServletName(String defaultServletName) {
this.defaultServletName = defaultServletName;
}
因为在url请求映射之手,如果在handlermapping中,如果在之前的mapping中没有匹配到url,就使用SimpleIUrlHandlerMapping来处理,因为SimpleIUrlHandlerMapping中urlMap中存有/**,肯定能匹配,
其对应的handler是DefaultServletHttpRequestHandler。调用handlerRequest方法,将请求转发到制定
的servlet上处理。
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
if (rd == null) {
throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" +
this.defaultServletName +"'");
}
rd.forward(request, response);
}
因为写的比较随意,所以存在错误希望多多指点。