天天看点

@RequestMapping注解和Controller方法建立映射的流程

当你的才华还撑不起你的野心的时候,唯有静下心来学习。
           

       有没有想过,为什么在controller类的方法上,添加一个

@RequestMapping("/toIndex")

注解的时候,从浏览器的输入

localhost:8080/toIndex

请求就可以到达指定的controller方法呢?

大致实现原理

       大致实现原理,可以简单来说,稍后会进行源码证明。原理就是在spring在解析到这个

@RequestMapping

的时候,可以拿到它里面的值,以及标记了这个注解的方法。然后,把他们存在一个

map集合

中,

key

@RequestMapping

value

(对应上面的

toIndex

),

value

为标记了这个注解的方法。当浏览器发送请求的时候,可以从

request

中获取到请求路径(如

localhost:8080/toIndex

,可以获取到

toIndex

),然后拿到请求路径后从之前的

map集合

中去查找,如查找到就反射调用他的

method

方法。

源码实现

SpringMVC

在和

Tomcat

整合的时候,会在

Tomcat

启动的时加载

DispatcherServlet

这个类。在加载

DispatcherServlet

这个类的时候,他有静态代码块,会在加载的时候执行:

static {
		try {
		//  加载DispatcherServlet.properties属性文件, 供之后使用
			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
		}
	}
           

DispatcherServlet.properties

的属性文件中有这一段内容,其中

RequestMappingHandlerMapping

这个类就是完成

@RequestMapping

到方法的映射,其他内容自己查看:

org.springframework.web.servlet.HandlerMapping=
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
           

DispatcherServlet

本身就是一个

Servlet

容器。所以启动的过程中会调用他的

init()

方法进行

Springmvc/Web环境

的初始化,init方法由他的父类HttpBeanServlet进行实现,具体细节可以看源码实现,相对简单。

@RequestMapping注解和Controller方法建立映射的流程

通过以上init()方法的初始化流程,最终会到initStrategies(context)这个方法,这个方法中有调用initHandlerMappings(context)方法,完成HandlerMapping的初始化:

protected void initStrategies(ApplicationContext context) {
		...
		initHandlerMappings(context);// 初始化HandlerMapping
		...
}
           

       额外说明,其实在加载

DispatcherServlet

这个类的时候,

Spring的环境

已经初始化完成了,通过

DispatherServlet

的构造方法传递一个

WebApplicationContext

的实现类,然后

DispatcherServlet

就持有了

Spring上下文环境

(包括工厂,以及注册的bean)。

initHandlerMappings(context)方法的实现:

private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
		// detectAllHandlerMappings默认为true
		if (this.detectAllHandlerMappings) { 
		//	这个方法底层实现,是从beanFactory容器中获取HandlerMapping类型的bean
		//  第一次获取为空,还未注册
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
			}
		}
		// 获取为空,
		if (this.handlerMappings == null) {
			// 核心代码,从之前静态代码块已经加载好的DispatcherServlet.properties的
			// defaultStrategies对象从获取内容,反射创建实例注册到容器中
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}
           
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		// key = org.springframework.web.servlet.HandlerMapping
		String key = strategyInterface.getName();
		// 
		/**
		* 获取到属性文件的key对应的value
		* 	org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
		*   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
		*/
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			// 根据逗号分隔
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			List<T> strategies = new ArrayList<>(classNames.length);
			for (String className : classNames) {
				try {
				    // 反射创建实例
					Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
					// 底层是调用createBean方法,注册到beanFactory容器中,并实例化
					Object strategy = createDefaultStrategy(context, clazz);
					strategies.add((T) strategy);
				}
				catch (ClassNotFoundException ex) {
					throw new BeanInitializationException(
							"Could not find DispatcherServlet's default strategy class [" + className +
							"] for interface [" + key + "]", ex);
				}
				catch (LinkageError err) {
					throw new BeanInitializationException(
							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
							className + "] for interface [" + key + "]", err);
				}
			}
			return strategies;
		}
		else {
			return new LinkedList<>();
		}
	}
           

getDefaultStrategies()

这个方法执行结束后,很明显

RequestMappingHandlerMapping

这个类已经注入到

beanFactory

容器中了,交给了

spring

来管理。又由于

RequestMappingHandlerMapping

这个类的父类实现了

InitializingBean

这个接口,而

RequestMappingHandlerMapping

这个类又重写了

InitializingBean

接口的

afterPropertiesSet()

方法,所有在spring在创建他的实例的时候,会回调他的

afterPropertiesSet()

@Override
	public void afterPropertiesSet() {
		// 调用父类
		super.afterPropertiesSet();
	}
           
@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}

	protected void initHandlerMethods() {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for request mappings in application context: " + getApplicationContext());
		}
		// 从spring容器中获取Object类型的对象的名称,也就是获取容器中的所有beanName
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
				obtainApplicationContext().getBeanNamesForType(Object.class));

		for (String beanName : beanNames) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				Class<?> beanType = null;
				try {
					// 根据beanName获取对应的实例信息
					beanType = obtainApplicationContext().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方法就是检测这个实例的类上是否有
				// @Contoller注解或@RequestMapping注解
				if (beanType != null && isHandler(beanType)) {
					// 发现handler方法
					detectHandlerMethods(beanName);
				}
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
           
@Override
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}
           

detectHandlerMethods(beanName);

就是用来处理我们的类里面加了

@ReuqestMapping

注解的方法:

protected void detectHandlerMethods(final Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			final Class<?> userType = ClassUtils.getUserClass(handlerType);
			// 这个方法是发现 所有加了@RequestMapping注解的方法,放入map集合中
			// key为方法,T为我们的url路径字符串,可以自行调试
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) 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);
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				// 把查询处理的方法放入到map集合中,供请求时使用
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}
           
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 = createHandlerMethod(handler, method);
				assertUniqueMethodMapping(handlerMethod, mapping);

				if (logger.isInfoEnabled()) {
					logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
				}
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					// TODO 在这一步放入到map集合中(spring自己定义的集合,和map结构差不多)
					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();
			}
		}
           

       到这里整个

@RequestMapping

到方法的映射,并存入map集合中的初始化工作已经结束了,可以认为在浏览器输入请求的时候,直接获取url,然后到

urlLookup

的map集合中去获取方法就可以了,然后执行method.invoke方法完成方法的调用。这样就完成了

@RequestMapping

到方法的映射。