SpingMVC模块常用几种handlerMapping的配置场景

本文详细介绍了Spring MVC中HandlerMapping的工作原理,包括BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping和RequestMappingHandlerMapping三种实现方式的特点及其配置方法。

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

HandlerMapping接口负责根据request请求找到对应的Handler处理器及Interceptor拦截器,并将它们封装在HandlerExecutionChain对象内,返回给中央调度器。

SpringMVC,有很多默认配置,具体可参考jar包内的DispatcherServlet.properties文件。

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.


org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter


org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
......

这里,我们只关注HandlerMapping接口的几个实现,主要是BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping、RequestMappingHandlerMapping这三个类。

HandlerMapping接口,仅有一个方法,就是HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

 

  • BeanNameUrlHandlerMapping

bean的名字或别名,必须以/开头,必须实现Controller接口。配置方式如下

<!-- 1:BeanNameUrlHandlerMapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    
<!-- 2:ViewResolver,视图解析器。-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/view/"/>
    <property name="suffix" value=".jsp"/>
</bean>
    
<!-- 3:Controller -->
<bean name="/beanNameUrlController" class="com.zhao.web.MessageControl"/>


public class MessageControl implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request,
           HttpServletResponseresponse) throws Exception {
       ModelAndView mav = new ModelAndView("index");
       mav.addObject("message", "默认的映射处理器示例");
       return mav;
    }

}

 

  • SimpleUrlHandlerMapping

配置方式如下

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/simpleUrlHandlerMapping.do">welcomeController</prop>
        </props>
     </property>
</bean>
<bean id="welcomeController" class="com.epoch.controller.SimpleUrlHandlerMappingController" />


public class SimpleUrlHandlerMappingController implements Controller {
    protected final Log logger = LogFactory.getLog(this.getClass());
    //@Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        logger.info("run here");
        return null;
    }
}

 

  • RequestMappingHandlerMapping

RequestMappingHandlerMapping,是现在常用的方式。上面两种方式,已经不常用了。

RequestMappingHandlerMapping,一般结合配置文件和java标签@Controller、@RequestMapping一起使用。如下:

<!-- 注册HandlerMapper、HandlerAdapter两个映射类 -->
<mvc:annotation-driven />

<!-- 访问静态资源 -->
<mvc:default-servlet-handler />

<!-- 配置扫描的包 -->
<context:component-scan base-package="com.epoch.*" />




@RestController
@RequestMapping("/hello")
public class HelloController {
    protected final Log logger = LogFactory.getLog(this.getClass());

    @RequestMapping("/index")
    public String index(){
        return "test";
    }
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    @AliasFor(
        annotation = Controller.class
    )
    String value() default "";
}

 

这里,有几点需要额外注意。

这里,我们只关注HandlerMapping接口的几个实现,主要是BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping、RequestMappingHandlerMapping这三个类。

1、BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping的引入

现在,注解很流行,对于注解的解析,可能会涉及到上面两个类的引入。所以,针对上面两个类在配置文件中的配置时,一定要注意配置方法,避免重复引入导致重复实例化的情况。我在验证SimpleUrlHandlerMapping时,因为在配置文件文件中,明确配置了SimpleUrlHandlerMapping类。同时,SpringMVC,对于标签<mvc:default-servlet-handler />的解析,也会引入SimpleUrlHandlerMapping。这样的结果就是,springMVC容器中,出现两个SimpleUrlHandlerMapping类的实例。

registerHandler this = org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@6c219ca4,this.handlerMap = {/**=org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler@54a02e00}
registerHandler this = org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@e448d70,this.handlerMap = {/simpleUrlHandlerMapping.do=com.epoch.controller.SimpleUrlHandlerMappingController@71b9e643}

第一个SimpleUrlHandlerMapping实例,是解析<mvc:default-servlet-handler />标签时引入的。第二个SimpleUrlHandlerMapping实例,是解析配置文件引入的。

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/simpleUrlHandlerMapping.do">welcomeController</prop>
        </props>
     </property>
</bean>
<bean id="welcomeController" class="com.epoch.controller.SimpleUrlHandlerMappingController" />

因为引入了两个SimpleUrlHandlerMapping实例,而每次通过HandlerMapping接口获取handler时,调用的都是第一个实例,所以对应的Handler是DefaultServletHttpRequestHandler。这种情况,导致SimpleUrlHandlerMappingController类的handleRequest方法始终无法执行。解决方案,去掉<mvc:default-servlet-handler />标签即可。

2、BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping对应的Handler实现接口的问题

为BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping两种HandlerMapping接口配置Handler时,Handler需要实现相应的接口,即org.springframework.web.servlet.mvc.Controller接口。这是因为,这两种HandlerMapping实现类所配置的Handler,使用的是相同的HandlerAdapter接口实现类,即SimpleControllerHandlerAdapter类。SimpleControllerHandlerAdapter仅支持实现了Controller的Handler。

/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.mvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;

/**
 * Adapter to use the plain {@link Controller} workflow interface with
 * the generic {@link org.springframework.web.servlet.DispatcherServlet}.
 * Supports handlers that implement the {@link LastModified} interface.
 *
 * <p>This is an SPI class, not used directly by application code.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see org.springframework.web.servlet.DispatcherServlet
 * @see Controller
 * @see LastModified
 * @see HttpRequestHandlerAdapter
 */
public class SimpleControllerHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return ((Controller) handler).handleRequest(request, response);
	}
	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}
}

其实,Handler实现其他接口也行,只要能找到对应的HandlerAdapter接口实现类即可。这里,可能根据自身情况,灵活定制。

3、SimpleUrlHandlerMapping的默认初始化引入DefaultServletHttpRequestHandler类

如果利用配置方式<mvc:default-servlet-handler />,那么配置对应的解析类DefaultServletHandlerBeanDefinitionParser,在解析配置时,系统会向容器注册的DefaultServletHttpRequestHandler类和SimpleUrlHandlerMapping。

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.config;

import java.util.Map;

import org.w3c.dom.Element;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;

/**
 * {@link BeanDefinitionParser} that parses a {@code default-servlet-handler} element to
 * register a {@link DefaultServletHttpRequestHandler}.  Will also register a
 * {@link SimpleUrlHandlerMapping} for mapping resource requests, and a
 * {@link HttpRequestHandlerAdapter}.
 *
 * @author Jeremy Grelle
 * @author Rossen Stoyanchev
 * @since 3.0.4
 */
class DefaultServletHandlerBeanDefinitionParser implements BeanDefinitionParser {

	@Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		Object source = parserContext.extractSource(element);
                //注册DefaultServletHttpRequestHandler类
		String defaultServletName = element.getAttribute("default-servlet-name");
		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<>();
		urlMap.put("/**", defaultServletHandlerName);

		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"
		MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

		return null;
	}

}

DefaultServletHttpRequestHandler类实现了HttpRequestHandler接口,这也导致DefaultServletHttpRequestHandler在获取HandlerAdapter时,获取到的是HttpRequestHandlerAdapter类。

package org.springframework.web.servlet.resource;


public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware {
     ......

    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ......
    }
}
/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.mvc;


/**
 * Adapter to use the plain {@link HttpRequestHandler}
 * interface with the generic {@link org.springframework.web.servlet.DispatcherServlet}.
 * Supports handlers that implement the {@link LastModified} interface.
 *
 * <p>This is an SPI class, not used directly by application code.
 *
 * @author Juergen Hoeller
 * @since 2.0
 * @see org.springframework.web.servlet.DispatcherServlet
 * @see HttpRequestHandler
 * @see LastModified
 * @see SimpleControllerHandlerAdapter
 */
public class HttpRequestHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}

}

4、RequestMappingHandlerMapping

RequestMappingHandlerMapping配置handler时,对handler没有任何要求。这是因为,RequestMappingHandlerMapping在构建HandlerExecutionChain对象时,会在内部将对象内的handler属性的类型,设置成HandlerMethod类型。

/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.handler;


/**
 * Abstract base class for {@link HandlerMapping} implementations that define
 * a mapping between a request and a {@link HandlerMethod}.
 *
 * <p>For each registered handler method, a unique mapping is maintained with
 * subclasses defining the details of the mapping type {@code <T>}.
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 * @author Juergen Hoeller
 * @since 3.1
 * @param <T> The mapping for a {@link HandlerMethod} containing the conditions
 * needed to match the handler method to incoming request.
 */
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

	/**
	 * Bean name prefix for target beans behind scoped proxies. Used to exclude those
	 * targets from handler method detection, in favor of the corresponding proxies.
	 * <p>We're not checking the autowire-candidate status here, which is how the
	 * proxy target filtering problem is being handled at the autowiring level,
	 * since autowire-candidate may have been turned to {@code false} for other
	 * reasons, while still expecting the bean to be eligible for handler methods.
	 * <p>Originally defined in {@link org.springframework.aop.scope.ScopedProxyUtils}
	 * but duplicated here to avoid a hard dependency on the spring-aop module.
	 */

	// Handler method lookup
	/**
	 * Look up a handler method for the given request.
	 */
	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock();
		try {
                        //返回HandlerMethod对象
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
                        //返回HandlerMethod对象
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
	
}

通过RequestMappingHandlerMapping获取的HandlerExecutionChain对象的handler属性,是HandlerMethod的类型,这也导致了SpringMVC后续会使用到RequestMappingHandlerAdapter作为handler的Adapter。因为RequestMappingHandlerAdapter支持HandlerMethod类型的Handler。

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {

/**
	 * This implementation expects the handler to be an {@link HandlerMethod}.
	 * @param handler the handler instance to check
	 * @return whether or not this adapter can adapt the given handler
	 */
	@Override
	public final boolean supports(Object handler) {
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}
}
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
    /**
	 * Always return {@code true} since any method argument and return value
	 * type will be processed in some way. A method argument not recognized
	 * by any HandlerMethodArgumentResolver is interpreted as a request parameter
	 * if it is a simple type, or as a model attribute otherwise. A return value
	 * not recognized by any HandlerMethodReturnValueHandler will be interpreted
	 * as a model attribute.
	 */
	@Override
	protected boolean supportsInternal(HandlerMethod handlerMethod) {
		return true;
	}

}

5、HandlerAdapter接口源代码

public interface HandlerAdapter {

	/**
	 * Given a handler instance, return whether or not this {@code HandlerAdapter}
	 * can support it. Typical HandlerAdapters will base the decision on the handler
	 * type. HandlerAdapters will usually only support one handler type each.
	 * <p>A typical implementation:
	 * <p>{@code
	 * return (handler instanceof MyHandler);
	 * }
	 * @param handler handler object to check
	 * @return whether or not this object can use the given handler
	 */
	boolean supports(Object handler);

	/**
	 * Use the given handler to handle this request.
	 * The workflow that is required may vary widely.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler handler to use. This object must have previously been passed
	 * to the {@code supports} method of this interface, which must have
	 * returned {@code true}.
	 * @throws Exception in case of errors
	 * @return ModelAndView object with the name of the view and the required
	 * model data, or {@code null} if the request has been handled directly
	 */
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	/**
	 * Same contract as for HttpServlet's {@code getLastModified} method.
	 * Can simply return -1 if there's no support in the handler class.
	 * @param request current HTTP request
	 * @param handler handler to use
	 * @return the lastModified value for the given handler
	 * @see javax.servlet.http.HttpServlet#getLastModified
	 * @see org.springframework.web.servlet.mvc.LastModified#getLastModified
	 */
	long getLastModified(HttpServletRequest request, Object handler);

}

有关HandlerAdapter的,不在这里详细介绍。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值