Spring 学习笔记-- <mvc:default-servlet-handler />默认加载simpleurlhandlermapping

本文记录了在Spring 3.2版本中,配置Spring-mvc.xml时遇到的问题。当使用<mvc:default-servlet-handler />且RequestMappingHandlerMapping未设置order属性时,部分请求被SimpleUrlHandlerMapping处理。解析过程涉及DefaultServletHttpRequestHandler和SimpleUrlHandlerMapping。SimpleUrlHandlerMapping的url映射为/**,用于处理静态资源。若要改变处理顺序,可通过设置order属性。同时讨论了默认Servlet处理静态资源的方法,包括使用容器默认Servlet和自定义defaultServletName。

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

本博客只作为自己学习记录使用,如有错误,希望多指点指点


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);
}



因为写的比较随意,所以存在错误希望多多指点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值