注:SpringFramework的版本是4.3.x。
1.HandlerMapping的俩个默认实现类
们由DispatcherServlet的初始化简析得知默认的HandlerMapping是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping,这俩个类的继承图如下图2、图3所示,
图2 BeanNameUrlHandlerMapping的类继承图
图3 DefaultAnnotationHandlerMapping的类继承图
BeanNameUrlHandlerMapping是spring-webmvc模块的,DefaultAnnotationHandlerMapping是spring-webmvc-porlet的。我们主要分析这俩个HandlerMapping。
BeanNameUrlHandlerMapping的用法如下,
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean name="/hello" class="com.mjduan.project.example8_aop.HelloController"/>
HelloController的源码如下,
public class HelloController extends AbstractController {
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
return null;
}
}
BeanNameUrlHandlerMapping初始化的时序图,如下图4所示:
图4 BeanNameUrlHandlerMapping的初始化时序图
图3的说明:由于ApplicationObjectSupport实现了ApplicationContextAware接口,所有在执行setApplicationContext的时候会初始化AbstractUrlHandlerMapping的属性handlerMap。
图4的步骤6中,会从applicationContext中取出所有的MappedInterceptor,放到AbstractHandlerMapping的属性adaptedInterceptors中,这些MappedInterceptor是HandlerInterceptor的子类,在构造HandlerExecutionChain时用到。
我们再来分析AbstractDetectingUrlHandlerMapping的detectHandlers方法,源码如下List-1所示,
List-1 AbstractDetectingUrlHandlerMapping的detectHandlers()源码
protected void detectHandlers() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
determineUrlsForHandler方法的实现是在BeanNameUrlHandlerMapping中,源码如下List-2所示,
List-2 BeanNameUrlHandlerMapping的determineUrlsForHandler方法源码
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
/**
* Checks name and aliases of the given bean for URLs, starting with "/".
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<String>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = getApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
}
由List-1的代码可知,从applicationContext中取出所有的beanName,之后遍历所有的beanName,如果该beanName以"/"开头,则将这个beanName视为url,记录。
一般情况下,我们不会使用BeanNameUrlHandlerMapping的,BeanNameUrlHandlerMapping使用起来感觉不是很灵活。
3.SimpleUrlHandlerMapping的用法
SimpleUrlHandlerMapping的一般使用方式如下,prop中key的值,是spring bean。这种是以前的用法,现在基本都使用注解的方式了,很少用这种了。注:下面这段代码来源: https://blog.csdn.net/trigl/article/details/50494492
<bean
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/upfile.do">upfile</prop>
<prop key="/upfiles.do">upfiles</prop>
<prop key="/extjs.do">SpringMVC</prop>
<prop key="/show.do">show</prop>
</props>
</property>
</bean>
4.DefaultAnnotationHandlerMapping的分析
List-3 DefaultAnnotationHandlerMapping的initApplicationContext()源码
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
detectHandlers();
}
/**
* Register all handlers specified in the Portlet mode map for the corresponding modes.
* @throws org.springframework.beans.BeansException if the handler couldn't be registered
*/
protected void detectHandlers() throws BeansException {
ApplicationContext context = getApplicationContext();
String[] beanNames = context.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
Class<?> handlerType = context.getType(beanName);
RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
if (mapping != null) {
// @RequestMapping found at type level
String[] modeKeys = mapping.value();
String[] params = mapping.params();
boolean registerHandlerType = true;
if (modeKeys.length == 0 || params.length == 0) {
registerHandlerType = !detectHandlerMethods(handlerType, beanName, mapping);
}
if (registerHandlerType) {
AbstractParameterMappingPredicate predicate = new TypeLevelMappingPredicate(
params, mapping.headers(), mapping.method());
for (String modeKey : modeKeys) {
registerHandler(new PortletMode(modeKey), beanName, predicate);
}
}
}
else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
detectHandlerMethods(handlerType, beanName, mapping);
}
}
}
从List-3中的detectHandlers可知,
- 先获取applicationContext中所有的beanName,而后获取其对应的Class<?>,判断类上是否有RequestMapping注解,有的话,进行解析,之后registerHandler。
- 若类上没有找到RequestMapping注解,则判断类上是否有Controller注解,如果有,那么执行detectHandlerMethods,这个方法的源码有点多,我只是给出部分,如下List-4所示:
List-4 DefaultAnnotationHandlerMapping的detectHandlerMethods方法源码
protected boolean detectHandlerMethods(Class<?> handlerType, final String beanName, final RequestMapping typeMapping) {
final Set<Boolean> handlersRegistered = new HashSet<Boolean>(1);
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
handlerTypes.add(handlerType);
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) {
PortletRequestMappingPredicate predicate = null;
String[] modeKeys = new String[0];
String[] params = new String[0];
if (typeMapping != null) {
params = PortletAnnotationMappingUtils.mergeStringArrays(typeMapping.params(), params);
}
ActionMapping actionMapping = AnnotationUtils.findAnnotation(method, ActionMapping.class);
RenderMapping renderMapping = AnnotationUtils.findAnnotation(method, RenderMapping.class);
ResourceMapping resourceMapping = AnnotationUtils.findAnnotation(method, ResourceMapping.class);
EventMapping eventMapping = AnnotationUtils.findAnnotation(method, EventMapping.class);
RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (actionMapping != null) {
params = PortletAnnotationMappingUtils.mergeStringArrays(params, actionMapping.params());
predicate = new ActionMappingPredicate(actionMapping.name(), params);
}
......
从List-4可知,detectHandlerMethods方法,对类的方法进行遍历,之后逐个处理每个方法。
DefaultAnnotationHandlerMapping处理的就是我们平时所用的基于注解的方式。