@RequestMapping是如何運行的?

@RequestMapping是如何運行的?

最近在寫一個權限框架,想着怎麼獲取項目裏面全部的url映射? 先說結論是怎麼獲取的,後面詳細介紹。java

Map<RequestMappingInfo, HandlerMethod> map = RequestMappingHandlerMapping.getHandlerMethods();
    // 獲取全部的key
    Set<RequestMappingInfo> requestMappingInfos = handlerMethods.keySet();
    // 獲取全部的mapping
    Set<String> mappings =new LinkedHashSet<>();
    for (RequestMappingInfo info : requestMappingInfos) {
        PatternsRequestCondition patternsCondition = info.getPatternsCondition();
        for (String mapping : patternsCondition.getPatterns()){
            mappings.add(mapping);
        }
    }
複製代碼

而後就想起來MVC運行的流程,它爲何能夠經過一個uri就能夠執行到對應的方法呢? 這確定是初始化的時候,將Controller裏面的帶有@RequestMapping註解的方法與uri保存在集合中,而後執行的時候,根據請求的uri到集合裏面去找到對應的方法, 而後經過反射執行方法,完成調用。web

@RequestMapping的初始化

RequestMappingHandlerMapping:首先從該類的的afterPropertiesSet入手:spring

public void afterPropertiesSet() {
	this.config = new RequestMappingInfo.BuilderConfiguration();
	this.config.setUrlPathHelper(getUrlPathHelper());
	this.config.setPathMatcher(getPathMatcher());
	this.config.setSuffixPatternMatch(useSuffixPatternMatch());
	this.config.setTrailingSlashMatch(useTrailingSlashMatch());
	this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
	this.config.setContentNegotiationManager(getContentNegotiationManager());

    //調用了父類的afterPropertiesSet
	super.afterPropertiesSet();
}

//判斷bean是否帶有@Controller或者@RequestMapping註解
protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

複製代碼

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet父類的初始化方法:bash

@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}

protected void initHandlerMethods() {
        //getCandidateBeanNames()獲取全部註冊的bean
	for (String beanName : getCandidateBeanNames()) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
		    // 處理候選bean
			processCandidateBean(beanName);
		}
	}
	// 打印日誌,輸出日誌handlerMethods的數量
	handlerMethodsInitialized(getHandlerMethods());
}

protected void processCandidateBean(String beanName) {
	Class<?> beanType = null;
	try {
	    // 獲取bean的類型
		beanType = obtainApplicationContext().getType(beanName);
	}
	catch (Throwable ex) {
		// An unresolvable bean type, probably from a lazy bean - let's ignore it.
		if (logger.isTraceEnabled()) {
			logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
		}
	}
	// 從候選bean中選出知足isHandler(beanType)的bean,判斷bean是否帶有@Controller或者@RequestMapping註解
	// isHandler(Class<?> beanType)在該類中未實現,具體實現是在RequestMappingHandlerMapping中,上面貼出了代碼
	if (beanType != null && isHandler(beanType)) {
		detectHandlerMethods(beanName);
	}
}

    // 在指定的handler中尋找被@RequestMapping標記的methods
    // 這裏的handler能夠理解成咱們常說的Controller
	protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
		    // 返回自己的類,若是該Class是CGLB代理的,那麼就返回它的父類
			Class<?> userType = ClassUtils.getUserClass(handlerType);
            // 基於關聯元數據的查找,選擇給定目標相似上的方法
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
						    // 選擇被@RequestMapping標記的方法,這裏返回的是RequestMappingInfo對象,mapping也被存放在該對象中
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
			    // 返回給定一個Class上的可調用的方法
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				// 註冊請求映射
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}
複製代碼

RequestMappingInfo,簡單介紹一下,這個也是下面註冊方法裏面傳遞的mapping,也就是說咱們所須要的mapping值存在這個對象中。app

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	    // 獲取帶有@RequestMapping註解方法上面的path值
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
		    //獲取方法對於類上@RequestMapping註解方法上面的path值
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
			    // 拼接起來
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
			}
		}
		return info;
	}

	protected RequestMappingInfo createRequestMappingInfo(
			RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

		RequestMappingInfo.Builder builder = RequestMappingInfo
		         // 能夠看到咱們path被賦值到paths中
				.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
				.methods(requestMapping.method())
				.params(requestMapping.params())
				.headers(requestMapping.headers())
				.consumes(requestMapping.consumes())
				.produces(requestMapping.produces())
				.mappingName(requestMapping.name());
		if (customCondition != null) {
			builder.customCondition(customCondition);
		}
		// 看一下這個builder,將path屬性轉移到哪裏?
		return builder.options(this.config).build();
	}
    public RequestMappingInfo build() {
    		ContentNegotiationManager manager = this.options.getContentNegotiationManager();

            // 能夠看到path值變成PatternsRequestCondition的一個屬性patterns;,這裏代碼比較簡單,我就知道說結論了
    		PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
    				this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
    				this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
    				this.options.getFileExtensions());

            // 而PatternsRequestCondition又是RequestMappingInfo的一個屬性,因此獲取mapping的方法找到了
            // 對應文章開頭的方法獲取全部的mapping
    		return new RequestMappingInfo(this.mappingName, patternsCondition,
    				new RequestMethodsRequestCondition(this.methods),
    				new ParamsRequestCondition(this.params),
    				new HeadersRequestCondition(this.headers),
    				new ConsumesRequestCondition(this.consumes, this.headers),
    				new ProducesRequestCondition(this.produces, this.headers, manager),
    				this.customCondition);
    	}
複製代碼

介紹這個registerHandlerMethod(Object,Method,Mapping)方法以前,先介紹幾個類HandlerMethod,看名字就知道是handle與method的結合體,這個結構保存了handle與對應的method。cors

public class HandlerMethod {

    // handle
	private final Object bean;

	@Nullable
	private final BeanFactory beanFactory;

    // handle Class
	private final Class<?> beanType;

    // 方法
	private final Method method;

	private final Method bridgedMethod;

	private final MethodParameter[] parameters;
    ...其餘幾個不重要的忽略

複製代碼

MappingRegistry是一個內部類,裏面有幾個關鍵成員變量,來存放全部的mapping與方法框架

class MappingRegistry {
        // 保存的是mapping與MappingRegistration關係。MappingRegistration
		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

        // 保存的是mapping與HandlerMethod的映射關係
		private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

        // 保存的是url與mapping的映射關係,原本這個很關鍵,可是沒有提供對外的方法,因此想要獲取mapping,仍是得從RequestMappingInfo獲取
		private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

        // 保存的是名字與List<HandlerMethod>的關係
		private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

        // // 保存的是HandlerMethod與CorsConfiguration的關係
		private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

複製代碼

咱們繼續來介紹核心方法`registerHandlerMethod(Object,Method,Mapping)ide

// mappingRegistry是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) {
			// Assert that the handler method is not a suspending one.
			if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
				Class<?>[] parameterTypes = method.getParameterTypes();
				if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
					throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
				}
			}
            // 上讀寫鎖
			this.readWriteLock.writeLock().lock();
			try {
                // 建立根據handle與method建立HandlerMethod,也就是創建起了Controller與對應執行方法的關係
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                //校驗
				validateMethodMapping(handlerMethod, mapping);
				// 將mapping與handlerMethod存入map
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
				    將url與mapping存入urlLookup中
					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<>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}
複製代碼
相關文章
相關標籤/搜索