前言
本文主要分析Spring Web MVC中一次请求的处理过程和Controller路径映射解析过程。
DispatcherServlet
DispatcherServlet
是Spring Web MVC的核心组件,本质上DispatcherServlet
是一个Servlet
。继承图如下:
DispatcherServlet
委托了很多特殊的bean来完成对请求的处理和渲染响应,这些bean都是被Spring管理的对象。
DispatherServlet组列表
DispatcherServlet
委托了很多特殊的bean来完成对请求的处理和渲染响应,这些bean都是被Spring管理的对象。
Bean Type | Explanation |
---|---|
HandlerMapping | handler和request的映射关系,HandlerMapping 有两个主要的实现类RequestMappingHandlerMapping (对@RequestMapping 注解的支持)和SimpleUrlHandlerMapping 显示的注册URI和handlers |
HandlerAdapter | 帮助DispatcherServlet 执行request对应的handler方法,HandlerAdapter 本身不关注具体的handler执行逻辑。 |
DispatcherServlet
中还有很多的其他组件可以参考官方文档
RequestMappingHandlerMapping
SpringMVC是如何将请求路由到Controller中的方法中的,分析RequestMappingHandlerMapping
将会的到想要的答案。
从上面的类图中可以看出RequestMappingHandlerMapping
实现了InitializingBean
接口,也就是在RequestMappingHandlerMapping
创建过程中属性设置后调用afterPropertiesSet
方法。
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
@Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
super.afterPropertiesSet();
}
该方法中又调用父类的afterProperitesSet()
方法
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
调用链入下图所示:
筛选IOC容器中是Controller的bean
AbstractHandlerMethodMapping#processCandidateBean
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
该方法筛选出了是Controller的bean主要判断逻辑是isHandler()
方法。
RequestMappingHandlerMapping#isHandler
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
可以看出该方法主要判断了IOC容器中的bean是否有Controller.class
和RequestMapping.class
注解。
HandlerMethod注册
AbstractHandlerMethodMapping#detectHandlerMethods
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
上面的方法将Controller解析成了一个Map
最终将循环遍历这个Map
并将RequestMappingInfo
和对应的处理方法注册到内部类MappingRegistry
中:
AbstractHandlerMethodMapping.MappingRegistry#register
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
最后RequestMappingInfo
和对应处理方法Method
又将放入一个Map
中只是这个Map
的key为RequestMappingInfo
,value为Method
。
在处理没次请求的时候都会交给org.springframework.web.servlet.DispatcherServlet#doDispatch
方法处理,在该方法中就会根据请求的url
对处理方法进行查找。最后使用反射调用该处理方法。