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


image

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注解的类。

image

由于我们并没有在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;
}

我画一个图,大家就明白了。

image

紧接着,将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对象。

posted @ 2021-08-16 14:57  hochan_100  阅读(342)  评论(0编辑  收藏  举报