SpringMVC组件之HandlerMapping分析
目录
1. HandlerMapping概述
2. HandlerMapping的类图
3. AbstractHandlerMapping分析
4. AbstractUrlHandlerMapping族分析
5. AbstracthandlerMethodMapping族分析
6. 总结
1. HandlerMapping概述
在DispatcherServlet的dodispatch方法中有这段断码:
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
在判断完request是不是文件上传请求后,dodispatch调用了getHandler来获取了一个类型为HandlerExecutionChain的对象。而getHandler方法如下:
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
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;
}
可以看到其遍历了本地的handlerMappings属性,对其中的每一个HandlerMapping调用了getHandler方法,其返回值是一个HandlerExecutionChain对象。
可以看出HandlerExecutionChain其实就是对Handler和HandlerInterceptor的一个包装。
由此我们可以知道HadlerMapping的作用就是根据传入的HttpServletRequest对象找到相应的HandlerExecutionChain,而HandlerExecutionChain就是对Handler和HandlerIntercptor的包装
而关于DispatcherServlet的初始化在之前也有过介绍了,在其onRefresh方法中调用initStrategies,用这个来初始化其九大组件,对于其中的HandlerMapping组件,初始化逻辑是
- 根据detectAllHandlerMappings标志为来决定是否在SpringMVC及其父容器中找到所有的类型为HandlerMapping的bean,如果为True的话,则是从容器中获取,并根据@Order进行排序,否则的话就仅从容器中获取beanname为handlerMapping的bean,detectAllHandlerMappings默认为True。
- 如果detectAllHandlerMappings为True而容器中没有类型为HandlerMapping的bean,或者detectAllHandlerMappings为False但容器中没有beanname为handlerMapping的bean的话,就根据DispatcherServlet.properties这个文件中配置的HandlerMapping类名来初始化HandlerMapping.
很据上面这个逻辑的话,那我们一般配置SpringMVC的时候没有主动的配置HandlerMapping,那应该就是采用的DispatcherServlet.properties这个文件里配置的类作为HandlerMapping了哦。但其实我们一般配置的时候会有一个或者使用@EnableWebMVC,这个标签或者注解其实帮助我们做了一些事情的,比如采用XML配置的话会有:
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}
}
可以看到当配置了annotation-driven后,MvcNamespaceHandler会使用AnnotationDrivenBeanDefinitionParser对XML文件进行解析,而这个Parser有这样的代码:
public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();
public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();
也就是说当配置了mvc:annotation-driven时,框架会自动的将HandlerMapping设置为RequestMappingHandlerMapping,将HandlerAdapter设置为RequestMappingHandlerAdapter。
2. HandlerMapping的类图
从这个体系结构我们可以看出,HandlerMapping主要可以分为两类,一类继承自AbstractUrlHandlerMapping,一类继承自AbstractHandlerMethodMapping,这个两个类又都继承了AbstractHandlerMapping,实现了MatchableHandlerMapping。
HandlerMapping是一个函数接口,只有一个方法
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
3. AbstractHandlerMapping分析
可以看到AnstractHandlerMapping继承了WebApplicationObjectSupport,所以其初始化的时候会调用模版方法initApplicationContext.
...
private final List<Object> interceptors = new ArrayList<>();
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
...
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
其中extendInterceptors是个模版方法,默认实现为空。子类可以覆盖这个方法,用以在已有拦截器之前和之后注册额外的拦截器。
detectMappedInterceptors将容器中所有类型为MapperIntercepter类型的bean添加到了adaptedInterceptors属性中
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
initIntercptors的函数内容为:
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
if (interceptor instanceof HandlerInterceptor) {
return (HandlerInterceptor) interceptor;
}
else if (interceptor instanceof WebRequestInterceptor) {
//使用适配器模式将WebRequestInterceptor适配为HandlerInterceptor
return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
}
else {
throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
}
}
对于interceptors这个属性来说,有两种设置方法,一种是通过HandlerMapping的setInterceptors方法设置,一种是通过子类实现extendInterceptors钩子方法进行设置。
方法detectMappedInterceptor将容器中类型为MapperInterceptor类型的bean查找出来,在这里将查找到的MapperInterceptor存储到了adaptedInterceptors这个属性中了,MapperInterceptor是HandlerInterceptor的子类,用了装饰者模式,除了preHandle。postHandle和afterCompletion之外还提供了match方法,也就是说MapperInterceptor的是由作用范围的,会根据它的includePatterns,excludePatterns来决定是否拦截某个请求路径。
方法initInterceptors,会遍历interceptors属性,先判断其是否为null,是的话就报错,否者
调用adaptInterceptor方法,将其添加到adaptInterceptor属性中。
从以上代码中我们可以得出结论,AbstractHandlerMapping会引用到容器中所有的interceptor,并且将其存储在其adaptedInterceptors属性中。接下来看看其主要方法,getHandler:
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 = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
getHandler首先调用了其模版方法getHandlerInternal,交由子类去找handler,然后将子类返回的handler做个判断,如果为空则使用默认的handler,如果默认的handler也为空的话就直接返回null。
得到不为空的handler就判断其是否是string类型,是的话就从容器中获取这个name代表的Bean。
AbstractHandlerMapping有了handler,还有所有的interceptor,现在需要做的就是将handler和需要配置的interceptor一起封装起来形成HandlerExecutionChain,所以其调用了getHandlerExecutionChain,根据request找到了需要的interceptor,这里的interceptor分分为了两类,一类是MappedInterceptor,需要选择性的配置,一类是非MappedInterceptor,这种interceptor会拦截所有的request。
**可以看出,AbstractHandlerMapping拥有容器中所有的interceptor,存储在其adaptedInterceptors属性中。然后交由子类通过getHandlerInternal方法找到request对应的Handler,最后遍历adaptedInterceptors属性找到需要配置的interceptor,最后封装成HandlerExecutionChain对象返回给DispatcherServlet。**在源码中还可以看出AbstractHandlerMapping还和跨域请求有关,这部分就在之后进行分析了。
4. AbstractUrlHandlerMapping族分析
从上面的分析,我们知道了AbstractHandlerMapping的子类需要做的就是实现getHandlerInternal,即通过某种方式去找到request相对应的handler。AbstractUrlHandlerMapping从名字上就可以看出它是通过request的url进行匹配的。这个类簇的大致原理是将url和handler的对应关系用map保存起来,在getHandlerInternal方法中使用url从Map中获取其对应的Handler。AbstractUrlHandlerMapping实现了用url在Map中查找Handler的过程,而Map的初试化则交给了具体的子类去完成。
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Mapping [" + lookupPath + "] to " + handler);
}
else if (handler == null && logger.isTraceEnabled()) {
logger.trace("No handler mapping found for [" + lookupPath + "]");
}
return handler;
}
即通过lookupHandler方法去找到handler,
如果没有找到,即handler为null:
则判断路径是否为“/”,是的话将rootHandler设置给rawHandler(rootHandler是AbstractUrlHandlerMapping的一个属性,用于处理路径为“/”的请求)
如果路径不是"/",或者rootHandler没有设置,则将rawHandler设置为defaultHandler。如果rawHandler不为空,则看rawHandler是否为String类型,如果是String类型,则从容器中获取对应的Bean,然后调用了buildPathExposingHandler给rawHandler添加两个内部拦截器。
从这个过程可以看出,主要的方法就两个lookupHandler与buildPathExposingHandler。
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
//先看哈是否能够直接匹配上
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {//可以直接通过url找到handler
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern match?
//临时变量,因为一个url可能与多个pattern相匹配
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
//useTrailingSlashMatch标志代表/AA/等于/AA
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern +"/");
}
}
}//到这里已经找到了与request的url相匹配的所有pattren。
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
//找到与url最优匹配的pattern
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
//找到bestMatch相对应的handler
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
//有可能多个Pattren的顺序相同,再次比较下
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isDebugEnabled()) {
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
虽然说有一个用于request与handler对应的Map,但是大部分情况下也不能直接通过url找到handler。因为有的handler使用了pattern的匹配模式。
buildPathExposingHandler方法用于给查找到的Handler注册两个拦截器PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor。
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
//添加拦截器
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {
private final String bestMatchingPattern;
private final String pathWithinMapping;
public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
this.bestMatchingPattern = bestMatchingPattern;
this.pathWithinMapping = pathWithinMapping;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
return true;
}
}
protected void exposePathWithinMapping(String bestMatchingPattern, String pathWithinMapping,
HttpServletRequest request) {
request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern);
request.setAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);
}
private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {
private final Map<String, String> uriTemplateVariables;
public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
this.uriTemplateVariables = uriTemplateVariables;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
exposeUriTemplateVariables(this.uriTemplateVariables, request);
return true;
}
}
protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) {
request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
}
在这个地方我们可以知道AbstractUrlHandlerMapping的getHandlerInternal会调用lookupHandler,而lookupHandler会从handlerMap中找到与url相匹配的handler,而这个找的过程又可以分为直接查找与通过pattern模式匹配查找,如果能找到的话会调用buildPathExposingHandler方法将一些属性设置到request域中。
在这个地方其实也就是一个查找的过程,那我们再去看看查找所需要的handlerMap是怎样建立的。
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for (String urlPath : urlPaths) {
registerHandler(urlPath, beanName);
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
//如果handler是String类型的且没有设置lazyInitHandlers,则从SpringMVC容器中获取handler.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
//从handlerMap中看这个url是否已经注册过handler了,可以多个url对应一个handler,但不能说一个完全一模一样的url对
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else {
//如果路径等于/则设置为rootHandler
if (urlPath.equals("/")) {
if (logger.isInfoEnabled()) {
logger.info("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
//如果路径等于"/*"则设置defaultHandler
else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) {
logger.info("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) {
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
可以看到有两个registerHandler,第一个的参数为String[]和String可以通过这个方法来将多个pattern或者url注册到一个handler上(这里的handler是一个容器中的bean),它会调用第二个方法。第二个则是会先从容器中检查这个url是否已经组册过了,不允许一个url(注意是调用的get方法)对应多个handler。然后还会根据路径是‘/’或者‘/*’来设置rootHandler和defaultHandler。
现在再来看看AbstractUrlHandlerMapping的子类,SimpleUrlHandlerMapping,
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
SimpleUrlHandlerMapping重写了父类的initApplicationContext,除了直接调用父类的初试方法之外,还调用了自己的registerHandlers方法:
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
urlMap.forEach((url, handler) -> {
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
// Remove whitespace from handler bean name.
if (handler instanceof String) {
handler = ((String) handler).trim();
}
registerHandler(url, handler);
});
}
}
可以看出SimpleUrlHandlerMapping类就是直接将自身的urlMap内容注册到handlerMap中。
另一个继承自AbstractUrlHandlerMapping的就是AbstractDetecingUrlHandlerMapping,这个类也是非常简单的
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {
private boolean detectHandlersInAncestorContexts = false;
/**
* Set whether to detect handler beans in ancestor ApplicationContexts.
* <p>Default is "false": Only handler beans in the current ApplicationContext
* will be detected, i.e. only in the context that this HandlerMapping itself
* is defined in (typically the current DispatcherServlet's context).
* <p>Switch this flag on to detect handler beans in ancestor contexts
* (typically the Spring root WebApplicationContext) as well.
*/
public void setDetectHandlersInAncestorContexts(boolean detectHandlersInAncestorContexts) {
this.detectHandlersInAncestorContexts = detectHandlersInAncestorContexts;
}
/**
* Calls the {@link #detectHandlers()} method in addition to the
* superclass's initialization.
*/
//AbstractDetecingHandlerMapping也是通过重写父类的initApplicationContext来设置handlerMap的
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
/**
* Register all handlers found in the current ApplicationContext.
* <p>The actual URL determination for a handler is up to the concrete
* {@link #determineUrlsForHandler(String)} implementation. A bean for
* which no such URLs could be determined is simply not considered a handler.
* @throws org.springframework.beans.BeansException if the handler couldn't be registered
* @see #determineUrlsForHandler(String)
*/
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + applicationContext);
}
//在容器中找到所有的bean的name
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
//这是个模版方法,通过bean的name得出url路径
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
//得到对应关系的话就组册到handlerMap中
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
/**
* Determine the URLs for the given handler bean.
* @param beanName the name of the candidate bean
* @return the URLs determined for the bean, or an empty array if none
*/
protected abstract String[] determineUrlsForHandler(String beanName);
}
而AbstractDetectingUrlHandlerMapping有子类,BeanNameUrlHandlerMapping,而这个HandlerMapping的源码
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<>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
}
它所做的也很简单,就是看看beanName和alias是不是以"/"开头,如果是的话就将其作为url。
至此AbstractUrlHandlerMapping的分析就完毕了,可以看出它的层次结构也是很清晰的,在AbstractUrlHandlerMapping中实现了在handlerMap中通过url查找Handler的逻辑,而handlerMap的初始化则是交给子类来处理的,这里初始化又分为两种,一种是直接给出对应关系,另一种则是在容器中查找beanName或者alias以”/“开头的。
5. AbstracthandlerMethodMapping族分析
AbstractHandlerMethodMapping系列是将Method作为Handler来使用的,这也是平时使用最多的一种Handler–HandlerMethod,例如@RequestMapping所注释的方法就是这种handler.
HandlerMethod的主要继承结构是HandlerMethod–>InvocableHandlerMethod–>ServletInvocableHandlerMethod。
HandlerMethod的主要属性为:
Object bean; //用于封转bean
Method method; //用于具体处理请求的method
BeanFactory beanfactory; //主要用于新建HandlerMethod时传入的Handler时String类型的情况,这时要从容器中根据beanName获取到真实的Bean,并将其设置为Handler
Method bridgedMethod; //如果method是bridge method则设置为其所对应的原有方法,否则直接设置为method.这个与泛型有关
Methodparameter[] parameters;//代表处理请求的方法。
关于这个类族,在之后的HandlerAdapter中我们会再次的详细的去看看的。现在就先回到AbstractHandlerMethodMapping中,
AbstractHandlerMethodMapping还实现了InitializingBean,这是bean生命周期中的一个方法,会在参数设置完成后调用其afterPropertiesSet方法,这个方法的内容为:
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
//从容器中获取所有的beanNames
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
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.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
//通过isHandler来筛选Handler 这是个模版方法,在RequestMappingHandlerMapping里面实现了这个方法,逻辑就是判断类是否有@Controller或者@RequestMapping注释
if (beanType != null && isHandler(beanType)) {
//detectHandlerMethods负责将Handler保存到Map中,
detectHandlerMethods(beanName);
}
}
}
//可以对Handler进行一些初试化工作
handlerMethodsInitialized(getHandlerMethods());
}
protected void detectHandlerMethods(final Object handler) {
//获取实际Handler的类型
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
//这是个模版方法,在RequestMappingHandlerMapping中实现,将@RequestMapping注解的信息转换为RequestMappingInfo
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);
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
到现在我们可以清楚AbstractHandlerMethodMapping的初试化过程,总结起来就是查找容器中所有的beanName,然后遍历这些beanName,对每一个beanName取得其类类型,看它是否是有@Controller或者@RequestMapping注解(这是在RequestMappingHandlerMapping中完成的),如果有的话会调用detectHandlerMethods,这个方法会遍历传入Bean的所有符合条件的方法,即可以理解为有@RequestMapping注解的方法。将@RequestMapping中的信息转换为RequestMappingInfo对象,就形成了method与RequestMappingInfo对应关系的map,最后再将这个Map存储在mappingRegistry属性中.这是AbstractHandlerMethodMapping的内部类MappingRegistry。这个内部类主要有以下属性
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
看了其初始化过程,再看下其是如何运作的。从之前的分析中知道AbstractHandlerMapping会调用子类的getHandlerInternal方法,因此,我们可以先从这个方法看起走:
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 + "]");
}
}
//createWithResolvedBean是用于处理handlerMethod是String类型的情况
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
现在看这个方法就能看的很清楚了,它就是通过request的lookUpPath找到Handler,即HandlerMethod对象。如果找到的话就调用createWithResolvedBean方法后返回。在这个过程中还用了一个读写锁来保护整个方法,这是因为前面说的MappingRegistry中有线程不安全的集合类。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
//Match是个内部类,封装了匹配条件和HandlerMethod两个属性
List<Match> matches = new ArrayList<>();
//根据lookupPath直接在mappingRegistry的urlLookup属性中查找T,这个T是类类型,默认是RequestMappingInfo
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//最终调用了RequestMappingInfo的getMatchingCondition方法
addMatchingMappings(directPathMatches, matches, request);
}
//如果不能根据lookupPath找到RequestMappingInfo对象,则将所有的RequestMappingInfo都遍历一遍,看是否和request相匹配
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
//如果有与request相匹配的RequestMappingInfo
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(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);
//返回找到的handlerMethod
return bestMatch.handlerMethod;
}
else {
//返回null
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
6. 总结
在这篇笔记中对HandlerMapping类族做了个简单的分析,可以清楚的是:HandlerMapping就只定义一个方法,getHandler,作用是根据request找到相应的HandlerExecutionChain。HandlerExecutionChain就是对Interceptor和handler的包装。而Handler的定义是丰富多样的,可能是某个类也可能是某个方法,所以后面流程需要交给HandlerMapping处理。而HandlerMapping有个抽象类AbstractHandlerMapping,这个类拥有容器中所有的interceptor,所以关于interceptor的处理就交给了AbstractHandlerMapping,而找Handler的方法就交给了子类去完成,所以子类需要实现模版方法getHandlerInternal。而从request找handler又分为了两大类,一是根据url路径来查找,即AbstractUrlHandlerMapping类族,它又分为SimpleUrlHandlerMapping,它的handlerMap是我们直接告诉容器的,而AbstractDetctingHandlerMapping则是从容器中获取url与bean的对应关系,它的子类BeanNameHandlerMapping就是看beanName或者alias是否以’/'开始。二是根据RequestMappingInfo来查找Handler(即AbstractHandlerMethodMapping),这里的Hanndler就是HandlerMethod的类型。它的基本思路就是查找到容器中所有的以@Controller或者@RequestMapping注解的bean,然后遍历bean中的方法,看方法是否被@RequestMapping注释,有的话就将@RequestMapping中的信息转化为RequestMappingInfo,这是RequestCondition的子类。将RequestMappingInfo和HandlerMethod的对应关系放到Map中,然后将这些信息放到AbstractHandlerMethodMapping的MappingRegistry属性中,以供之后查找用。
推荐阅读
-
SpringMVC 4.3 源码分析之 HandlerExceptionResolver
-
SpringMVC组件之HandlerMapping分析
-
LotusPhp笔记之:基于ObjectUtil组件的使用分析_PHP
-
Django框架之DRF 认证组件源码分析、权限组件源码分析
-
LotusPhp笔记之:基于ObjectUtil组件的使用分析
-
LotusPhp笔记之:基于ObjectUtil组件的使用分析
-
React学习之受控组件与数据共享实例分析
-
Android编程四大组件之Activity用法实例分析
-
Android编程四大组件之Activity用法实例分析
-
React组件设计模式之组合组件应用实例分析