SpringMVC之解析Controller和HandlerMethod
回顾本例springmvc-config.xml的配置:
<context:component-scan base-package="com.spring"/>
<mvc:annotation-driven />
component-scan:扫描指定包下被@Component,@Service,@Controller,@Repository,@Configuration注解所标注的class。实际上,都是通过@Component来完成的。
annotation-driven
:注册SpringMVC的web组件,并完成HandlerMethod的解析。
名称空间处理器:NamespaceHandler
context:component-scan标签由ContextNamespaceHandler处理器处理。
mvc:annotation-driven标签由MvcNamespaceHandler处理器处理。
ContextNamespaceHandler处理器源码:
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
}
由此可见,component-scan是由具体的ComponentScanBeanDefinitionParser
解析器解析,它实现了BeanDefinitionParser接口,完成扫描指定包路径下的class文件,并注册所有满足条件的BeanDefinition。
ComponentScanBeanDefinitionParser解析器
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
// 完成扫描并注册BeanDefinitions
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
ClassPathScanningCandidateComponentProvider是ClassPathBeanDefinitionScanner父类,我们重点关注findCandidateComponents(String basePackage)方法。
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
// 首先将我们配置的com.spring包修改为classpath*:com/spring/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
// 重点关注该方法
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
//...
candidates.add(sbd);
}
//...
return candidates;
}
isCandidateComponent(metadataReader)方法,完成了@Component的筛选工作。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return isConditionMatch(metadataReader);
}
}
return false;
}
includeFilters指定了筛选条件为:@Component注解的类。
由于我们并没有在springmvc-config.xml中给component-scan配置includeFilters标签过滤属性,所以includeFilters取的是默认值@Component注解。
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
//...
}
既然默认读取@Component标注的类,那么,那些仅仅标注了@Service,@Controller,@Repository,@Configuration注解的类是怎么被包含进来的呢?原理在于,直接或间接使用@Component标注的类,都会被包含进来。@Service,@Controller,@Repository,@Configuration都是被@Component所标注的。
AnnotationTypeFilter.matchSelf()源码:
@Override
protected boolean matchSelf(MetadataReader metadataReader) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
return metadata.hasAnnotation(this.annotationType.getName()) ||
(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}
hasAnnotation:直接被@Component标注。
hasMetaAnnotation:间接被@Component标注,如@Service,@Controller,@Repository,@Configuration。
AnnotationDrivenBeanDefinitionParser解析器
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
@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("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}
}
AnnotationDrivenBeanDefinitionParser.parse()方法很长:(这里就不贴了)
mvc:annotation-driven解析器,向Spring IOC容器注册了很多SpringMVC的web组件,我们重点关注下面几个组件,其余读者可自行了解:
1、RequestMappingHandlerMapping:负责解析HandlerMethod,并根据request请求返回HandlerExecutionChain,HandlerExecutionChain是HandlerMethod和方法拦截器的合体。
2、RequestMappingHandlerAdapter:负责处理request请求,并返回ModelAndView对象。
3、DefaultFormattingConversionService:类型转换服务类,如String to Date,Date to String。
4、HttpMessageConverter:消息转换器,如根据HTTP的MIME类型,JavaBean to Json,JavaBean to XML。
5、ConfigurableWebBindingInitializer:WebDataBinder的初始化器。
...
其余可自行查看。
RequestMappingHandlerMapping实现了InitializingBean接口,那么,在Spring IOC容器创建该bean对象时,会调用它的afterPropertiesSet()方法,完成HandlerMethod的解析工作。
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
//...
// 从IOC容器中获得所有的beans
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {
//...
}
// isHandler()检测是否有@Controller注解
if (beanType != null && isHandler(beanType)) {
// 从Controllor中解析出被@RequestMapping标注的方法
detectHandlerMethods(beanName);
}
}
}
// 空方法
handlerMethodsInitialized(getHandlerMethods());
}
从源码中,的确看到了读取的是@RequestMapping注解标注的方法或类。
下面我们看看RequestMappingInfo的类结构:
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
private final ParamsRequestCondition paramsCondition;
private final HeadersRequestCondition headersCondition;
private final ConsumesRequestCondition consumesCondition;
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;
}
我画一个图,大家就明白了。
紧接着,将RequestMappingInfo封装成HandlerMethod,注册到MappingRegistry中。
AbstractHandlerMethodMapping.MappingRegistry.register():
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
// T mapping即RequestMappingInfo对象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
//...
this.mappingLookup.put(mapping, handlerMethod);
// 不带通配符*和?号的,直接加入urlLookup
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
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<T>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
class MappingRegistry {
//T为RequestMappingInfo对象
private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();
private final Map<String, List<HandlerMethod>> nameLookup =
new ConcurrentHashMap<String, List<HandlerMethod>>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup =
new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();
}
MappingRegistry是RequestMappingHandlerMapping的属性,MappingRegistry对RequestMappingInfo进行了多种缓存映射,方便查找。
至此,context:component-scan、mvc:annotation-driven、RequestMappingInfo以及HandlerMethod的解析及工作原理,都已经基本明确了。
context:component-scan:主要完成bean的解析工作。
mvc:annotation-driven:主要完成从controllor bean中解析出RequestMappingInfo,封装为HandlerMethod对象。