Spring源碼從入門到放棄-Controller注冊


Spring源碼從入門到放棄-Controller注冊

{toc}

@contact:zhangxin@benmu-health.com
@update:2017-03-23 02:18:31
@spirng.version:4.3.7.RELEASE

本文主要介紹SpringMVC中如何注冊Controller

SpringMVC中Controller由@Controller和@RequestMapping注解定義,@Controller定義對象為一個Controller,@RequestMapping定義了請求url路徑,SpringMVC內部將Controller的方法抽象為多個org.springframework.web.method.HandlerMethod,將Method的@RequestMapping注解抽象成org.springframework.web.servlet.mvc.method.RequestMappingInfo,一個到來的http請求,經過DispatcherServlet轉發,通過RequestMappingInfo匹配路徑,找到對應的HandlerMethod處理請求。這里的HandlerMethod可以理解為Controller的一個方法。

1.RequestMappingHandlerMapping

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping類的初始化過程完成了對@Controller和@RequestMapping兩個注解的解析該類由spring容器初始化過程解析。解析<mvc:annotation-driven />標簽時會自動向spring容器注冊該類。並在DispatcherServlet初始化的時候,在initHandlerMappings()方法中會從Spring容器中將該HandlerMapping作為DispatcherServlet的成員,用以處理http請求。

繼承關系:

該類實現了HandlerMapping和InitializingBean兩個接口,初始化方法afterPropertiesSet()完成了對@Controller和@RequestMapping的解析和注冊。

2.afterPropertiesSet

Controller注冊是在初始化方法afterPropertiesSet中,首先拿到Spring容器中所有的Bean,對每一個Bean判斷是否為Controller

	@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)
	 */
	protected void initHandlerMethods() {
	//拿到所有bean的名字
		String[] beanNames = getApplicationContext().getBeanNamesForType(Object.class));

		for (String beanName : beanNames) {
				Class<?> beanType = null;
				//拿到Class
				beanType = getApplicationContext().getType(beanName);
				
				//如果改bean帶有@Controller和@RequestMapping注解
				if (beanType != null && isHandler(beanType)) {
					//注冊hanler mapping即Controller
					detectHandlerMethods(beanName);
				}
		}
	
	}

3.解析RequestMappingInfo

detectHandlerMethods完成對一個Controller的解析,將@RequestMapping方法解析成映射和可執行的HandlerMethod,映射抽象為RequestMappingInfo(即url pattern),將可執行的HandlerMethod和RequestMappingInfo一起注冊到MappingRegistry中,DispatcherServlet收到一個請求的時候會從MappingRegistry中取出與url匹配的handler method來執行。

protected void detectHandlerMethods(final Object handler) {
        Class<?> handlerType = (handler instanceof String ?
                getApplicationContext().getType((String) handler) : handler.getClass());
        //拿到用戶實際注冊的類,防止CGLIB代理
        final Class<?> userType = ClassUtils.getUserClass(handlerType);

        //選出該類打@RequestMapping的方法,並轉成Map<Method,RequestMappingInfo>
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                new MethodIntrospector.MetadataLookup<T>() {
                    @Override
                    public T inspect(Method method) {
                        //對每一個method,轉成RequestMappingInfo,如果不帶@RequestMapping注解則返回null
                        return getMappingForMethod(method, userType);
                    }
                });

        for (Map.Entry<Method, T> entry : methods.entrySet()) {
            //包裝成一個可執行方法
            Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
            //實際為RequestMappingInfo
            T mapping = entry.getValue();
            //將RequestMappingInfo和handler注冊到MappingRegistry
            //DispatcherServlet收到一個請求的時候會從MappingRegistry中取出與url匹配的handler來執行
            registerHandlerMethod(handler, invocableMethod, mapping);
        }
    }

getMappingForMethod()方法中完成了將帶有@RequestMapping注解的方法轉為RequestMappingInfo。
分別將Class和Method上的@RequestMapping拿到,用屬性生成RequestMappingInfo。然后將兩個RequestMappingInfo合並成一個。e.g. Class上的注解為path=/test,Method上的注解為path=/hello,method=POST,合並之后就是path=/test/hello,method=POST,並且為每一個RequestMappingInfo生成一個PatternsRequestCondition,用來完成DispatchServlet分發請求時url匹配。

@Override
	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		//解析method的@RequestMapping
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			//解析Class的@RequestMapping
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				//將兩個@RequestMapping合並
				info = typeInfo.combine(info);
			}
		}
		return info;
	}
	
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
		//拿到注解
		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
		return (requestMapping != null ? createRequestMappingInfo(requestMapping, null) : null);
	}

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

		//用@RequestMapping的屬性生成RequestMappingInfo
		return RequestMappingInfo
				.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))//e.g. /test
				.methods(requestMapping.method())//e.g. POST
				.params(requestMapping.params())
				.headers(requestMapping.headers())
				.consumes(requestMapping.consumes())
				.produces(requestMapping.produces())
				.mappingName(requestMapping.name())
				.customCondition(customCondition)
				.options(this.config)
				.build();
	}

@Override
		public RequestMappingInfo build() {
			ContentNegotiationManager manager = this.options.getContentNegotiationManager();

			//生成路徑匹配類,DispatcherServlet中分發url請求時調用
			PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
					this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
					this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
					this.options.getFileExtensions());

			return new RequestMappingInfo(this.mappingName, patternsCondition,
					new RequestMethodsRequestCondition(methods),
					new ParamsRequestCondition(this.params),
					new HeadersRequestCondition(this.headers),
					new ConsumesRequestCondition(this.consumes, this.headers),
					new ProducesRequestCondition(this.produces, this.headers, manager),
					this.customCondition);
		}

4.RequestMappingInfo與handler注冊

handler最終會被封裝成HandlerMethod
RequestMappingInfo與HandlerMethod都注冊到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry中,MappingRegistry有兩個屬性,Map<RequestMappingInfo, HandlerMethod>Map<url, HandlerMethod>,維護了路徑和HandlerMethod的關系。注冊@Controller即生成這兩個Map。

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 {
                //通過Bean和Method,抽象成可執行的HandlerMethod,即Controller的帶有@RequestMapping注解的Method
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);

                //注冊Map<RequestMappingInfo, HandlerMethod>
                this.mappingLookup.put(mapping, handlerMethod);

                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    //注冊Map<url, HandlerMethod>
                    this.urlLookup.add(url, mapping);
                }

                //http跨域配置
                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    this.corsLookup.put(handlerMethod, corsConfig);
                }

                //注冊Map<RequestMappingInfo, MappingRegistration<RequestMappingInfo>>
                this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            } finally {
                this.readWriteLock.writeLock().unlock();
            }
        }

5.DispatcherServlet與MappingRegistry

這里順帶提一下DispatcherServlet如何找到處理當前Http Request的HandlerMethod,最終http請求由匹配到的HandlerMethod來處理。

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<Match>();
        //從Map<url, HandlerMethod>中找
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            //從所有的RequestMappingInfo中找
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            //本質為Comparator<RequestMappingInfo>
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            Collections.sort(matches, comparator);
            //選出最匹配當前Request的RequestMappingInfo
            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 + "}");
                }
            }
            //處理url上的template variables, matrix variables
            handleMatch(bestMatch.mapping, lookupPath, request);
            
            //拿到handlerMethod
            return bestMatch.handlerMethod;
        } else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM