概述
AbstractHandlerMapping
AbstractHandlerMapping这个类,在父类进行设置容器的时候,就会被调用到的。那么调用的是
/**
* Initializes the interceptors.
* @see #extendInterceptors(java.util.List)
* @see #initInterceptors()
*/
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.mappedInterceptors);
initInterceptors();
}
/**
* Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors.
* <p>This is called in addition to any {@link MappedInterceptor}s that may have been provided
* via {@link #setInterceptors}, by default adding all beans of type {@link MappedInterceptor}
* from the current context and its ancestors. Subclasses can override and refine this policy.
* @param mappedInterceptors an empty list to add {@link MappedInterceptor} instances to
*/
protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), MappedInterceptor.class, true, false).values());
}
initInterceptors主要是放了两种的拦截器进来,一种是mappedInterceptors另一种是adaptedInterceptors,然后mappedInterceptors类型的拦截器的话只能是匹配用户请求的url才能放到用户的请求的拦截链子中去,但是后面这个adaptedInterceptors的话就可以直接放进去的。
/**
* Initialize the specified interceptors, checking for {@link MappedInterceptor}s and
* adapting {@link HandlerInterceptor}s and {@link WebRequestInterceptor}s if necessary.
* @see #setInterceptors
* @see #adaptInterceptor
*/
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");
}
if (interceptor instanceof MappedInterceptor) {
this.mappedInterceptors.add((MappedInterceptor) interceptor);
}
else {
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
}
真正使用的过程中一般是使用getHandler来获取拦截器和处理器的handler的。
/**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
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);
}
return getHandlerExecutionChain(handler, request);
}
/**
* Build a {@link HandlerExecutionChain} for the given handler, including
* applicable interceptors.
* <p>The default implementation builds a standard {@link HandlerExecutionChain}
* with the given handler, the handler mapping's common interceptors, and any
* {@link MappedInterceptor}s matching to the current request URL. Subclasses
* may override this in order to extend/rearrange the list of interceptors.
* <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a
* pre-built {@link HandlerExecutionChain}. This method should handle those
* two cases explicitly, either building a new {@link HandlerExecutionChain}
* or extending the existing chain.
* <p>For simply adding an interceptor in a custom subclass, consider calling
* {@code super.getHandlerExecutionChain(handler, request)} and invoking
* {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
* @param handler the resolved handler instance (never {@code null})
* @param request current HTTP request
* @return the HandlerExecutionChain (never {@code null})
* @see #getAdaptedInterceptors()
*/
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
chain.addInterceptors(getAdaptedInterceptors());
// 这里将刚才设置的mappedInterceptors中的url进行匹配,就是上面提到的部分了。
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
// 这里将刚才设置的mappedInterceptors中的url进行匹配,就是上面提到的部分了。
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
AbstractURLHandlerMapping
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
大致的原理是将url与handler保存在一个Map里面。
/**
* Look up a handler for the URL path of the given request.
* @param request current HTTP request
* @return the handler instance, or {@code null} if none found
*/
@Override
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();// 如果是/的话就默认用了RootHandler了。
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();// 如果是空的话就默认的handler。
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().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,它会对其进行排序的,然后排序完之后,挑选第一个,但是,可能并不是只有一个的得分情况是第一个的那个值,也就是说可能存在多个url实际上和第一个的url的得分情况是一样的,那么就应该扫描后续的那些url部分,然后将她们放在了uriTemplateVariables里面。
/**
* Look up a handler instance for the given URL path.
* <p>Supports direct matches, e.g. a registered "/test" matches "/test",
* and various Ant-style pattern matches, e.g. a registered "/t*" matches
* both "/test" and "/team". For details, see the AntPathMatcher class.
* <p>Looks for the most exact pattern, where most exact is defined as
* the longest path pattern.
* @param urlPath URL the bean is mapped to
* @param request current HTTP request (to expose the path within the mapping to)
* @return the associated handler instance, or {@code null} if not found
* @see #exposePathWithinMapping
* @see org.springframework.util.AntPathMatcher
*/
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern match?
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);
// Bean name or resolved handler?
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;
}
buildPathExposingHandler
/**
* Build a handler object for the given raw handler, exposing the actual
* handler, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}, as well as
* the {@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE} before executing the handler.
* <p>The default implementation builds a {@link HandlerExecutionChain}
* with a special interceptor that exposes the path attribute and uri template variables
* @param rawHandler the raw handler to expose
* @param pathWithinMapping the path to expose before executing the handler
* @param uriTemplateVariables the URI template variables, can be {@code null} if no variables found
* @return the final handler object
*/
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;
}
这里的俩拦截器本身就是用于将当前的Url实际匹配的Pattern、匹配条件,和URL模版参数设置到Request请求中去。这样以后就可以从url中获取了。
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(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
return true;
}
}
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;
}
}
总结下:
从DespatcherServelet类中的getHandler开始进行调用,然后调用了AbstractHandlerMapping中的getHandler方法,然后里面有个getHandlerInternal方法,这个方法要求子类进行实现的,那么,在其子类中AbstractUrlHandlerMapping中,实现了这个方法,其中比较重要的是lookupHandler方法,然后这个方法会获取handlerMap中的url的映射对象,如果没有的话就说明直接获取不到,只能模式匹配通过url的匹配确定一个集合,然后排序,然后将排序结果选取第一个返回,但是也会有多个价值同等的情况,那么这些同等的元素将会全部封装进一个集合中去,然后这个结合连同刚才匹配到的第一个结果,通过封装进一个执行ExecutionChain中去,然后层层返回,在getHandler接收到返回值之后还要检查是不是string类型的,如果是的话就尝试获取该字符串对应的对象的实例,到这里确保了一个比较重要的概念,是handler到底是谁的问题,之前是各种字符串的handler其实是url的,但是为了映射成能够被调用的handler也就是controller等的类的实例,只能getBean的形式来从容器中获取类的实例了,最后将返回的结果封装称为执行链,但是这里需要注意的是getHandlerInternal返回的handler是经过处理以后的,有可能会已经封装为chain了,所以在最后封装成chain链的时候会进行一个确认,到底需要不需要自己进一步封装了。
里面这个handlerMap子类会自动的进行注册的。
AbstractUrlHandlerMapping
/**
* Register the specified handler for the given URL paths.
* @param urlPaths the URLs that the bean should be mapped to
* @param beanName the name of the handler bean
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
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);
}
}
/**
* Register the specified handler for the given URL path.
* @param urlPath the URL the bean should be mapped to
* @param handler the handler instance or handler bean name String
* (a bean name will automatically be resolved into the corresponding handler bean)
* @throws BeansException if the handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
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.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
resolvedHandler = getApplicationContext().getBean(handlerName);
}
}
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 {
if (urlPath.equals("/")) {
if (logger.isInfoEnabled()) {
logger.info("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);// 如果是斜杠就设置为roothandler
}
else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) {
logger.info("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);// 如果是/*就设置为defaultHandler
}
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) {
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
上面的子类调用,注册使用的。
SimpleUrlHandlerMapping
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
调用了一个父类的initApplicationContext,然后调用registerHandlers方法,来完成注册handler,然后调用的就是父类的registerHandler方法了、
/**
* Calls the {@link #registerHandlers} method in addition to the
* superclass's initialization.
*/
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
/**
* Register all handlers specified in the URL map for the corresponding paths.
* @param urlMap Map with URL paths as keys and handler beans or bean names as values
* @throws BeansException if a handler couldn't be registered
* @throws IllegalStateException if there is a conflicting handler registered
*/
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
/**
* Calls the {@link #detectHandlers()} method in addition to the
* superclass's initialization.
*/
@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 {
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
}// 获取容器中的所有bean的名字
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// 对每个beanName解析url,如果能解析到的话就注册到父类的map中。
// 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");
}
}
}
}
// 对每个beanName解析url,如果能解析到的话就注册到父类的map中。
// 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");
}
}
}
}
BeanNameUrlHandlerMapping
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);
}
}
设置beanName和alias是不是以/开头,如果是的话就将其作为url.
AbstractControllerUrlHandlerMapping
/**
* This implementation delegates to {@link #buildUrlsForHandler},
* provided that {@link #isEligibleForMapping} returns {@code true}.
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
Class<?> beanClass = getApplicationContext().getType(beanName);
if (isEligibleForMapping(beanName, beanClass)) {
return buildUrlsForHandler(beanName, beanClass);
}
else {
return null;
}
}
将实现了Controller或者是标记了Controller注解的类的bean作为handler,并且可以通过设置excludedClasses和excludedPackages将不包含的bean或者不包含的包下的所有bean排除在外。
protected boolean isEligibleForMapping(String beanName, Class<?> beanClass) {
if (beanClass == null) {
if (logger.isDebugEnabled()) {
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean type could not be determined");
}
return false;
}
if (this.excludedClasses.contains(beanClass)) {
if (logger.isDebugEnabled()) {
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean class is explicitly excluded: " + beanClass.getName());
}
return false;
}
String beanClassName = beanClass.getName();
for (String packageName : this.excludedPackages) {
if (beanClassName.startsWith(packageName)) {
if (logger.isDebugEnabled()) {
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean class is defined in an excluded package: " + beanClass.getName());
}
return false;
}
}
return isControllerType(beanClass);// 是不是controller类型
}
ControllerClassNameHandlerMapping和ControllerBeanNameHandlerMapping,一个是使用了beanName作为url一个是使用的className作为的url.
AbstractHandlerMethodMapping
因为他实现了一个InitializingBean所以会自动调用其
/**
* Detects handler methods at initialization.
*/
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
这个方法,然后调用initHandlerMethods方法。
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) &&
isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
detectHandlerMethods方法
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
});
for (Method method : methods) {// 将符合要求的handler保存到三个map中去。
registerHandlerMethod(handler, method, mappings.get(method));
}
}
/**
* Register a handler method and its unique mapping.
* @param handler the bean name of the handler or the handler instance
* @param method the method to register
* @param mapping the mapping conditions associated with the handler method
* @throws IllegalStateException if another method was already registered
* under the same mapping
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
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 methodn" + oldHandlerMethod + " mapped.");
}
// 添加handlermethods
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped "" + mapping + "" onto " + newHandlerMethod);
}
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);// 添加urlMap中
}
}
if (this.namingStrategy != null) {
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);// 添加updateNameMap中、
}
}
// 添加handlermethods
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped "" + mapping + "" onto " + newHandlerMethod);
}
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);// 添加urlMap中
}
}
if (this.namingStrategy != null) {
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);// 添加updateNameMap中、
}
}
在AbstractHandlerMapping中实际上在调用getHandler的时候调用了一个getInternalHandler方法,
这个方法在AbstractHandlerMethodMapping中也进行了实现。
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
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);
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.handlerMethods.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) {
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 {// 无法使用lookupPath得到匹配条件,将所有的匹配条件加入matches中去。
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}
RequestMappingHandlerMapping
/**
* Uses method and type-level @{@link RequestMapping} annotations to create
* the RequestMappingInfo.
* @return the created RequestMappingInfo, or {@code null} if the method
* does not have a {@code @RequestMapping} annotation.
* @see #getCustomMethodCondition(Method)
* @see #getCustomTypeCondition(Class)
*/
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
根据@RequestMapping来找方法的,如果没有注解的,就返回null了。
总结一下:
如何匹配的方法呢?
封装,将方法封装成一个RequestMappingInfo类,里面封装了多个的相关的方法标记中可能会有的参数。以次来标识一个方法。
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
private final String name;
private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
private final ParamsRequestCondition paramsCondition;
private final HeadersRequestCondition headersCondition;
private final ConsumesRequestCondition consumesCondition;
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;
整个Method类型的HandlerMapping的话主要的流程如下:
首先是AbstractHandlerMethodMapping类实现了InitilizingBean接口,那么会现调用afterPropertiesSet来设置其参数,调用initMethodHandler方法,然后这个方法中会调用detectHandlerMethod方法,这个方法会将handler保存在Map中去,
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);// 筛选出来能够进行匹配的方法添加到这个map中去。
return true;
}
else {
return false;
}
}
});
for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));// 进行相应的注册
}
}
注册分为三个不同的map
一个handlerMap保存的是匹配条件和方法的映射关系。
一个urlMap保存的是url和方法的映射关系。
一个nameMap是保存的方法名和MethodHandler的对应关系
后面这俩属于多值的情况。
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
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 methodn" + oldHandlerMethod + " mapped.");
}
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped "" + mapping + "" onto " + newHandlerMethod);
}
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
if (this.namingStrategy != null) {
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);
}
}
上面完成一个初始化的过程,然后调用过程中会调用其handlerInternal方法
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
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);
}
这个方法里面主要是lookupHandlerMethod方法了。
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
List<T> directPathMatches = this.urlMap.get(lookupPath);// 根据url获取可以进行匹配的method
if (directPathMatches != null) {// 将匹配的条件添加到matches中去
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {// 没有匹配条件
// No choice but to go through all mappings...所有的匹配条件全部添加进去
addMatchingMappings(this.handlerMethods.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) {// 前两个相同抛出异常
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);
}
}
再总之:
整个的流程基本上就是先初始化基本数据,首先在AbstractHandlerMapping中会默认初始化一些拦截器,然后也与部分请求的路径等进行匹配,一旦匹配成功可以直接添加上,不过也存在不用匹配直接添加的情况。
AbstractUrlHandlerMapping这个是针对Url进行匹配的,主要的目的是找到Controller的类,它是根据AbstractHandlerMapping中的方法,getHandler方法也是这种类的外界访问切入口来调用的,内部访问了getHandlerInternal方法,然后这个方法会调用lookuppath寻找能够进行匹配的url对应的controller,然后在匹配的过程中如果直接能匹配就返回,不能的话就采用模式匹配,然后匹配结果再排个序,然后获取第零个作为返回的。不过如果存在多个和第零个价值一样的也会将其封装进一个chain中返回,这个chain是作为多重情况返回的,返回的结果回到父类AbstractHandlerMapping的getHandler中去,然后封装刚刚返回的数据称为一个chain链。然后直接作为结果返回就好了。
需要注意的是,在获取url与controller等的实例之间的关系时,不使用父类去挨着遍历的,而是在子类中自动注册到父类中的。所以AbstractUrlHandlerMapping中会有一个handlerMap的集合装有所以的映射关系。
AbstractMethodHandlerMapping这个是针对method来进行匹配的,
初始化过程中是因为实现了initilizingBean接口,然后会调用afterPropertiesSet方法,这个方法调用了initHandlerMethods方法,在里面会获取到所有的bean对象,然后又通过循环的方式调用了detectHandlerMethods(beanName)方法,这个方法会对类中的放进行选取,然后将选取到的符合条件的方法用于注册三个map,其实会用到controller和method和mappings.get(method).获取到的这个集合通过循环遍历的方式进行注册,会注册到handlerMap和urlMap和nameMap中去,到这里注册完成后基本上完成了初始化的过程。
调用的过程中实际上是调用了getHandlerInternal方法的,这个方法中会调用lookupHandlerMethod方法来对方法进行筛选,能匹配到的都放到一个集合里面去,然后对其进行排序,选取排序后的第一个作为最好的,如果还有第二个和第一个是一样的话就说明冲突了,抛出异常,然后将最好的这个匹配结果封装成methodResolver返回去,翻回去也就是返回到了getHandler中去了,那么接下来将得到的method的handler进行封装,将其封装为ExecutionChain然后返回即可完成了。
最后
以上就是还单身金毛为你收集整理的SpringMVC源码解读--HandlerMapping代码解读AbstractHandlerMappingAbstractURLHandlerMappingAbstractUrlHandlerMappingSimpleUrlHandlerMappingAbstractDetectingUrlHandlerMappingBeanNameUrlHandlerMappingAbstractControllerUrlHandlerMappingAbstractHandlerMethodMapping的全部内容,希望文章能够帮你解决SpringMVC源码解读--HandlerMapping代码解读AbstractHandlerMappingAbstractURLHandlerMappingAbstractUrlHandlerMappingSimpleUrlHandlerMappingAbstractDetectingUrlHandlerMappingBeanNameUrlHandlerMappingAbstractControllerUrlHandlerMappingAbstractHandlerMethodMapping所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复