携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
2. initHandlerMappings
初始化处理器映射器
从上面的流程,我们知道了初始化的大致流程,现在根据我所了解到的SpringMVC的流程,我们着重看下HandlerMapping,上面的方法中其实只是从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());
// We keep HandlerMappings in sorted order. 排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);// 进到这里
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
for (HandlerMapping mapping : this.handlerMappings) {
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}
首先从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
回到getDefaultStrategies方法里面
// context类型是XmlApplication
// strategyInterface是HandlerMapping
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
if (defaultStrategies == null) {
try {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
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());
}
}
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
// 根据逗号将value切割为String数组
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());
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 Collections.emptyList();
}
}
那么传过来的两个参数是什么呢,context已经在前面分析过,是XmlApplicationContext类型,在initHandlerMappings这个方法调用getDefaultStrategies这个方法的时候,可以看出穿的类型是HandlerMapping的类型。 首先利用strategyInterface得到他的全类名,然后再用这个全类名当 key 去defaultStrategies中获取value。上面提到,DispatcherServlet.properties已经全部被设置到defaultStrategies当中,那么根据HandlerMapping这个全类名在DispatcherServlet.properties能获取到什么呢。通过对DispatcherServlet.properties的查找我们知道,
org.springframework.web.servlet.handler.BeanNameUrlHandlerMappingorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMappingorg.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 RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
if (getPatternParser() != null) {
this.config.setPatternParser(getPatternParser());
Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
"Suffix pattern matching not supported with PathPatternParser.");
}
else {
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setPathMatcher(getPathMatcher());
}
super.afterPropertiesSet();
}
在给config属性设置了一些属性之后,调用父类的afterPropertiesSet方法。
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
->
protected void initHandlerMethods() {
// spring容器中拿到所有的beanName
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
先获取spring容器中所有的beanName,然后循环,如果beanName不是以scopedTarget.开头,就会调用processCandidateBean方法。
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) { // <--这里
detectHandlerMethods(beanName);
}
}
当我们拿到HandlerMapping的beanType之后,会进行 if 语句中的判断,值得注意的是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 ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// //获取到真实的类类型,因为有可能拿到的类的类型是一个实例的代理对象,需要得到代理对象对应类的真实类
Class<?> userType = ClassUtils.getUserClass(handlerType);
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.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
else if (mappingsLogger.isDebugEnabled()) {
mappingsLogger.debug(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
如果传的handler参数是string类型,就根据这个参数到spring容器中获取对应类型,否则直接拿到该类型。然后获取到真实的类型,再去进行操作。那么里面这个lambda表达式就比较重要了。我们看看getMappingForMethod这个方法。
/*
使用方法和类型级别的 @RequestMapping 注释来创建 RequestMappingInfo。
*/
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
这个RequestMappingInfo这个类就比较重要了,这个类实际上对应的就是@RequestMapping这个注解。他封装了这个注解里面的内容,在下面的源码中我们会看到。那么进入到这个方法,首先就调用createRequestMappingInfo方法传入method去创建一个RequestMappingInfo对象。如果拿到的info不为空的话,再调用这个方法再去创建一个RequestMappingInfo对象,那么为什么要创建两次,值得注意的是,虽然调用了两次方法,但是传入的参数却不一样,我们刚刚说RequestMappingInfo对象对应@RequestMapping注解,这个注解是可以作用于方法或者类上面得到,所以要想拿到所有的注解信息,就需要调用两次。我们现在来看看这个创建方法。
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
首先通过传来的参数,去拿到这个方法或者是类上面的RequestMapping注解对象。然后如果有自定义的策略,去调用。我们这里没有,condition 为null,然后createRequestMappingInfo。
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(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() {
PathPatternsRequestCondition pathPatterns = null;
PatternsRequestCondition patterns = null;
if (this.options.patternParser != null) {
pathPatterns = (ObjectUtils.isEmpty(this.paths) ?
EMPTY_PATH_PATTERNS :
new PathPatternsRequestCondition(this.options.patternParser, this.paths));
}
else {
patterns = (ObjectUtils.isEmpty(this.paths) ?
EMPTY_PATTERNS :
new PatternsRequestCondition(
this.paths, null, this.options.getPathMatcher(),
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
this.options.getFileExtensions()));
}
ContentNegotiationManager manager = this.options.getContentNegotiationManager();
return new RequestMappingInfo(
this.mappingName, pathPatterns, patterns,
ObjectUtils.isEmpty(this.methods) ?
EMPTY_REQUEST_METHODS : new RequestMethodsRequestCondition(this.methods),
ObjectUtils.isEmpty(this.params) ?
EMPTY_PARAMS : new ParamsRequestCondition(this.params),
ObjectUtils.isEmpty(this.headers) ?
EMPTY_HEADERS : new HeadersRequestCondition(this.headers),
ObjectUtils.isEmpty(this.consumes) && !this.hasContentType ?
EMPTY_CONSUMES : new ConsumesRequestCondition(this.consumes, this.headers),
ObjectUtils.isEmpty(this.produces) && !this.hasAccept ?
EMPTY_PRODUCES : new ProducesRequestCondition(this.produces, this.headers, manager),
this.customCondition != null ?
new RequestConditionHolder(this.customCondition) : EMPTY_CUSTOM,
this.options);
}
}
其实这个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这个方法中。
后面的,明天学!!