天天看點

SpringMVC(九)-----AbstractHandlerMethodMapping如何注冊url和HandlerMethod之間的關系推薦公衆号引言上一篇正文

目錄

  • 推薦公衆号
  • 引言
  • 上一篇
  • 正文
    • initHandlerMethods
    • registerHandlerMethod
    • 維護關系的map集合說明

推薦公衆号

有彩蛋哦!!!(或者公衆号内點選網賺擷取彩蛋)

SpringMVC(九)-----AbstractHandlerMethodMapping如何注冊url和HandlerMethod之間的關系推薦公衆号引言上一篇正文

引言

現在大緻有個概念,HandlerMapping維護url和HandlerMethod之間的關系

HandlerMethod包含的資訊是:類,方法,參數

HandlerAdapter是具體執行HandlerMethod的

DispatcherServlet在接到請求後就根據HandlerMapping找到HandlerMethod

HandlerAdapter執行HandlerMethod

這篇解釋SpringMVC是如何維護url和HandlerMethod()之間的關系

上一篇

SpringMVC(八)-----AbstractHandlerMapping

https://blog.csdn.net/yueloveme/article/details/89878530

正文

initHandlerMethods

/**
	 * Detects handler methods at initialization.
	 */
	 AbstractHandlerMethodMapping 
	 項目啟動時會調用這個方法維護url和HandlerMethod之間的關系
	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}
/**
	 * Scan beans in the ApplicationContext, detect and register handler methods.
	 * @see #isHandler(Class)
	 * @see #getMappingForMethod(Method, Class)
	 * @see #handlerMethodsInitialized(Map)
	 */
	 AbstractHandlerMethodMapping 
	 這個方法會循環周遊Spring IOC容器中的bean 将有 Controller RequestMapping等注解的bean
	 維護url和HandlerMethod關系
	protected void initHandlerMethods() {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for request mappings in application context: " + 
			getApplicationContext());
		}
		IOC中所有bean
		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);
					}
				}
				isHandler(beanType) 判斷是否有Controller等注解
				if (beanType != null && isHandler(beanType)) {
					維護關系
					detectHandlerMethods(beanName);
				}
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
/**
	 * {@inheritDoc}
	 * <p>Expects a handler to have either a type-level @{@link Controller}
	 * annotation or a type-level @{@link RequestMapping} annotation.
	 */
	 RequestMappingHandlerMapping
	 判斷是否有 Controller RequestMapping注解
	@Override
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}
/**
	 * Look for handler methods in a handler.
	 * @param handler the bean name of a handler or a handler instance
	 */
	 AbstractHandlerMethodMapping 
	protected void detectHandlerMethods(final Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				getApplicationContext().getType((String) handler) : 
				handler.getClass());
		final Class<?> userType = ClassUtils.getUserClass(handlerType);
		
		目前bean中所有的可以請求的方法(也就是@RequestMapping定義的接口)
		方法轉成RequestMappingInfo(包含url,請求類型GET,POST等資訊)
		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);
		}
		注冊 關系 裡面維護了幾個map集合
		for (Map.Entry<Method, T> entry : methods.entrySet()) {
			Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), 
			userType);
			T mapping = entry.getValue();
			registerHandlerMethod(handler, invocableMethod, mapping);
		}
	}
           

registerHandlerMethod

/**
	 * Register a handler method and its unique mapping. Invoked at startup for
	 * each detected handler method.
	 * @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
	 */
	 AbstractHandlerMethodMapping 
	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		this.mappingRegistry.register(mapping, handler, method);
	}
public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				獲得HandlerMethod
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				assertUniqueMethodMapping(handlerMethod, mapping);

				if (logger.isInfoEnabled()) {
					logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
				}
				維護 RequestMappingInfo 和 HandlerMethod之間的關系
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					維護了  url(例如 /api/users) 和	RequestMappingInfo
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					維護了 方法名 和 對應HandlerMethod集合的關系
					addMappingName(name, handlerMethod);
				}

				CorsConfiguration corsConfig = initCorsConfiguration(handler, method,
				 mapping);
				if (corsConfig != null) {
					this.corsLookup.put(handlerMethod, corsConfig);
				}
				維護 RequestMappingInfo和 MappingRegistration之間的關系 
				MappingRegistration包含 下面資訊
				this.registry.put(mapping, new MappingRegistration<T>(mapping, 
				handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}
           

維護關系的map集合說明

class MappingRegistry {
		維護 T 和 MappingRegistration之間的關系 MappingRegistration包含
		資訊看上面代碼
		private final Map<T, MappingRegistration<T>> registry = new HashMap<T, 
		MappingRegistration<T>>();

		維護 T 和 HandlerMethod之間的關系 T一般是RequestMappingInfo
		HandlerMethod 包含了 類 方法 參數資訊
		private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, 
		HandlerMethod>();

		維護了  url(例如 /api/users) 和 T 之間的關系  T一般是RequestMappingInfo(包含url和
		請求方式等資訊)
		一個url 可能對應多個RequestMappingInfo 
		比如 /api/users 對應 /api/users POST 和 /api/users GET
		private final MultiValueMap<String, T> urlLookup = new 
		LinkedMultiValueMap<String, T>();

		維護了 方法名(方法名規則控制器名稱的大寫字母+#+方法名) 和 對應HandlerMethod集合的關
		系  因為同樣的方法名可能對應多個不同的HandlerMethod
		方法名規則 例如 控制器叫StudentController 其中一個方法名叫 addStudent 那麼
		addStudent 的方法名叫  SC#addStudent
		private final Map<String, List<HandlerMethod>> nameLookup =
				new ConcurrentHashMap<String, List<HandlerMethod>>();
		
		private final Map<HandlerMethod, CorsConfiguration> corsLookup =
				new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();