initHandlerMappings
protected void initStrategies(ApplicationContext context) {
// 初始化文件上传解析器
initMultipartResolver(context);
// 本地化解析
initLocaleResolver(context);
// 主题解析器
initThemeResolver(context);
// 处理器映射器 保存Url映射关系
initHandlerMappings(context);
// 处理器适配器
initHandlerAdapters(context);
// 异常解析器
initHandlerExceptionResolvers(context);
// 视图提取器,从request中获取viemName
initRequestToViewNameTranslator(context);
// 视图解析器
initViewResolvers(context);
// 参数解析器
initFlashMapManager(context);
}
我们主要来看this.initHandlerMappings(context);这个方法,上面的方法中其实只是从spring的容器中获取对应的实例bean,不是我们的重点。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//detectAllHandlerMappings默认为true
if (this.detectAllHandlerMappings) {
//找出Spring容器中所有类型为HandlerMapping的实例,再前面没有做任何类型的管理,所以spring容器中根本就没有
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 = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException var3) {
}
}
if (this.handlerMappings == null) {
this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No HandlerMappings declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");
}
}
}
首先从spring的容器中获取所有的类型为HandlerMapping类型的对象,我们此前没有设置过,所以是为空的。那接下来就会进入到getDefaultStrategies方法了。在进入到这个方法之前呢,还有一点需要说明。
static {
try {
ClassPathResource resource = new ClassPathResource("DispatcherServlet.properties", DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException var1) {
throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + var1.getMessage());
}
}
上面这段代码,是DispatcherServlet中的一段静态代码块,我们知道,静态代码块会随着类的加载而执行,只会执行一次。也就是说,在调用其他方法之前,这段代码已经被执行。这段代码的意思其实就是,将DispatcherServlet类路径下的DispatcherServlet.properties加载,然后将其放入到defaultStrategies这样的一个属性中。了解这个之后我们接下来再去看getDefaultStrategies方法。
DispatcherServlet.properties
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
//context类型是XmlApplication,strategyInterface是HandlerMapping
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value == null) {
return new LinkedList();
} else {
//根据逗号将value切割为String数组
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList(classNames.length);
String[] var7 = classNames;
int var8 = classNames.length;
for(int var9 = 0; var9 < var8; ++var9) {
String className = var7[var9];
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = this.createDefaultStrategy(context, clazz);
strategies.add(strategy);
} catch (ClassNotFoundException var13) {
throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var13);
} catch (LinkageError var14) {
throw new BeanInitializationException("Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var14);
}
}
return strategies;
}
}
那么传过来的两个参数是什么呢,context已经在上篇中分析过,是XmlApplicationContext类型,在initHandlerMappings这个方法调用getDefaultStrategies这个方法的时候,可以看出穿的类型是HandlerMapping的类型。
首先利用strategyInterface得到他的全类名,然后再用这个全类名当key去defaultStrategies中获取value。上面提到,DispatcherServlet.properties已经全部被设置到defaultStrategies当中,那么根据HandlerMapping这个全类名在DispatcherServlet.properties能获取到什么呢。通过对DispatcherServlet.properties的查找我们知道,
- org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
- org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
- org.springframework.web.servlet.function.support.RouterFunctionMapping
是这三个值。然后根据逗号将value切割成为一个String数组。然后去遍历这个数组,分别调用createDefaultStrategy方法。
但其实这个方法就是createBean,去创建bean实例,但真的只有这么简单吗?其实并不是。BeanNameUrlHandlerMappingr这个类,没有实现什么扩展性的接口,我们不用管他,RouterFunctionMapping这个类,是关于路由的,不是我们的重点,我们主要来看RequestMappingHandlerMapping这个类。
RequestMappingHandlerMapping是继承于RequestMappingInfoHandlerMapping,而RequestMappingInfoHandlerMapping这个类实现了一个接口,InitializingBean接口,实现这个接口的类,会在bean实例化完成,填充完属性之后调用afterPropertiesSet方法。
public void afterPropertiesSet() {
this.config = new BuilderConfiguration();
this.config.setUrlPathHelper(this.getUrlPathHelper());
this.config.setPathMatcher(this.getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(this.getContentNegotiationManager());
super.afterPropertiesSet();
}
在给config属性设置了一些属性之后,调用父类的afterPropertiesSet方法。
public void afterPropertiesSet() {
this.initHandlerMethods();
}
protected void initHandlerMethods() {
//spring容器中拿到所有的beanName
String[] var1 = this.getCandidateBeanNames();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
String beanName = var1[var3];
if (!beanName.startsWith("scopedTarget.")) {
this.processCandidateBean(beanName);
}
}
this.handlerMethodsInitialized(this.getHandlerMethods());
}
先获取spring容器中所有的beanName,然后循环,如果beanName不是以scopedTarget.开头,就会调用processCandidateBean方法。
当我们拿到HandlerMapping的beanType之后,会进行 if 语句中的判断,值得注意的是this.isHandler(beanType)这个方法。
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class);
}
这个方法会查找你传入的beanType类型中有没有Controller注解或者RequestMapping注解。也就是说,spring容器中所有的实例都会一 一的进行筛选,只有bean类型上有Controller注解或者RequestMapping注解的才会继续执行detectHandlerMethods方法。
protected void detectHandlerMethods(Object handler) {
//获取当前beanName获取BeanClass对应类对象的类型
Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
if (handlerType != null) {
//获取到真实的类类型,因为有可能拿到的类的类型是一个实例的代理对象,需要得到代理对象对应类的真实类
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
try {
return this.getMappingForMethod(method, userType);
} catch (Throwable var4) {
throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
}
});
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
this.registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
如果穿的参数是string类型,就根据这个参数到spring容器中获取对应类型,否则直接拿到该类型。然后获取到真实的类型,再去进行操作。那么下面这个lambda表达式就比较重要了。我们看看getMappingForMethod这个方法。
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = this.createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = this.createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
String prefix = this.getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(new String[]{prefix}).build().combine(info);
}
}
return info;
}
这个RequestMappingInfo这个类就比较重要了,这个类实际上对应的就是@RequestMapping这个注解。他封装了这个注解里面的内容,在下面的源码中我们会看到。那么进入到这个方法,首先就调用createRequestMappingInfo方法传入method去创建一个RequestMappingInfo 对象。如果拿到的info不为空的话,再调用这个方法再去创建一个RequestMappingInfo对象,那么为什么要创建两次,值得注意的是,虽然调用了两次方法,但是传入的参数却不一样,我们刚刚说RequestMappingInfo对象对应@RequestMapping注解,这个注解是可以作用于方法或者类上面得到,所以要想拿到所有的注解信息,就需要调用两次。我们现在来看看这个创建方法。
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = (RequestMapping)AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = element instanceof Class ? this.getCustomTypeCondition((Class)element) : this.getCustomMethodCondition((Method)element);
return requestMapping != null ? this.createRequestMappingInfo(requestMapping, condition) : null;
}
首先通过传来的参数,去拿到这个方法或者是类上面的RequestMapping注解对象。然后如果有自定义的策略,去调用。我们这里没有,condition 为null,然后createRequestMappingInfo。
protected RequestMappingInfo createRequestMappingInfo(RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
Builder builder = RequestMappingInfo.paths(this.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);
}
return builder.options(this.config).build();
}
这里的也比较明了,这个方法的作用就是去创建一个对应的RequestMappingInfo对象,在创建对象之前,把requestMapping注解的所有参数给builder包装起来,然后调用build方法真正的去创建RequestMappingInfo对象。
public RequestMappingInfo build() {
ContentNegotiationManager manager = this.options.getContentNegotiationManager();
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(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);
}
其实这个build方法就是包装参数,new对象。刚刚builder包装了RequestMapping注解中的所有内容,这里在new对象的时候,再把他的所有封装的属性再包装成对象。
-
methods ------>new RequestMethodsRequestCondition(this.methods)---->RequestMethodsRequestCondition对象
-
params ----->new ParamsRequestCondition(this.params)---->ParamsRequestCondition对象
-
headers ---->new HeadersRequestCondition(this.headers)---->HeadersRequestCondition对象
-
consumes—>new ConsumesRequestCondition(this.consumes, this.headers)—>ConsumesRequestCondition对象
-
produces------> new ProducesRequestCondition(this.produces, this.headers, manager)---->ProducesRequestCondition对象
封装之后,返回创建好的RequestMappingInfo对象。直接返回到getMappingForMethod这个方法中。到这,方法和方法所在类上面的RequestMapping注解解析完成,并把内容封装的两个RequestMappingInfo对象中。那其实这两个对象分开是不行的,因为,他们对应的RequestMapping中的属性,最后所需要的其实是两个注解进行合并的,比如他的value值,也就是路径,他是需要进行合并的,所以,在combine方法中,进行了合并,然后再获取路径前缀,如果有的话在进行一次合并,返回合并好的RequestMappingInfo对象。返回到detectHandlerMethods这个方法中。
这时,这一步执行完成,我们返回一个Map集合,此时的泛型 T 就是RequestMappingInfo类型。
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
try {
return this.getMappingForMethod(method, userType);
} catch (Throwable var4) {
throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
}
});
这个Map集合method,他的key是方法method,他的value值就是方法对应的RequestMapping注解所封装好的RequestMappingInfo对象,注意,此时这个RequestMappingInfo对象是已经合并好的。然后进行了一些日志的操作之后,执行到这个地方。
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
this.registerHandlerMethod(handler, invocableMethod, mapping);
});
循环刚刚获得到的Map集合。两个方法。
- selectInvocableMethod方法
public static Method selectInvocableMethod(Method method, @Nullable Class<?> targetType) {
if (targetType == null) {
return method;
} else {
Method methodToUse = MethodIntrospector.selectInvocableMethod(method, targetType);
//不能是私有的,不能是静态的,不能是spring代理的
if (Modifier.isPrivate(methodToUse.getModifiers()) && !Modifier.isStatic(methodToUse.getModifiers()) && SpringProxy.class.isAssignableFrom(targetType)) {
throw new IllegalStateException(String.format("Need to invoke method '%s' found on proxy for target class '%s' but cannot be delegated to target bean. Switch its visibility to package or protected.", method.getName(), method.getDeclaringClass().getSimpleName()));
} else {
return methodToUse;
}
}
}
这个方法其实就是去判断你这个方法,是不是私有的,是不是静态的,是不是spring代理的,如果是,就抛出异常。这里我们也就知道,我们所执行的方法,不能是私有的,静态的或者是spring代理的。筛选完可执行的方法之后,再去执行registerHandlerMethod方法。
- registerHandlerMethod方法
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
//mapping是RequestMappingInfo对象,method是处理方法,handler是方法所对应Controller类的类型
public void register(T mapping, Object handler, Method method) {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && AbstractHandlerMethodMapping.KotlinDelegate.isSuspend(method)) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
} else {
this.readWriteLock.writeLock().lock();
try {
//根据处理类和处理方法创建HandlerMethod类
//把方法封装成了HandlerMethod对象
//把方法中的参数列表封装成了MethodParameter对象
HandlerMethod handlerMethod = AbstractHandlerMethodMapping.this.createHandlerMethod(handler, method);
this.validateMethodMapping(handlerMethod, mapping);
//mappingLookup--------->(RequestMappingInfo,HandlerMethod)
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = this.getDirectUrls(mapping);
Iterator var6 = directUrls.iterator();
while(var6.hasNext()) {
String url = (String)var6.next();
//urlLookup---->(url,RequestMappingInfo)
this.urlLookup.add(url, mapping);
}
String name = null;
if (AbstractHandlerMethodMapping.this.getNamingStrategy() != null) {
name = AbstractHandlerMethodMapping.this.getNamingStrategy().getName(handlerMethod, mapping);
this.addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = AbstractHandlerMethodMapping.this.initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration(mapping, handlerMethod, directUrls, name));
} finally {
this.readWriteLock.writeLock().unlock();
}
}
}
我们直接来看register这个方法,首先就用传过来的处理类和处理方法去创建一个HandlerMethod对象。
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
return handler instanceof String ? new HandlerMethod((String)handler, this.obtainApplicationContext().getAutowireCapableBeanFactory(), method) : new HandlerMethod(handler, method);
}
public HandlerMethod(Object bean, Method method) {
Assert.notNull(bean, "Bean is required");
Assert.notNull(method, "Method is required");
this.bean = bean;
this.beanFactory = null;
this.beanType = ClassUtils.getUserClass(bean);
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = this.initMethodParameters();
this.evaluateResponseStatus();
this.description = initDescription(this.beanType, this.method);
}
这个类中也有很多的属性,这个方法其实就是去new一个HandlerMethod对象,在他的构造方法中,对这些属性一 一赋值,也就是说,一个方法对应一个HandlerMethod对象。值得注意的是,这里的parameters类型为MethodParameter[ ],就是说HandlerMethod把方法参数列表封装成了一个MethodParameter数组。然后在mappingLookup这个集合中,存储mapping和handlerMethod,也就是我们的RequestMappingInfo和其对应的处理方法。接着它遍历从mapping(RequestMappingInfo)拿到的Urls,在把他们放到urlLookup集合中,url为key,mapping为value。此时我们有两个集合,mappingLookup和urlLookup。
- mappingLookup--------->(RequestMappingInfo,handlerMethod)
- urlLookup----------------->(url, RequestMappingInfo)
也就是说,当我们拿到http请求时候,获取url,根据urlLookup这个集合能得到对应的@RequestMapping注解对应的RequestMappingInfo对象,然后从mappingLookup中根据对应的RequestMappingInfo对象去查找对应的处理方法,这样就能进行处理了。处理完之后,RequestMappingHandlerMapping就真正的创建完成了。
总结
- 我们从initStrategies方法进入到this.initHandlerMappings(context);方法中
- 在容器中获取不到HandlerMapping实例的时候就会去创建
- 因为在DispatcherServlet加载的时候会把它类路径下的DispatcherServlet.properties一起加载放入到属性中,所以直接通过HandlerMapping的全类名可以获取到对应的HandlerMapping类型。
- 获取到类型之后就去创建,但由于他是InitializingBean的子类,所以会去调用afterPropertiesSet方法。
- 通过一系列的调用之后,会拿到spring容器中的所有beanName去一一筛选类上标注了Controller或者RequestMapping的注解的类。
- 筛选完成之后,会解析类和方法上面的RequestMapping注解,并把他们封装成RequestMappingInfo对象,然后进行合并,合并完成之后,放到一个map集合中
- 遍历刚刚获得的map集合,将RequestMappingInfo对象和对应处理方法放入到mappingLookup,将RequestMappingInfo中的url和对应RequestMappingInfo对象放入到urlLookup中。此时HandlerMapping真正的创建完成。