分析版本Spring5.0.9.release,Springboot2.0.3.release
spring-webmvc的META-INFO/spring.handles文件中,有MvcNamespaceHandler,这是用来解析标签的,来看下MvcNamespaceHandler的init(),如下List-1,我们暂时只关注AnnotationDrivenBeanDefinitionParser。
List-1
@Override
public void init() {
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}
AnnotationDrivenBeanDefinitionParser的parse方法里面有很多代码,但是我们只关注俩个,如下List-2:
- 用RootBeanDefinition将RequestMappingHandlerMapping注册到spring容器中。
- MvcNamespaceUtils.registerDefaultComponents中,将BeanNameUrlHandlerMapping注册到spring容器中、将HttpRequestHandlerAdapter注册到spring容器中、将SimpleControllerHandlerAdapter注册到spring容器中、将HandlerMappingIntrospector注册到容器中。
List-2
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
...
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
...
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(context, source);
return null;
}
RequestMappingHandlerMapping的继承图如下图1所示:
图1
图1中,AbstractHandlerMethodMapping实现了接口InitializingBean,实现了afterPropertiesSet(),如果了解SpringIOC,应该知道这个方法意味着什么,Spring在创建Bean的时候会调用这个方法。
List-3
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
由List-3知道afterPropertiesSet调用了initHandlerMethods(),如下List-4,首先从ApplicationContext或者所有的beanName,之后循环它们。isHandler方法由子类RequestMappingHandlerMapping实现,判断类上是否有Controller或者RequestMapping注解,所以即使不加上@Controller,只是用@RequestMapping注解也可以。List-4中的handlerMethodsInitialized是空方法,不用管。
List-4
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
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 {
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);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
来看List-4中的方法detectHandlerMethods,传入的是String类型的bean name,如下List-5,首先获取bean name对应的Class,getMappingForMethod在RequestMappingHandlerMapping中实现,如List-6所示,判断方法上是否有RequestMapping注解,如果有,则获取参数、path、header等信息,用builder模式构造出RequestMappingInfo。MethodIntrospector.selectMethods会遍历类上的方法,所以整体上对类的所有方法进行是否有RequestMapping的检查。需要注意的是,类似GetMapping等是组合注解,它们还是基于RequestMapping,所以方法上有GetMapping/PostMapping等都会会扫描并创建出对应的RequestMappingInfo。
List-5
protected void detectHandlerMethods(Object handler) {
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.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
List-6
@Override
@Nullable
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);
}
}
return info;
}
@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);
}
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();
}
List-5中的registerHandlerMethod,会将得到的Mapper信息注册到mappingRegistry到,这个mappingRegistry后面在DispatcherServlet中使用到。注意传入到registerHandlerMethod方法的handler是String类型的bean name。
Springboot中,RequestMappingHandlerMapping是通过@Configuration方式注入的,如下List-7
List-7
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
...