当你的才华还撑不起你的野心的时候,唯有静下心来学习。
有没有想过,为什么在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进行实现,具体细节可以看源码实现,相对简单。
通过以上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
到方法的映射。