欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring5 源码阅读笔记(1.1.2)delegate.parseCustomElement(ele) 自定义标签的解析和注册

程序员文章站 2022-04-02 10:56:35
...

前面的文章 Spring5 源码阅读笔记(1.1)obtainFreshBeanFactory() 末尾介绍到了默认标签解析和自定义标签解析。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {

						//默认标签解析
						parseDefaultElement(ele, delegate);
					}
					else {

						//自定义标签解析
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

默认标签解析已经分析过了,见文章 Spring5 源码阅读笔记(1.1.1)parseDefaultElement(ele, delegate) 默认标签的解析和注册

本节会揭晓:
自定义标签解析流程和注册过程
重点:< context:component-scan > 的解析

跟 parseCustomElement :
类 BeanDefinitionParserDelegate
Spring5 源码阅读笔记(1.1.2)delegate.parseCustomElement(ele) 自定义标签的解析和注册
跟 parseCustomElement :

@Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

关于 NamespaceHandler 如果不了解,可以看这篇文章:从源码角度解读 xml 文件中的 xmlns、xsi、xsd

跟 parse :
类 NamespaceHandler
Spring5 源码阅读笔记(1.1.2)delegate.parseCustomElement(ele) 自定义标签的解析和注册

Spring5 源码阅读笔记(1.1.2)delegate.parseCustomElement(ele) 自定义标签的解析和注册
跟 parse :
类 BeanDefinitionParser
Spring5 源码阅读笔记(1.1.2)delegate.parseCustomElement(ele) 自定义标签的解析和注册
我们重点看 ComponentScanBeanDefinitionParser:
这个类对应< context:component-scan > 这个标签

/*
* 1、扫描路径。.class后缀的文件
* 2、要判断类上是否有注解 @Component
* 3、对于有注解的类,创建对应的 BeanDefinition
* 4、完成 BeanDefinition 注册
* */
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
	//获取basePackage属性
	String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
	basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
	//可以用逗号分开
	String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
			ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

	//创建注解扫描器 重要程度 2 就是这里面支持了@Component
	// Actually scan for bean definitions and register them.
	ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
	//扫描并把扫描的类封装成beanDefinition对象并注册  核心方法,重要程度 5
	Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
	registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

	return null;
}

1.1.2.1 configureScanner

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
	//使用默认的过滤器
	boolean useDefaultFilters = true;
	
	if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
		useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
	}

	//创建注解的扫描器
	// Delegate bean definition registration to scanner class.
	ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
	scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
	scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());

	if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
		scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
	}

	try {
		parseBeanNameGenerator(element, scanner);
	}
	catch (Exception ex) {
		parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
	}

	try {
		parseScope(element, scanner);
	}
	catch (Exception ex) {
		parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
	}

	parseTypeFilters(element, scanner, parserContext);

	return scanner;
}

跟 createScanner:

protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
	return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
			readerContext.getEnvironment(), readerContext.getResourceLoader());
}

跟 ClassPathBeanDefinitionScanner :

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	this.registry = registry;

	//使用默认的过滤器
	if (useDefaultFilters) {
		//@Component
		registerDefaultFilters();
	}
	setEnvironment(environment);
	setResourceLoader(resourceLoader);
}

跟 registerDefaultFilters :
类 ClassPathScanningCandidateComponentProvider,上面那个类的父类

protected void registerDefaultFilters() {
	//注意这里,说明了要扫描的注解是 Component。具体的说是 includeFilters 里包含了 Component
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
		logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
	}
	try {
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
		logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}

1.1.2.2 doScan

类 ClassPathBeanDefinitionScanner

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
	for (String basePackage : basePackages) {
		//扫描到有注解的类并封装成BeanDefinition对象
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				//支持了@Lazy @DependOn注解
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				//这里不看
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);

				//BeanDefinition 注册
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

可以看到这里和默认标签的 parseBeanDefinition 有点类似,核心是两步:
一、封装 BeanDefinition 对象
二、对 BeanDefinition 进行注册

第二步和前面默认标签的注册过程是一样的,就不跟代码了。主要看第一步:
跟 findCandidateComponents :
类 ClassPathScanningCandidateComponentProvider
Spring5 源码阅读笔记(1.1.2)delegate.parseCustomElement(ele) 自定义标签的解析和注册
跟 scanCandidateComponents :

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<>();
	try {
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + '/' + this.resourcePattern;

		//这里递归寻找文件
		Resource[] resources = getResourcePatternResolver().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 = getMetadataReaderFactory().getMetadataReader(resource);
					//如果类上面有候选的注解,这里面可以看一下
					if (isCandidateComponent(metadataReader)) {
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setResource(resource);
						sbd.setSource(resource);
						if (isCandidateComponent(sbd)) {
							if (debugEnabled) {
								logger.debug("Identified candidate component class: " + resource);
							}
							candidates.add(sbd);
						}
						else {
							if (debugEnabled) {
								logger.debug("Ignored because not a concrete top-level class: " + resource);
							}
						}
					}
					else {
						if (traceEnabled) {
							logger.trace("Ignored because not matching any filter: " + resource);
						}
					}
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to read candidate component class: " + resource, ex);
				}
			}
			else {
				if (traceEnabled) {
					logger.trace("Ignored because not readable: " + resource);
				}
			}
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
	}
	return candidates;
}

跟 isCandidateComponent :

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	for (TypeFilter tf : this.excludeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return false;
		}
	}
	//前面出现过的 includeFilters 这里又出现了,这里面就有 Component 注解
	for (TypeFilter tf : this.includeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return isConditionMatch(metadataReader);
		}
	}
	return false;
}

看完上面的源码,我们就可以知道了为什么加了 < context:component-scan base-package=""> 标签,spring 就会扫描包下的 @Component 注解。

相关标签: spring