前言
前面我们简单地分析了两个过程,本文将会来介绍请求过来是如何找到对应的Controller中的方法的。
概述
在分析之前,我们先记住几个类名,HandlerMapping接口,RequestMapingHandlerMapping类,HandlerMethod类。
- HandlerMapping接口:请求和处理方法的映射接口,提供了getHandler方法,我们可以通过这个接口的实现类来完成请求和处理方法的映射
- RequestMappingHandlerMapping:请求和处理方法映射关系的实现类,这是方法是我们分析的重点
- HandlerMethod:处理方法,这个方法封装了许多东西,比如我们加了@controller注解的对象、对应Controller的方法、方法的参数、方法的注解、方法参数的注解等等。
关注一下,RequestMappingHandlerMapping的继承关系,几点说明一下
- 实现了HandlerMapping接口
- 实现了InitialzingBean接口,这个接口我们知道,在IOC完成注入后,会有一个初始化处理过程,这个过程中会调用这个接口实现类的afterPropertiesSet方法,对于RequestMappingHandlerMapping来说,主要是在这个方法里完成了映射关系的注册
- 实现了ApplicationAware接口,会在这个实现方法时进行拦截的器的查找的注册
这里是HttpMethod的属性,本篇我们关注下bean和method两个属性,其它属性后面会谈到。
web.xml配置和之前的一样,这里不贴图了,看一下spring-servlet-config.xml的配置
源码分析
看上图,context命名空间下的,我们不进行分析,关注<mvc:annotation-driven/>。分析事务、AOP时,我们知道了除了spring的基础命名空间外,其它标签的解析都是走parseCustomElement方法的。而对标签是解析是交给特点的解析器的。我们先在spring-webmvc的META-INF/spring.handlers方法找到MvcNamespaceHander,再在MvcNamespaceHandler找到AnnotationDrivenBeanDefinitionParser解析器。我们先看这个解析器的parse方法。
- AnnotationDrivenBeanDefinitionParser解析<mvc:annotation-driven/>标签。这个方法太长,这里只贴出这里分析要用到的核心代码。我们阅读下面的关键代码,发现解析标签时注册了RequestMappingHandlerMapping和BeanNameUrlHandlerMapping两个处理器,后面这个处理器,有点类似struts2。这两个处理器的order值前者小于后者,并且开发时前者对所有的Controller都支持,所以在平常的开发中,走的都是前者的逻辑。所以,我们这里只对RequestMappingHandlerMapping进行分析。
//AnnotationDrivenBeanDefinitionParser类的方法
//解析<mvc:annotation-driven/>,spring mvc的核心注解,有了它可以给我们注入映射器、适配器、异常处理器等等,这里我们关注映射器,而不用我们一个个<bean>标签地去配置了
public BeanDefinition parse(Element element, ParserContext parserContext) {
......
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
......
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
......
}
//MvcNamespaceUtils类的方法
//注册默认组件,这里我们关注下BeanNameUrlHandlerMapping的注册
public static void registerDefaultComponents(ParserContext parserContext, Object source) {
registerBeanNameUrlHandlerMapping(parserContext, source);
registerHttpRequestHandlerAdapter(parserContext, source);
registerSimpleControllerHandlerAdapter(parserContext, source);
}
- 通过上过的解析过程,我们已经把RequestMappingHandlerMapping给注册到容器了。接下来就是实例化了,通过概述中的整个继承体系中,我们知道RequestMappingHandlerMapping是实现了InitialzingBean接口的,而实现接口的类是AbstractHandlerMethodMapping。而这个类的afterPropertiesSet方法是整个映射关系建立的入口,我们从这里开始分析。这里我们可以看到有以下几步
- 遍历所有的bean
- 如果是scope proxy则不管(这个在AOP中再补充进去),否则判断是不是handler。比较简单,就是看看类是否有@Controller或@RequestMapping注解
- 如果这个类是handler,则会去查找这个类的所有方法
- 最后的一个handlerMethodsInitialized方法是个空方法,不用管了
//AbstractHandlerMethodMapping类的方法
//映射关系建立的入口
public void afterPropertiesSet() {
initHandlerMethods();
}
//AbstactHandlerMethodMapping类的方法
//建立映射关系
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
//RequestMappingHandlerMapping类的方法
//判断是否为handler
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
- 查找Controller下所有带有@RequestMapping注解的方法,最后注册HandlerMethod
//AbstractHandlerMethodMapping类的方法
//查找并注册带HandlerMethod
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
for (Map.Entry<Method, T> entry : methods.entrySet()) {
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
registerHandlerMethod(handler, invocableMethod, mapping);
}
}
- 查找所有带@RequestMapping的方法,并存放到Map<Method,RequestMappingInfo>中,这里我们注意下RequestMappingInfo中的patternsCondition属性就是我们解析注解中的url
//MethodIntrospector类的方法
//遍历所有的方法,包括父类中的,匹配到的则注册到map中
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<Method, T>();
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(targetType)) {
handlerTypes.add(targetType);
specificHandlerType = targetType;
}
handlerTypes.addAll(Arrays.asList(targetType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod, result);
}
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
//RequestMappingHandlerMapping类的方法
//判断是否有@RequestMapping注解
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
//RequestMappingHandlerMapping类的方法
//这里就是把@RequestMappingHandlerMapping中的信息封装到RequestMappingInfo对象中
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, RequestCondition<?> customCondition) {
return RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name())
.customCondition(customCondition)
.options(this.config)
.build();
}
public RequestMappingInfo build() {
ContentNegotiationManager manager = this.options.getContentNegotiationManager();
PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
this.options.getFileExtensions());
return new RequestMappingInfo(this.mappingName, patternsCondition,
new RequestMethodsRequestCondition(methods),
new ParamsRequestCondition(this.params),
new HeadersRequestCondition(this.headers),
new ConsumesRequestCondition(this.consumes, this.headers),
new ProducesRequestCondition(this.produces, this.headers, manager),
this.customCondition);
}
- 注册HandlerMethod,首先我们看到注册信息是存放在AbstractHandlerMethodMapping的mappingRegistry属性,而这个属性对应的类是AbstractHandlerMethodMapping的一个内部类,下面我们看在这个类上的注册过程。
- 创建HandlerMethod,这个比较简单,就是把bean名字、对应是IOC容器、方法等这些属性设置到HandlerMethod中
- 建立mapping与handlerMethod的映射
- 查找@RequestMapping注解中的url地址(这个在上一步中已经保存在RequestMappingInfo对象中),建立url和mapping的映射关系
- 建立mapping和MappingRegistration(这里名包含了mapping、handlerMethod、url等所有的信息)
到这里所有的映射关系都建立好的,通过url就可以找到maping信息,通过mapping信息就对找到handlerMethod、mappingRegistration等所有所有信息了。接下来,我们就来看调用过程。
//AbstractHandlerMethodMapping类的方法
//注册处理器方法
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
//AbstractHandlerMethodMapping的内部类
//映射关系都是注册在这个类上的
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
}
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<T>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
- 调用过程,让我们来到调用过程。之前我们分析过,调用过程入口doDispatch方法,这里只贴出我们关心的一点代码,即拿到请求对应的handler。我们看到返回的是HandlerExecutionChan对象,这个类有handler和拦截器的属性,本篇我们的关注点放到handler属性上,拦截器后面用单独的章节来讨论。
//DispatcherServlet类方法
//请求调用过程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
......
HandlerExecutionChain mappedHandler = null;
......
mappedHandler = getHandler(processedRequest);
.......
}
//HandlerExecutionChain的属性
private final Object handler;
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
- 遍历所有的HandlerMapping,在上面的分析中,我们说过这里会有两个HandlerMapping对象:RequestMappingHandlerMapping和BeanNameUrlHandlerMapping对象。因为RequestMappingHandlerMapping的优先级高,会先去其中查找handler。通过后面的分析,我们会知道在RequestMappingHandlerMapping中会找到相应的handler并返回到DispatcherServlet。
//DispatcherServlet类的方法
//获取处理器链
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
//RequestMappingHandlerMapping的父类AbstractHandlerMapping的方法
//查找handler和拦截器,形成处理链
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
- 现在的过程是比较清楚的,分为下面几步
- 从请求中解析出url
- 根据url从之前说过的url与handlerMethod中找到拿到的handlerMethod集合
- 选出了适合的handlerMethod返回回去,所以HandlerExecutionChain中的handler属性是一个handlerMethod对象
//AbstractHandlerMethodMapping类的方法
//根据请求获取合适的HandlerMethod对象
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 = 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 + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
//AbstractHandlerMethodMapping类的方法
//根据url获取合适的HandlerMethod对象
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
lookupPath + "] : " + matches);
}
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
public List<T> getMappingsByUrl(String urlPath) {
return this.urlLookup.get(urlPath);
}
总结
整个流程还是比较清楚的,解析标签<mvc:annotation-driven/>、实例化RequestMappingHandlerMapping时会建立映射关系、请求时查找映射关系。接下来会接着对拦截器进行分析