HandlerMapping的作用是根据request找到相应的处理器Handler和Interceptors。
我们看下接口的定义
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
复制代码
AbstractHandlerMapping
AbstractHandlerMapping是HandlerMapping的抽象实现,很显然用模版模式来设计。子类只需要通过模版方法提供一些初始值或者具体算法。
获取Handler的过程通过模版方法getHandlerInternal交给子类。
AbstractHandlerMapping中保存所用配置的Interceptor,在获取到Handler后会自己根据从request提取的lookupPath将相应的Interceptors装配上去,当然子类也可以通过getHandlerInternal设置自己的Interceptor。
创建AbstractHandlerMapping之器
AbstractHandlerMapping的创建就是在initApplicationContext方法中实现
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);//模版方法,用于给子类提供一个增加(或修改)Interceptors入口,目前没用到
//将SpringMVC容器及父容器中所有MapperInterceptor的Bean添加到mappedInterceptors
detectMappedInterceptors(this.mappedInterceptors);
//将interceptors里的对象添加到mappedInterceptor或者adaptedInterceptors
initInterceptors();
}
复制代码
- interceptors:用于配置SpringMVC的拦截器,有两种设置方式:1)注册HandlerMapping时通过属性设置。2)通过extendInterceptors钩子方法设置。interceptors并不会直接使用,而是通过initInterceptors方法分配到mappedInterceptor或者adaptedInterceptors
- mappedInterceptor: 与url进行匹配,成功后放到getHandler的返回值HandlerExecutionChain中。
- adaptedInterceptors: 这种Interceptor不要匹配,在getHandler中会全部添加到HandlerExecutionChain。只能从interceptors中取。
-
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request);//模版方法,子类实现 if (handler == null) { handler = getDefaultHandler();//通过set方法注入。可在配置HandlerMapping时配置,也可在子类进行设置 } if (handler == null) { return null; } // if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } return getHandlerExecutionChain(handler, request); } 复制代码
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
chain.addInterceptors(getAdaptedInterceptors());//设置adapted
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
复制代码
这个方法简单,首先使用handler创建HandlerExectionChain类型变量,然后添加拦截器。
AbstractUrlHandlerMapping系列
AbstractUrlHandlerMapping是通过url来进行匹配的。原理是将URL和对应的Handler保存在Map中,在getHandlerInternal中用URL从map中取Handler,在这个类中实现了取handler的过程,而Map的初始化则交给子类完成。
private Object rootHandler;
private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
//具体获取Handler
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
//
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
//该方法lookupHandler用lookupPath从Map中查找Handler
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 直接Map中取
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// 若Sring类型则从容器中取
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern 匹配,比如使用带*号的模式与URL进行匹配
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
}
String bestPatternMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestPatternMatch = matchingPatterns.get(0);
}
if (bestPatternMatch != null) {
handler = this.handlerMap.get(bestPatternMatch);
//
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, 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<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestPatternMatch, 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, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, 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;
}
复制代码
buildPathExposingHandler方法用于给查找到的Handler注册两个内部拦截器PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor,主要是将与当前URL实际匹配的Pattern、匹配条件和URL模版参数等设置到request的属性里,方便后续直接取。
- Map的初始化。 它通过registerHandler方法进行,这个方法负责AbstractUrlHandlerMapping的创建工作,registerHandler方法是通过子类调用的。这样不同的子类就可以注册不同的Handler将组件创建出来。
SimpleUrlHandlerMapping
该类定义一个Map 变量(自己定义一个Map两个作用,第一方便配置,第二在注册前做预处理),将所有的url和Handler的对应关系放在里面,最后注册到父类的Map中,而AbstractDetectingUrlHandlerMapping则是将容器中所有bean都拿出来,按一定规则注册到父类Map。
SimpleUrlHandlerMapping通过重写父类initApplicationContext方法调用自己registerHandlers(内部调父类registerHandlers)完成Handler注册。
public void initApplicationContext() throws BeansException {
super.initApplicationContext();//调父类方法
registerHandlers(this.urlMap);
}
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
String url = entry.getKey();
Object handler = entry.getValue();
// 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);//调父类的方法
}
}
}
复制代码
AbstractDetectingUrlHandlerMapping
该类也是重写initApplicationContext来注册Handler的,里面调用detectHandler方法,找到所有bean的beanName,然后用determineUrlsForHandler方法对每个beanName解析出对应的urls,注册到map(URL,beanName作为Handler),注册方法依然是调用父类的registerHandler方法。
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
protected void detectHandlers() throws BeansException {
//获取容器的所有bean的名字
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// 对每个beanName解析URL,解析到注册到父类的Map中
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);//beanName解析URL,模版方法,子类实现
if (!ObjectUtils.isEmpty(urls)) {
// 调父类
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
复制代码
AbstractDetectingUrlHandlerMapping有三个子类:我们分析BeanNameUrlHandlerMapping和AbstractControllerUrlHandlerMapping
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
@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);
}
}
复制代码
主要是检查beanName和alias是不是以"/"开头,如果是则将其作为url。
AbstractControllerUrlHandlerMapping是将实现了Controller接口或者@Controller的bean作为Handler,并且可以设置excludedClass和excludedPackages将不包含的bean或者不包含的包下所有的bean排出在外,determineUrlsForHandler主要负责将符合条件的Handler找出来,而具体用什么URL则使用模版buildUrlsForHandler交给子类去做。
protected String[] determineUrlsForHandler(String beanName) {
Class<?> beanClass = getApplicationContext().getType(beanName);
//判断是不是支持的类型
if (isEligibleForMapping(beanName, beanClass)) {
return buildUrlsForHandler(beanName, beanClass);//模版方法,子类实现
}
else {
return null;
}
}
protected boolean isEligibleForMapping(String beanName, Class<?> beanClass) {
if (beanClass == null) {
return false;
}//排除excludedClass里配置的类
if (this.excludedClasses.contains(beanClass)) {
return false;
}
String beanClassName = beanClass.getName();
//排除包下的类
for (String packageName : this.excludedPackages) {
if (beanClassName.startsWith(packageName)) {
return false;
}
}//检查是否实现Controller接口或者注释@Controller
return isControllerType(beanClass);
}
复制代码
它有两个子类ControllerClassNameHandlerMapping(使用className作为url)、ControllerBeanNameHandlerMapping(使用beanName)作为url。
总结一下:首先在AbstractUrlHandlerMapping中设计整体结构,完成查找Handler的具体逻辑,这用到一个保存url和Handler对应关系的Map,这个Map是给子类初始化的。但是提供了初始化Map的工具方法。registerHandler。
初始化Map分为两种方式,一种通过手工在配置文件注册,另一种是在spring的容器里面找。
AbstractHandlerMethodMapping系列
我们经常使用的@RequestMapping所注释的方法就是这种Handler,也就是Method类型的Handler。
//handlerMethods保存着匹配条件(RequestCondition)和HandlerMethod的对应关系
//key={[/articles/{articleId}/comment],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}
//value=FollowMeController.doComment()
private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
//保存URL与匹配条件的关系
//key=/articles/{articleId}/comment
//value={[/articles/{articleId}/comment],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
//name与HandlerMethod关系
//key=FMC#doComment
//value=FollowMeController.doComment()
MultiValueMap<String, HandlerMethod> nameMap = new LinkedMultiValueMap<String, HandlerMethod>();
复制代码
该类实现InitializingBean,所以我们很敏感的看到
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
//首选拿到所有的bean
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith("scopedTarget.") &&
isHandler(getApplicationContext().getType(beanName))){//然后根据isHandler找出Handler
detectHandlerMethods(beanName);//handler保存到Map
}
}
//对Handler进行初始化,模版方法
handlerMethodsInitialized(getHandlerMethods());
}
复制代码
首选拿到所有的bean,然后根据isHandler找出Handler,最后放到Map。
isHandler是模版方法在RequestMappingHandlerMapping实现。筛选的逻辑是检查类是否有@Controller或者@RequestMapping注释
//RequestMappingHandlerMapping
protected boolean isHandler(Class<?> beanType) {
return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
protected void detectHandlerMethods(final Object handler) {
//获取Handler类型
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// 保存Handler与匹配条件的关系,用于给registerHandlerMethod传入匹配条件
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
//如果是cglib代理的子对象类型,返回父类型,否则直接返回传入的类型
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//获取当前bean里所有符合Handler要求的Method
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
//模版方法,实现在RequestMappingHandlerMapping
//根据@RequestMapping找匹配条件
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
});
//将符合要求的Method注册起来,也就是保存到三个Map中
for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));
}
}
复制代码
detectHandlerMethods方法分两步:首先从传入的处理器中找到符合要求的方法,然后注册到Map中。 说完了怎么找到HandlerMethod,再来看一下怎么将找到的HandlerMethod注册到Map里。
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
//检查是否已经在handlerMethods中存在,若存在而且不同抛出异常
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
"' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +
oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
}
//添加到handlerMethods
this.handlerMethods.put(mapping, newHandlerMethod);
//添加到urlMap
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
//添加到nameMap
if (this.namingStrategy != null) {
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);
}
}
复制代码
//AbstractHandlerMethodMapping实战
该类中实现的getHandlerInternal方法
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//找handlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { //Match保存匹配条件和Handler
List<Match> matches = new ArrayList<Match>();
//根据lookupPath获取到匹配条件
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
//将找到的匹配条件放到matches
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
//如果上面没找到匹配条件,则将所有匹配条件加入matches
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
}
//将matches排序
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
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(handlerMethods.keySet(), lookupPath, request);
}
}
//调用链路
DispatcherServlet#doService()
doDispatch()
getHandler()
AbstractHandlerMapping#getHandler()
AbstractHandlerMethodMapping#getHandlerInternal()
AbstractHandlerMapping#getHandlerExecutionChain()
复制代码
那么SpringMVC是如何用HandlerMapping的相应实现类(RequestMappingHandlerMapping)的呢?
通过注解mvc:annotation-driven/,Spring在启动时会解析标签,注册bean。这时RequestMappingHandlerMapping就有了。当我们通过getBean方式获取时,就走到了InitializingBean逻辑。剩下的就是利用RequestMappingHandlerMapping找handler注册Handler的事了。