springmvc源码笔记-RequestMappingHandlerMapping

233 阅读2分钟

下图是springmvc的执行流程

5220087-3c0f59d3c39a12dd

图片来源:www.jianshu.com/p/8a20c547e…


DispatcherServlet根据url定位到Controller和方法,依赖的是HandlerMapping接口的各个实现类,其中,RequestMappingHandlerMapping是专门用来处理注解方式的Controller的

下面,我们分RequestMappingHandlerMapping的加载以及RequestMappingHandlerMapping如何根据url定位Controller两部分来讲

RequestMappingHandlerMapping加载过程

  1. RMHP在系统启动时会被注册成bean,见《springmvc源码笔记-HandlerMapping注入》

  2. 因为RMHP实现了InitializingBean接口,在bean加载完成后会自动调用afterPropertiesSet方法,在此方法中调用了AbstractHandlerMethodMapping#initHandlerMethods()来实现初始化

    protected void initHandlerMethods() {
    	// 遍历所有的bean
    	for (String beanName : getCandidateBeanNames()) {
    		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
    			processCandidateBean(beanName);
    		}
    	}
    	handlerMethodsInitialized(getHandlerMethods());
    }
    
    protected void processCandidateBean(String beanName) {
    	Class<?> beanType = null;
    	try {
    		beanType = obtainApplicationContext().getType(beanName);
    	}
    	......
    	// isHandler判断类是否有Controller或者RequestMapping注解
    	if (beanType != null && isHandler(beanType)) {
    		detectHandlerMethods(beanName);
    	}
    }
    
    protected void detectHandlerMethods(Object handler) {
    	......
    	if (handlerType != null) {
    		Class<?> userType = ClassUtils.getUserClass(handlerType);
    		// 遍历类的所有方法
    		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
    				(MethodIntrospector.MetadataLookup<T>) method -> {
    					try {
    						// 获取方法上的映射(会读取方法上的RequestMapping注解获取url)
    						return getMappingForMethod(method, userType);
    					}
    					catch (Throwable ex) {
    						throw new IllegalStateException("Invalid mapping on handler class [" +
    								userType.getName() + "]: " + method, ex);
    					}
    				});
    		......
    		methods.forEach((method, mapping) -> {
    			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
    			// 将映射关系放入AbstractHandlerMethodMapping.mappingRegistry中
    			registerHandlerMethod(handler, invocableMethod, mapping);
    		});
    	}
    }
    

RequestMappingHandlerMapping解析过程

  1. DispatcherServlet继承HttpServlet,所以执行链是FrameworkServlet#doGet→DispatcherServlet#doService→DispatcherServlet#doDispatch→DispatcherServlet#getHandler

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    	if (this.handlerMappings != null) {
    		// handlerMappings已在onRefresh方法中初始化
    		for (HandlerMapping mapping : this.handlerMappings) {
    			// 第一个mapping就是RequestMappingHandlerMapping
    			// 获取处理程序,也就是url对应的controller和method
    			HandlerExecutionChain handler = mapping.getHandler(request);
    			if (handler != null) {
    				return handler;
    			}
    		}
    	}
    	return null;
    }
    
    // AbstractHandlerMapping#getHandler->RequestMappingHandlerMapping#getHandlerInternal->AbstractHandlerMapping#lookupHandlerMethod
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    	// lookupPath就是请求路径
    	List<Match> matches = new ArrayList<>();
    	// 获取url路径匹配项
    	List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    	if (directPathMatches != null) {
    		// 添加映射匹配集合
    		addMatchingMappings(directPathMatches, matches, request);
    	}
    	if (matches.isEmpty()) {
    		addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    	}
    	if (!matches.isEmpty()) {
    		......
    		// 至此,根据url获取到controller和method
    		return bestMatch.getHandlerMethod();
    	} else {
    		return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
    	}
    }
    
    private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    	for (T mapping : mappings) {
    		T match = getMatchingMapping(mapping, request);
    		if (match != null) {
    			// this.mappingRegistry.getRegistrations().get(mapping)就是获取HandlerMethodMapping
    			// HandlerMethodMapping保存controller和method信息的类
    			matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
    		}
    	}
    }