Spring源码之注解扫描Component-scan

栏目: Java · 发布时间: 5年前

内容简介:本文主要介绍Spring的component-scan标签,了解spring是如果实现扫描注解进行bean的注册,主要实现实在 NamespaceHandler, NamespaceHandlerSupport 和 BeanDefinitionParser 三个接口中,还需要配置spring.handlers文件,在接下里的源码解析中会详细解析,在本篇博客中将使用ApplicationConntext作为起点,直接从差异开始讲解,如果想了解ApplicationContext 源码的全流程请看上篇博客。GI

本文主要介绍Spring的component-scan标签,了解spring是如果实现扫描注解进行bean的注册,主要实现实在 NamespaceHandler, NamespaceHandlerSupport 和 BeanDefinitionParser 三个接口中,还需要配置spring.handlers文件,在接下里的源码解析中会详细解析,在本篇博客中将使用ApplicationConntext作为起点,直接从差异开始讲解,如果想了解ApplicationContext 源码的全流程请看上篇博客。

GItHub: github.com/lantaoGitHu…

这里解析解释一下他们之间的关系:

NamespaceHandlerSupport 是 Abstract 修饰的抽象类 并 实现 NamespaceHandler 接口,继而实现了 NamespaceHandler接口的parser和docreate方法,自定的NamespaceHandler需要继承NamespaceHandlerSupport类并需要实现NamespaceHandler接口的init方法,init方法需要做解析类的注册操作,代码如下:

package org.springframework.context.config;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser;
import org.springframework.context.annotation.ComponentScanBeanDefinitionParser;

/**
 * {@link org.springframework.beans.factory.xml.NamespaceHandler}
 * for the '{@code context}' namespace.
 *
 * @author Mark Fisher
 * @author Juergen Hoeller
 * @since 2.5
 */
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());
	}

}复制代码
/**
	 * Subclasses can call this to register the supplied {@link BeanDefinitionParser} to
	 * handle the specified element. The element name is the local (non-namespace qualified)
	 * name.
	 */
	protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
		this.parsers.put(elementName, parser);
	}复制代码

BeanDefinitionParser类是解析类的顶层接口,自定义的解析类需要实现BeanDefinitionParser类的Parser方法,解析类的注册就在NameSpaceHandler的init方法中年进行;

  • 还是先看一下测试类:
package lantao.scan;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ScanTest {
	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-bean-scan.xml");
	}
}复制代码

xml文件中use-default-filters 属性的默认值为 true,即使用默认的 Filter 进行包扫描,而默认的 Filter 对标有 @Service,@Controller,@Component和@Repository 的注解的类进行扫描 ,如果定位为false的话,就需要进行自定义include-filter。

<?xml version="1.0" encoding="UTF-8" ?>
<beans
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans.xsd
	   http://www.springframework.org/schema/context
	   http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- use-default-filters 属性的默认值为 true,即使用默认的 Filter 进行包扫描,而默认的 Filter 对标有 @Service,@Controller和@Repository 的注解的类进行扫描 -->
	<context:component-scan base-package="lantao.scan" use-default-filters="false">
		<!-- 只扫描 base-package 的 controller 注解 还有对应的 exclude-filter 标签 排除 ; use-default-filters="false" 和 include-filter 一起使用 和 exclude-filter一起回抛异常-->
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
	</context:component-scan>

</beans>复制代码

因为这里使用 ApplicationContext,ApplicationContext 在上篇文章已经进行了源码解读,接下来我们直接看 差异点。

  • 差异代码在DefaultBeanDefinitionDocumentReader类中的parseBeanDefinitions方法中:
/**
 * Parse the elements at the root level in the document:
 * "import", "alias", "bean".
 * @param root the DOM root element of the document
 */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	//验证xml namespace, BeanDefinitionParserDelegate.BEANS_NAMESPACE_URI
	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)) {
					//对默认标签处理
					// 这里只处理 nade namespace 为 http://www.springframework.org/schema/beans 的标签
					parseDefaultElement(ele, delegate);
				}
				else {
					//对自定义标签处理 会解析 <context:component-scan base-package="lantao.scan"/> 或者自定义 dubbo
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		//对自定义标签处理
		delegate.parseCustomElement(root);
	}
}复制代码

主要差异就在 parseDefaultElement(ele, delegate) 和 delegate.parseCustomElement(ele) 方法上,parseDefaultElement方法仅仅会处理node的namespace是: www.springframework.org/schema/bean… 的标签, 其他标签自定义标签 全部都是通过这个方法来解析的;

  • delegate.parseCustomElement源码:
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
	return parseCustomElement(ele, null);
}

@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	// 获取node的 NameSpaceURI
	String namespaceUri = getNamespaceURI(ele);
	if (namespaceUri == null) {
		return null;
	}
	// 解析自定义标签 需要在 Meta-inf 文件加 增加 spring.handlers 文件 例如:http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
	// 根据指定的 NameSpaceURI 获取 NamespaceHandler  handler可以参考spring.handlers文件
	// abstract NamespaceHandlerSupport 实现了 NamespaceHandler 接口,继而实现了 NamespaceHandler 的两个个方法(parser,docreate),自定义handler 需要实现 NamespaceHandlerSupport 类
	// 进行 NamespaceHandler 类的 init 方法的 实现, 主要是做注册 BeanDefinitionParser( registerBeanDefinitionParser ) , 需要自定义解析类 继承 BeanDefinitionParser 类
	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));
}复制代码

这里代码很简单,只做了一下三件事情:

1:获取Element的NamespaceUri;

2:通过命名空间处理解析器(NamespaceHandlerResolver)的resolver方法进行NameSpaceHandler的处理;

3:通过NameSpaceHandler的Parse方法进行标签解析;

  • 我们直接看 resolve方法:
/**
 * Locate the {@link NamespaceHandler} for the supplied namespace URI
 * from the configured mappings.
 * @param namespaceUri the relevant namespace URI
 * @return the located {@link NamespaceHandler}, or {@code null} if none found
 */
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
        // 这里获取的是所有注册到 handlerMappings 中的  NamespaceHandler , 
	// 就是 resource/META-INF/spring.handler 中的类  key就是namespaceUri ,
	// 这些类都继承了 NamespaceHandlerSupport 实现了init方法 在init方法中进行 BeanDefinitionParse 的注册	Map<String, Object> handlerMappings = getHandlerMappings();
	// 通过 namespaceUri 在 handlerMappings 中获取对应的处理器或者 className 如果是初始化过的就直接返回,反之进行类初始化工作
	Object handlerOrClassName = handlerMappings.get(namespaceUri);
	if (handlerOrClassName == null) {
		return null;
	}
	else if (handlerOrClassName instanceof NamespaceHandler) {
		return (NamespaceHandler) handlerOrClassName;
	}
	else {
		String className = (String) handlerOrClassName;
		try {
			// 实例化
			Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
			// 判断实例化的类的超类或者超级接口 是否是 NamespaceHandler
			if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
						"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
			}
			NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
			// 注册 自定义标签所对应的 解析策略类  解析策略类都继承了 BeanDefinitionParser ,比如 ComponentScanBeanDefinitionParser
			namespaceHandler.init();
			// 放入缓存中
			handlerMappings.put(namespaceUri, namespaceHandler);
			return namespaceHandler;
		}
		catch (ClassNotFoundException ex) {
				throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
						"] for namespace [" + namespaceUri + "]", ex);
		}
		catch (LinkageError err) {
			throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
						className + "] for namespace [" + namespaceUri + "]", err);
		}
	}
}复制代码

这里主要做了一件事情,就是获取 nameSpaceUri 对应的 NameSpaceHandler ,首先会调动 getHandlerMappings 方法获取全部的 NameSpaceHandler, 然后通过 namespaceUri 获取对应的 NameSpaceHandler, 如果还未实例化则进行实例化操作执行init方法向parsers注册解析类,反之直接返回; getHandlerMappings 方法获取的 NameSpaceHandler 是解析于 resource/META-INF/spring.handler 文件下, key就是namespaceUri,value就是自定义的NameSpaceHandler;

  • getHandlerMappings方法源码:
/**
 * Load the specified NamespaceHandler mappings lazily.
 */
private Map<String, Object> getHandlerMappings() {
	Map<String, Object> handlerMappings = this.handlerMappings;
	if (handlerMappings == null) {
		synchronized (this) {
			handlerMappings = this.handlerMappings;
			if (handlerMappings == null) {
				if (logger.isTraceEnabled()) {
					logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
				}
				try {
					// 这里的handlerMappingsLocation指定的地址就是 resources 中的 META-INF/spring.handlers
					Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
					if (logger.isTraceEnabled()) {
						logger.trace("Loaded NamespaceHandler mappings: " + mappings);
					}
					handlerMappings = new ConcurrentHashMap<>(mappings.size());
					CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
					this.handlerMappings = handlerMappings;
				}
				catch (IOException ex) {
					throw new IllegalStateException(
								"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
				}
			}
		}
	}
	return handlerMappings;
}复制代码

getHandlerMappings就是解析spring.handler文件和执行NameSpaceHandler的init方法并放入缓存的操作,NameSpaceHandler获取到了以后我们看一下init注册的BeanDefinitionParser的parser方法;

  • NameSpaceHandlerSupport的parse方法源码:
/**
 * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
 * registered for that {@link Element}.
 */
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
	// 在 NamespaceHandlerSupport 中的 parser 集合中获取 BeanDefinitionParser 的实现类 进行 parser
	BeanDefinitionParser parser = findParserForElement(element, parserContext);
	return (parser != null ? parser.parse(element, parserContext) : null);
}复制代码

parse方法做了两件事情:

1:通过定义的标签属性(例如:component-scan)获取对应的BeanDefinitionParser解析类,源码如下:

/**
 * Locates the {@link BeanDefinitionParser} from the register implementations using
 * the local name of the supplied {@link Element}.
 */
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
	// 这里判断各种标签的解析策略 获取标签名字
	String localName = parserContext.getDelegate().getLocalName(element);
	// 从 parsers 中获取对应的解析策略类  parsers 是在 NameSpaceHandler 的 init 方法是初始化的;
	BeanDefinitionParser parser = this.parsers.get(localName);
	if (parser == null) {
		parserContext.getReaderContext().fatal(
				"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
	}
	// 返回对应的策略类进行解析
	return parser;
}复制代码

2:开始解析;

  • parse方法源码:
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
	// 获取 basePackage 的 路径
	String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
	// 解析给定文本中的${.},将其替换为由{@link #getProperty}解析的相应属性值 就是可以使用 ${} 和 properties中的值对应
	basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
	//我们这里在设置 base-package 的值时, 可以通过上面指示的分隔符 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS 进行多个package的指定. 可以使用”,” “;” “\t\n(回车符)”来分割多个包名
	String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
			ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

	// Actually scan for bean definitions and register them.
	// 下面的代码就是 实际扫描bean定义并注册它们。

	// 配置 ClassPathBeanDefinitionScanner
	ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
	// 扫描 并 注册
	Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
	// 处理 annotation-config
	registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

	return null;
}复制代码

parse方法主要做了以下五件事情:

1:获取basePackage的值,就是xml中配置的路径地址;

2:basePackage可以配置多个,使用 ‘,’ ';' 或者回车符 进行分割;

3:初始化ClassPathBeanDefinitionScanner,后边的解析操作有ClassPathBeanDefinitionScanner来完成;

4:扫描并注册bean;

5:处理annotation-config(这个后续会详细讲解,这里就不赘述了)

  • 先看一下configureScanner方法源码:
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
	boolean useDefaultFilters = true;
	// 设置 use-default-filters 标签  use-default-filters 属性的默认值为 true,即使用默认的 Filter 进行包扫描,而默认的 Filter 对标有 @Service,@Controller和@Repository 的注解的类进行扫描
	if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
		useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
	}

	// Delegate bean definition registration to scanner class.
	// 将注册Bean的任务委托给ClassPathBeanDefinitionScanner类。初始化 ClassPathBeanDefinitionScanner  ,ClassPathBeanDefinitionScanner 是解析conponentScanner 的类
	ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
		scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
		scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());

	// set RESOURCE_PATTERN_ATTRIBUTE 设置 扫描Resource(资源) 路径 默认为 "**/*.class"
	if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
		scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
	}

	try {
		// set name-generator
		// 初始化bean 名称生成器
		parseBeanNameGenerator(element, scanner);
	}
	catch (Exception ex) {
		parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
	}

	try {
		// 设置bean作用域
		parseScope(element, scanner);
	}
	catch (Exception ex) {
		parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
	}

	// 设置扫描包含 和 排除的 注解
	// 设置过滤器,即用于指定哪些类需要被处理,哪些类需要被忽略
	// set INCLUDE_FILTER_ELEMENT and EXCLUDE_FILTER_ELEMENT
	parseTypeFilters(element, scanner, parserContext);

	return scanner;
}复制代码

configureScanner方法主要做了以下五件事:

1:获取并设置use-default-filters,use-default-filters 属性的默认值为 true,即使用默认的 Filter 进行包扫描,而默认的 Filter 对标有 @Service,@Controller和@Repository 的注解的类进行扫描,如果设置为false,则需要自行对include-filter添加;

2:初始化ClassPathBeanDefinitionScanner,如果use-default-filters为true则对include-filter进行add操作;

3:初始化bean 名称生成器;

4:设置bean作用域;

5:设置扫描包含 和 排除的 注解,include-filter和exclude-filter;

上述代码就不展现了,git上代码有对应的注释;

  • 接下来看scanner.doScan方法:
/**
 * Perform a scan within the specified base packages,
 * returning the registered bean definitions.
 * <p>This method does <i>not</i> register an annotation config processor
 * but rather leaves this up to the caller.
 * @param basePackages the packages to check for annotated classes
 * @return set of beans registered if any for tooling registration purposes (never {@code null})
 */
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对象,默认为AnnotationScopeMetadataResolver
			// 如果目标类未被@Scope注解,则返回一个默认的ScopeMetadata
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());

			// 使用bean名称生成器生成bean名称,默认生成器为AnnotationBeanNameGenerator
			// 首先是以注解的value为bean名称,如果注解的value没有值,则使用默认的名称
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				// 处理定义在目标类上的注解,包括@Lazy, @Primary, @DependsOn, @Role, @Description
				// 这里会检查和 设置 AnnotatedBeanDefinition 的 @Lazy(懒加载) @Primary(主要,https://www.cnblogs.com/liaojie970/p/7885106.html) @DependsOn(需要依赖但不需要持有) 注解
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			// 检查beanName是否已经存在 BeanDefinitionRegistry 中存在。
			if (checkCandidate(beanName, candidate)) {
				//beanName 还没使用过
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				// 如果有必要,则创建作用域代理
				// 如果创建了代理,则返回表示代理对象的BeanDefinitionHolder
				definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				// 注册Bean
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}复制代码

看以下在doScan方法中都做了什么:

1:获取指定包下(指定的basePackage)所有 BeanDefinition;

2:获取一个ScopeMetadata对象,默认为AnnotationScopeMetadataResolver,如果目标类未被@Scope注解,则返回一个默认的ScopeMetadata;

3:使用bean名称生成器生成bean名称,默认生成器为AnnotationBeanNameGenerator,如果注解上的value值是null,则需要生成;

4:设置AutowireCandidate autowire-candidate="false" 表示该对象不参与自动注入,借鉴: blog.csdn.net/shangboerds…

5:处理定义在目标类上的注解,包括@Lazy, @Primary, @DependsOn, @Role, @Description,这里会检查和设置 AnnotatedBeanDefinition 的 @Lazy(懒加载) @Primary(主要, www.cnblogs.com/liaojie970/… ) @DependsOn(需要依赖但不需要持有) 注解;

6:检查beanName是否已经存在 beanDefinitionMap 中存在;

7:如果设置了scopedProxyMode,则需要创建代理类和注册代理类;

8:调用registerBeanDefinition注册bean,就是put到beanDefinitionMap中;

  • 这里只说核心的scanCandidateComponents方法,其他的方法都很简单,读者自行通过debug来做就可以了:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<>();
	try {
		// ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX = "classpath*:";
		// 通过观察resolveBasePackage()方法的实现, 我们可以在设置basePackage时, 使用形如${}的占位符, Spring会在这里进行替换
		// this.resourcePattern 默认为 "**/*.class" resourcePattern 可以再xml中配置
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + '/' + this.resourcePattern;
		// 使用上面拼接出的形如 "classpath*:xx/yyy/zzz/**/*.class", 将其检索为Spring内置的Resource对象(这样就统一化了资源的差异)
		// 使用ResourcePatternResolver的getResources方法获取 路径下全部  比如:classpath*:lantao/scan/**/*.class
		Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
		boolean traceEnabled = logger.isTraceEnabled();
		boolean debugEnabled = logger.isDebugEnabled();
		for (Resource resource : resources) {
			if (traceEnabled) {
				logger.trace("Scanning " + resource);
			}
			// file是否可读
			if (resource.isReadable()) {
				try {
					// 获取元数据  元数据就是用来定义数据的数据 就是定义 class 的 属性
					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);

					// 根据锅炉器来判断是否符合要求 做  includeFilters excludeFilters 的判断
					if (isCandidateComponent(metadataReader)) {
						// 实例化 ScannedGenericBeanDefinition
						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;
}复制代码

这里主要就是做了通过ResourcePatternResolver的getResource获取指定路径的资源文件,再通过资源文件Resource获取MetadataReader (元数据就是用来定义数据的数据 就是定义 class 的 属性),接下来通过isCandidateComponent方法来做核心处理,因为通过路径获取的资源是全部的,不是想要的,通过isCandidateComponent方法来做 ncludeFilters excludeFilters 的判断,再通过isCandidateComponent(sbd)判断BeanDefinition必须是一个实现类,不可以是接口等;

  • 我们看一下核心判断方法isCandidateComponent:
  • /**
    	 * Determine whether the given class does not match any exclude filter
    	 * and does match at least one include filter.
    	 * @param metadataReader the ASM ClassReader for the class
    	 * @return whether the class qualifies as a candidate component
    	 */
    	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    		// 判断 excludeFilters 的 TypeFilter
    		for (TypeFilter tf : this.excludeFilters) {
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				return false;
    			}
    		}
    		// 判断逻辑 includeFilters 中的 TypeFilter 默认包含的filter有 @components 和 引用他的  @service @controller @Repository
    		for (TypeFilter tf : this.includeFilters) {
    			if (tf.match(metadataReader, getMetadataReaderFactory())) {
    				// 判断 @Conditional , @Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。 还有 @ConditionalOnXX 等注解
    				return isConditionMatch(metadataReader);
    			}
    		}
    		return false;
    	}复制代码
    /**
     * Determine whether the given bean definition qualifies as candidate.
     * <p>The default implementation checks whether the class is not an interface
     * and not dependent on an enclosing class.
     * <p>Can be overridden in subclasses.
     * @param beanDefinition the bean definition to check
     * @return whether the bean definition qualifies as a candidate component
     */
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    	AnnotationMetadata metadata = beanDefinition.getMetadata();
    	// metadata.isIndependent() 是独立的 &
    	// metadata.isConcrete() 是否是接口或者是抽象类 或
    	// 必须是抽象类 和 有@lookup 注解
    	return (metadata.isIndependent() && (metadata.isConcrete() ||
    			(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
    }复制代码
  • 到这里就已经讲完了Component-scan扫描注入的源码,这里涉及代理和annotation-config没有做详细的讲解,会在后续的文章中做,码字不易,转发请注明出处: blog.csdn.net/qq_30257149…


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

增长黑客

增长黑客

Sean Ellis / 张溪梦 / 中信出版集团股份有限公司 / 2017-11-1 / CNY 59.00

脸谱网如何从默默无闻到坐拥二十几亿用户? 爱彼迎、优步何以在短短时间估值超过百亿美元? 领英怎样跨步成为全球领先的职业社交平台? 这些初创公司实现爆发式成长的共同奥秘就是增长黑客。 增长黑客是硅谷当下热门的新商业方法论,其精髓在于通过快节奏测试和迭代,以极低甚至零成本获取并留存用户。 作为最早提出“增长黑客”概念的理论先驱、带领Dropbox实现500%增长的实战领军......一起来看看 《增长黑客》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具