【Spring】Spring原理源码解析(二)-- Spring怎么识别@ComponentScan类并完成扫描生成BD注册到beanDefinitionMap中的

本文深入探讨Spring框架中@ComponentScan注解的工作原理和源码解析。从Spring初始化开始,分析ConfigurationClassPostProcessor如何处理配置类,进而触发扫描过程。通过ConfigurationClassParser解析配置类,使用ClassPathBeanDefinitionScanner进行包扫描,识别@Component等注解的类并将其注册为Bean。文章详细讲解了每个关键步骤的源码和逻辑,帮助读者理解Spring的核心机制。

系列文章主旨

从如何把自己的Bean注册到Spring容器开始,从该点出发,每篇只关注一个核心流程的原理、源码,再由该篇带出引申出来的其他Spring知识点,继续剖析,最终达到理解Spring核心原理的目的;

上篇内容

分析了Spring如何把自己的类注册到容器中,并且引申到spring会实例化自己内部的postProcessor完成扫描bean;
详情:【Spring】Spring原理源码解析(一)-- 从如何把自己的类注册为Bean到容器开始

本篇内容

从上一篇的spring内部实现的ConfigurationClassPostProcessor,分析spring识别@ComponentScan并完成扫描的原理源码;
文章图片均出自:https://www.processon.com/view/link/62d776fb6376893785aaa0df

核心原理

代码示例

指定配置类初始化spring容器,方法内部会把配置类注册到spring容器中

public static void main(String[] args) {
	  // 该方法会把我们的配置类注册到spirng容器中
	  // 并执行refresh方法,初始化spring容器
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
  }

配置类MyConfig,指定了扫描包路径

@ComponentScan(basePackages = "com.zsh.demo.spring.learn.register.bean")
public class MyConfig {
}

图解

在这里插入图片描述

核心处理逻辑

Spring容器进行初始化时,会默认生成自己的bean工厂后置处理器ConfigurationClassPostProcessor,并注册到spring容器中,然后在执行所有的bean工厂后置处理时,就会执行到ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()方法;

在执行该方法时,会实例化配置类的解析器ConfigurationClassParser,该解析器会解析我们注册到Spring的配置类,该jie类可以解析配置类的所有注解,得到配置类的注解元数据信息,本篇解析的是@ComponentScan注解的元数据信息;

其内部的还有一个组件解析器componentScanParser,把解析完的@ComponentScan注解的元数据信息交给该解析器,调用其解析方法parse();该组件会实例化扫描器ClassPathBeanDefinitionScanner,这个扫描器会真正的去执行扫描的动作;

ClassPathBeanDefinitionScanner该组件本身重要的组件为include、exclude拦截器,通过使用ASM技术1得到的类文件数据,会借由拦截器校验是否符合Spring认为的规则,如:@Component注解标识的类,就会把这些类构建为BeanDefinition对象,这样,就得到了所有的扫描bean;

进阶源码解析

spring注册配置类后初始化,调用内置工厂后置处理器
源码解析
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
	this();
	// 注册配置类
	register(componentClasses);
	// 初始化
	refresh();
}

this()方法会把内部的工厂后置处理器ConfigurationClassPostProcessor,注册到spring容器中,位置是在:org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object),详情可以参考【Spring】Spring原理源码解析(一)-- 从如何把自己的类注册为Bean到容器开始

register()方法把配置类解析为BeanDefinition并注册到spring容器中;
refresh()方法为spring容器初始化方法,跟进去,找到调用工厂后置处理器调用方法,invokeBeanFactoryPostProcessors;
继续跟进,找到调用非api加入的BeanFactoryPostProcessors调用postProcessBeanDefinitionRegistry位置

public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
	// .. 忽略非相关代码
	// 在这里调用
	invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
	// .. 忽略非相关代码
}

这里就是ConfigurationClassPostProcessor实现的postProcessBeanDefinitionRegistry()方法

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
	// 忽略非相关

	// 继续跟进
	processConfigBeanDefinitions(registry);
}

跟进方法之后,这就是我们要分析的扫描Bean的入口

识别并扫描Bean,源码入口
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	String[] candidateNames = registry.getBeanDefinitionNames();

	for (String beanName : candidateNames) {
		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
		if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
			}
		}
		else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
			configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		}
	}

	// Return immediately if no @Configuration classes were found
	if (configCandidates.isEmpty()) {
		return;
	}

	// Sort by previously determined @Order value, if applicable
	configCandidates.sort((bd1, bd2) -> {
		int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
		int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
		return Integer.compare(i1, i2);
	});

	// Detect any custom bean name generation strategy supplied through the enclosing application context
	SingletonBeanRegistry sbr = null;
	if (registry instanceof SingletonBeanRegistry) {
		sbr = (SingletonBeanRegistry) registry;
		if (!this.localBeanNameGeneratorSet) {
			BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
					AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
			if (generator != null) {
				this.componentScanBeanNameGenerator = generator;
				this.importBeanNameGenerator = generator;
			}
		}
	}

	if (this.environment == null) {
		this.environment = new StandardEnvironment();
	}

	// Parse each @Configuration class
	ConfigurationClassParser parser = new ConfigurationClassParser(
			this.metadataReaderFactory, this.problemReporter, this.environment,
			this.resourceLoader, this.componentScanBeanNameGenerator, registry);

	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
	Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
	do {
		// 核心入口
		parser.parse(candidates);
		parser.validate();

		// ..忽略非相关
	}
	while (!candidates.isEmpty());

	// ..忽略非相关
}

下面就是围绕着该方法进行解析;

初始化配置类解析器ConfigurationClassParser
找到容器中的所有配置类
图解

在这里插入图片描述

源码解析

先从spring拿到所有已注册bean的名称,然后进行遍历;
遍历时,会通过bean名称拿到BeanDefinition,然后先看是不是没有处理过,没有的话继续,通过checkConfigurationClassCandidate()方法,知道是不是配置类,是的话,会存入到configCandidates集合里面;
如果集合里面最后没有配置类,就直接返回了,因为连扫包的路径都不知道;
如果有的话,给这些配置类进行一个排序,等待后面有序解析;

// 拿到所有bean的名称
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();

// 遍历
for (String beanName : candidateNames) {
	// 拿到bean定义
	BeanDefinition beanDef = registry.getBeanDefinition(beanName);
	// 这里是看下是不是重复校验了bean,因为有可能名称不一样,但实际上是同一个类
	if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
		if (logger.isDebugEnabled()) {
			logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
		}
	}
	// 这里checkConfigurationClassCandidate()会校验这些bean哪些是配置类
	else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
		configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
	}
}

// 如果没有找到配置类,则没有需要扫描的包,这里就会直接退出
if (configCandidates.isEmpty()) {
	return;
}

// 对要被解析的这些配置Bean,进行一个排序
configCandidates.sort((bd1, bd2) -> {
	int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
	int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
	return Integer.compare(i1, i2);
});

这个方法主要是先通过bd拿到元数据信息
然后先通过最简单:
如果不是代理类,又有@Configuration注解,则是配置类;
或者看isConfigurationCandidate()这个方法的校验是不是配置类;
如果上述两个校验都不符合,则不是配置类;
如果是配置类,则把@Order的值放入到元数据,在元数据打上标识(完全是配置类,可能是配置类,无论哪种都会被解析),后续该bd就不会被重复校验了;

public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

	String className = beanDef.getBeanClassName();
	if (className == null || beanDef.getFactoryMethodName() != null) {
		return false;
	}

	AnnotationMetadata metadata;
	// 先看是不是被注解的类,是的话拿到元数据
	if (beanDef instanceof AnnotatedBeanDefinition &&
			className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
		// Can reuse the pre-parsed metadata from the given BeanDefinition...
		metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
	}
	// 否则,普通的bean都会走这里
	else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
		// Check already loaded Class if present...
		// since we possibly can't even load the class file for this Class.
		Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
		// spirng内置的几个工厂后置处理器,不认为是配置类,直接返回false
		if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
				BeanPostProcessor.class.isAssignableFrom(beanClass) ||
				AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
				EventListenerFactory.class.isAssignableFrom(beanClass)) {
			return false;
		}
		metadata = AnnotationMetadata.introspect(beanClass);
	}
	else {
		// 一般不会走到这里
		try {
			MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
			metadata = metadataReader.getAnnotationMetadata();
		}
		catch (IOException ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Could not find class file for introspecting configuration annotations: " +
						className, ex);
			}
			return false;
		}
	}

	// 如果有配置Configuration注解,但不是代理对象
	Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
	if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
		// 标记为完全是配置类
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
	// 重要的是看isConfigurationCandidate()方法,来判断是不是配置类,起码有机会是配置类
	else if (config != null || isConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	// 否则不是配置类
	else {
		return false;
	}

	// 取排序注解的值,后续进行排序后,配置类解析也会按照该顺序
	Integer order = getOrder(metadata);
	if (order != null) {
		beanDef.setAttribute(ORDER_ATTRIBUTE, order);
	}

	return true;
}

这个校验规则主要是
1、不能是接口
2、被@Component、@ComponentScan@Import、@ImportResource、@Bean任意一个注解标识;
如果不符合校验,则不是配置类后补;

private static final Set<String> candidateIndicators = new HashSet<>(8);

	static {
		candidateIndicators.add(Component.class.getName());
		candidateIndicators.add(ComponentScan.class.getName());
		candidateIndicators.add(Import.class.getName());
		candidateIndicators.add(ImportResource.class.getName());
	}

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
		// 接口不是配置类
		if (metadata.isInterface()) {
			return false;
		}

		// 看是否被Component、ComponentScan、Import、ImportResource注解标识,如果是的话,就认为是配置类
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}try {
			// 最后,看有没有@Bean标识,有的话也认为是
			return metadata.hasAnnotatedMethods(Bean.class.getName());
		}
		catch (Throwable ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
			}
			return false;
		}
	}
实例化配置类解析器ConfigurationClassParser
图解

在这里插入图片描述

源码解析

为ConfigurationClassParser提供必要组件,如环境、注册器、beanName生成器…
然后实例化

SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
	// 默认会进入
	sbr = (SingletonBeanRegistry) registry;
	if (!this.localBeanNameGeneratorSet) {
		// 这里是扫描包的beanName生成器,如果没有配置的话,使用默认的
		BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
				AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
		if (generator != null) {
			this.componentScanBeanNameGenerator = generator;
			this.importBeanNameGenerator = generator;
		}
	}
}

// 标准环境
if (this.environment == null) {
	this.environment = new StandardEnvironment();
}

// 实例化ConfigurationClassParser类
ConfigurationClassParser parser = new ConfigurationClassParser(
		this.metadataReaderFactory, this.problemReporter, this.environment,
		this.resourceLoader, this.componentScanBeanNameGenerator, registry);

这里会把一些重要组件初始化
其中包括componentScanParser ,后续就是它来作为扫描bean的解析

public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
			ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
			BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {

	this.metadataReaderFactory = metadataReaderFactory;
	this.problemReporter = problemReporter;
	this.environment = environment;
	this.resourceLoader = resourceLoader;
	this.registry = registry;
	// 这里会生成component注解扫描解析器,后续使用它作为扫描bean的解析器
	this.componentScanParser = new ComponentScanAnnotationParser(
			environment, resourceLoader, componentScanBeanNameGenerator, registry);
	this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
遍历配置类,调用配置类解析器ConfigurationClassParser的解析方法,内部进行扫包处理
图解

在这里插入图片描述

源码解析

继续跟进到 org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

// org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions 
// 继续跟
parser.parse(candidates);

遍历所有配置类,继续解析

public void parse(Set<BeanDefinitionHolder> configCandidates) {
		// 遍历配置类集合
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				// 被@Configuation注解的
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				// 被其它注解的,一般会进入到这个犯法
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
			// ..忽略非相关
			}
		}
		// ..忽略非相关
	}

看下当前配置类需不需要按照条件被跳过;
如果不需要跳过,则看是不是处理过,没有处理过则,遍历解析该配置类及其父类;
最后标识已经处理过该类了;

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
	// 没有被@Conditional注解标识的,不会被跳过
	if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
		return;
	}

	// configurationClasses缓存已经处理过的配置类,所以第一次,这里不会进入
	ConfigurationClass existingClass = this.configurationClasses.get(configClass);
	if (existingClass != null) {
		// ..忽略非相关
	}

	// 这里处理了本配置类,会继续拿父类去解析
	SourceClass sourceClass = asSourceClass(configClass, filter);
	do {
		sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
	}
	while (sourceClass != null);

	// 标识配置类已经被处理过
	this.configurationClasses.put(configClass, configClass);
}
解析配置类各项注解入口:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

这里会发现会解析配置类的诸如内部类、@Import、@Bean、@ComponentScan等注解,这里的解析等待后续篇章的更新;
本篇着重看解析@ComponentScan的解析;

得到@ComponentScan的信息,得到扫描包路径
如果配置了包路径,而且不会被跳过(一般不会跳过),这里就会调用componentScanParser组件,进行包路径的扫描解析,得到beanDefinition集合然后注册到beanDefinitionMap中;
然后遍历扫描得到的bd集合,如果发现它是配置/候补配置类,则继续调用parse方法,再去解析这个新扫描出来的类

@Nullable
protected final SourceClass doProcessConfigurationClass(
		ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
		throws IOException {

	// 解析Component注解
		// ..忽略非相关

	// 解析PropertySource 注解
		// ..忽略非相关

	// 解析ComponentScan 注解
	// 本篇核心解析这里
	Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
	if (!componentScans.isEmpty() &&
			// 本身没有被@Conditional注解标识的类,不会被跳过
			// 如果被标识了,配置类注册bean阶段,这里不会跳过,因此也会跳进来进行扫描
			!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
		for (AnnotationAttributes componentScan : componentScans) {
			// 这里会调用componentScanParser解析方法,执行扫描,得到bd集合
			Set<BeanDefinitionHolder> scannedBeanDefinitions =
					this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
			// 遍历扫描得到的bd集合,如果发现它是配置/候补配置类,则继续调用parse方法,去解析这个类		
			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
				if (bdCand == null) {
					bdCand = holder.getBeanDefinition();
				}
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
					// 这里,就是扫描出来的类如果是配置/配置候补,则解析这个新扫描出来的类
					parse(bdCand.getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}

	// 解析Import 注解
		// ..忽略非相关

	// 解析ImportResource 注解
		// ..忽略非相关

	// 解析Bean 注解
		// ..忽略非相关

	// 解析接口的默认方法
		// ..忽略非相关

	// 记录拿父类进行解析,外面就会走继续循环去处理父类
	if (sourceClass.getMetadata().hasSuperClass()) {
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (superclass != null && !superclass.startsWith("java") &&
				!this.knownSuperclasses.containsKey(superclass)) {
			this.knownSuperclasses.put(superclass, configClass);
			// Superclass found, return its annotation metadata and recurse
			return sourceClass.getSuperClass();
		}
	}

	// No superclass -> processing is complete
	return null;
}
初始化扫描器,ClassPathBeanDefinitionScanner
图解

在这里插入图片描述

源码解析
构造方法实例化,ClassPathBeanDefinitionScanner

这里先是调用构造方法实例化

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
	// 构造方法,跟进
	ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
			componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

	// ...
}

这里会发现,构造方法里面先注册了默认的拦截器,然后加入一些需要的组件,如环境组件、资源读取相关组件…

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

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

	// 默认为true
	if (useDefaultFilters) {
		// 这里会注册默认的拦截器,重点关注
		registerDefaultFilters();
	}
	// 放置spring的环境组件,这样就可以拿到配置等信息了
	setEnvironment(environment);
	// ASM读取文件相关组件
	setResourceLoader(resourceLoader);
}

默认include拦截器,加入@Component、@ManagedBean、@Named注解类型的拦截器
因此,这里都可以预估到,spring默认会扫描有这些注解的类,并认为是bean进行管理;

protected void registerDefaultFilters() {
	// 加入@Component注解类型拦截器
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
		// 加入@ManagedBean注解类型拦截器
		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 {
		// 加入@Named注解类型拦截器
		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.
	}
}

解析@Component注解元数据内容,并填充到到扫描器中,如:
扫包路径、beanName生成器(一般采用默认的)、lazyInit、scopedProxy…

// 解析注解元数据,相应设置、处理填充到扫描器中
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
	scanner.setScopedProxyMode(scopedProxyMode);
}
else {
	Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
	scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}

scanner.setResourcePattern(componentScan.getString("resourcePattern"));

for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
	for (TypeFilter typeFilter : typeFiltersFor(filter)) {
		scanner.addIncludeFilter(typeFilter);
	}
}
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
	for (TypeFilter typeFilter : typeFiltersFor(filter)) {
		scanner.addExcludeFilter(typeFilter);
	}
}

boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
	scanner.getBeanDefinitionDefaults().setLazyInit(true);
}

// 解析得到扫包路径
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
	String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
			ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
	Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
	basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
	basePackages.add(ClassUtils.getPackageName(declaringClass));
}

excludeFilters增加AbstractTypeHierarchyTraversingFilter exclude过滤器
该过滤器重写了matchClassName()方法,并且入参声明为当前配置类类名
可以预估到,spring扫描的时候,会把当前配置类给过滤掉,因为当前配置类已经在BDMap中了

scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
	@Override
	protected boolean matchClassName(String className) {
		return declaringClass.equals(className);
	}
});

调用扫描器的doScan()方法,完成扫描注册bean到spring的BDMap中

// 完成扫描
return scanner.doScan(StringUtils.toStringArray(basePackages));
扫描器进行扫描
图解

在这里插入图片描述

源码解析
扫描得到的BD集合,注册到spring容器beanDefinitionMap中

遍历扫描包路径
通过findCandidateComponents()方法得到bd扫描结果,然后遍历扫描结果;
这里先会解析@Scope注解,并记录到BD;
默认构造出来的结果都是AnnotatedBeanDefinition,因此解析Bean上面的其他一些注解,如@Lazy、@DependsOn、@Primary…,填充到BD中,;
然后检查如果该bean不存在在spring中,如果可以注册,则这里会先通过上面的@Scope解析结果,看是否需要修改bd为代理bd(此篇章略过不细看),然后把该bean构造为BeanDefinitionHolder(实际上属性就是beanName和beanDefinition);
最后注册到spring容器中,即填充到beanDefinitionMap中(注册动作,后续篇章再分析);

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) {
		// 扫描得到结果的核心方法
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			// 没有配置@Scope注解的信息,则scope相关的动作这里都不会进行
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			// 生成默认beanName,首字母小写
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			if (candidate instanceof AbstractBeanDefinition) {
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				// 扫描解析出来的bean都是AnnotatedBeanDefinition
				// 所以会进入这里,解析如@Lazy、@DependsOn、@Primary...注解信息,填充到BD
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			// checkCandidate()检查是不是注册过了
			if (checkCandidate(beanName, candidate)) {
				// 构造为bdHolder对象
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				// scope的东西配置了,则才会在这里把bean修改为代理bean,此处省略
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				// 注册到spring容器中,即填充到bdMap中
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

解析结果BD上的其他注解元数据,并把解析的值填充到BD中;
这里会解析:@Lazy、@Primary、@DependsOn、@Role、@Description注解;

/***
* org.springframework.context.annotation.AnnotationConfigUtils#processCommonDefinitionAnnotations(org.springframework.beans.factory.annotation.AnnotatedBeanDefinition, org.springframework.core.type.AnnotatedTypeMetadata) 
*/
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
	// 解析@Lazy
	AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
	if (lazy != null) {
		abd.setLazyInit(lazy.getBoolean("value"));
	}
	else if (abd.getMetadata() != metadata) {
		lazy = attributesFor(abd.getMetadata(), Lazy.class);
		if (lazy != null) {
			abd.setLazyInit(lazy.getBoolean("value"));
		}
	}

	// 解析@Primary
	if (metadata.isAnnotated(Primary.class.getName())) {
		abd.setPrimary(true);
	}
	
	// 解析@DependsOn
	AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
	if (dependsOn != null) {
		abd.setDependsOn(dependsOn.getStringArray("value"));
	}

	// 解析@Role
	AnnotationAttributes role = attributesFor(metadata, Role.class);
	if (role != null) {
		abd.setRole(role.getNumber("value").intValue());
	}
	
	// 解析@Description
	AnnotationAttributes description = attributesFor(metadata, Description.class);
	if (description != null) {
		abd.setDescription(description.getString("value"));
	}
}

这里是检查有没有在spring容器已经注册过;
如果不能直接获取出来,则后续可以注册,否则,就对比原有的bd和该bd是否为同一个bd,例如通过:resouce文件是否一致、引用是否一致判断;

protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
	// 拿不到,则直接返回true,可以进行注册
	if (!this.registry.containsBeanDefinition(beanName)) {
		return true;
	}
	BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
	BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
	if (originatingDef != null) {
		existingDef = originatingDef;
	}
	// 否则通过isCompatible()判断源和现bd是否一致
	if (isCompatible(beanDefinition, existingDef)) {
		return false;
	}
	// ..抛出异常
	throw new ConflictingBeanDefinitionException("Annotation-specified bean name ...");
}
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
		return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || 
			// resouce资源文件是否一致
			(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) ||  
			// 引用是否一致
			newDefinition.equals(existingDefinition));  
}

bdHolder,其实最主要的就是beanName、bd、和别名数组

public class BeanDefinitionHolder implements BeanMetadataElement {
	private final String beanName;
	private final BeanDefinition beanDefinition;
	@Nullable
	private final String[] aliases;

	// ...其他内部方法
findCandidateComponents()方法,得到扫描结果bd集合

跟踪方法
默认不会配index了,所以会走scanCandidateComponents()方法

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
		return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
	}
	else {
		// 核心入口
		return scanCandidateComponents(basePackage);
	}
}
解析,scanCandidateComponents()方法

先构建正确的类扫包路径,先构建正确的类扫包路径,最终会构建出来形如:classpath*:/扫包路径**/*.class路径;

// 最终会构建为: classpath*:/扫包路径**/*.class
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
	String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
			resolveBasePackage(basePackage) + '/' + this.resourcePattern;
	
	// ..方法内部其他内容
}

根据构建好的路径,得到资源文件集合

// 根据构建好的路径,得到资源文件集合		
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

遍历资源文件;
先通过ASM字节码技术1,读取出来类文件的元数据信息

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		// ... 
			
		// 遍历资源文件
		for (Resource resource : resources) {
			// ..日志打印相关,省略
			
			if (resource.isReadable()) {
				// 通过ASM技术获取文件元数据信息
				MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
			 
			 	// 后续其他操作..
		 	}
	 	}
}

校验元数据信息,看是否符合规则,可以交给spring进行管理;
若满足的话,则构建为bd对象,判断该对象不是接口或抽象类,则加入到结果集合中;

// 通过拦截器校验类是否满足规则,可以交给spring进行管理
if (isCandidateComponent(metadataReader)) {
	ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
	sbd.setSource(resource);
	// 判断该对象不是接口或抽象类
	if (isCandidateComponent(sbd)) {
		// ..日志打印,省略
		candidates.add(sbd);
	}
	
	// ..
}

调用拦截器的match方法进行判断,如果exclude包含了,则返回false排除,如果include match通过,则用condition来判断,这里一般不会配置,因此可以认为match(),通过了,就是需要找到的;
下面是放入的拦截器集合:
exclude:AbstractTypeHierarchyTraversingFilter(正在解析的配置类类名)
include:AnnotionTypeFilter (@Component、@ManagedBean、@Named)

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	for (TypeFilter tf : this.excludeFilters) {
		// 调用exclude拦截器的match方法
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return false;
		}
	}
	for (TypeFilter tf : this.includeFilters) {
		// 调用include拦截器的match方法
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return isConditionMatch(metadataReader);
		}
	}
	return false;
}

对于默认加入的exclude,include,分开解析match方法

exclude:
使用AbstractTypeHierarchyTraversingFilter
重写了matchClassName()方法,incident,如果配置类本身的话,返回true,即配置类本身会被忽略,原因是该配置类已经被注册到spring中了,无需重复再注册

@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
		throws IOException {

	if (matchSelf(metadataReader)) {
		return true;
	}
	ClassMetadata metadata = metadataReader.getClassMetadata();
	if (matchClassName(metadata.getClassName())) {
		return true;
	}

	if (this.considerInherited) {
		String superClassName = metadata.getSuperClassName();
		if (superClassName != null) {
			// Optimization to avoid creating ClassReader for super class.
			Boolean superClassMatch = matchSuperClass(superClassName);
			if (superClassMatch != null) {
				if (superClassMatch.booleanValue()) {
					return true;
				}
			}
			else {
				// Need to read super class to determine a match...
				try {
					if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
						return true;
					}
				}
				catch (IOException ex) {
					if (logger.isDebugEnabled()) {
						logger.debug("Could not read super class [" + metadata.getSuperClassName() +
								"] of type-filtered class [" + metadata.getClassName() + "]");
					}
				}
			}
		}
	}

	if (this.considerInterfaces) {
		for (String ifc : metadata.getInterfaceNames()) {
			// Optimization to avoid creating ClassReader for super class
			Boolean interfaceMatch = matchInterface(ifc);
			if (interfaceMatch != null) {
				if (interfaceMatch.booleanValue()) {
					return true;
				}
			}
			else {
				// Need to read interface to determine a match...
				try {
					if (match(ifc, metadataReaderFactory)) {
						return true;
					}
				}
				catch (IOException ex) {
					if (logger.isDebugEnabled()) {
						logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
								metadata.getClassName() + "]");
					}
				}
			}
		}
	}

	return false;
}

include:
使用AnnotionTypeFilter ,该类重写了matchSelf()方法,因此如果包含了指定注解,即@Component、@ManagedBean、@Named注解如果有的话,则match()方法直接返回true,外层即会认为这是需要管理的类;

public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter {
	@Override
	protectedboolean matchSelf(MetadataReader metadataReader) {
		AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
		// 是不是有指定注解
		return metadata.hasAnnotation(this.annotationType.getName()) ||
				(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
	}
}

总结

本篇主要介绍了,spring如何通过内置的,bean工厂后置处理器,解析配置类,识别配置类注解@ComponentScan,然后去扫描对应的包路径,最后生成bd,注册到spring容器中;

即首先spring容器在实例化的时候,就注册了默认的bean工厂后置处理器(ConfigurationClassPostProcessor)以及自己的配置类,然后在第一次执行postProcessBeanDefinitionRegistry(),方法时,就会调用到该内置类的该方法去解析自己的配置类;

先是判断配置类本身,校验是否为配置类,如果类有以下注解:@Configuration(全配置类),@Component、@ComponentScan、@Bean、@Import、@ImportResource(候补配置/lite配置)则是;

接着,构建parser调用parse()方法,开始解析配置类,并且该方法是支持递归解析该配置类的父类的;

parse内部就会去解析配置类上的各种注解,如:内部类、@Import相关等注解、@Bean等,其中就包括了@ComponentScan注解;

解析@ComponentScan注解时,会先去构造扫描器,会通过@ComponentScan得到的扫描包路径,拿到资源文件然后通过ASM技术1,读取文件元数据,再通过扫描器内部的拦截器,默认@Component、@Named、@ManagedBean都会校验通过,然后构造为beanDefinition对象,并且把这些对象

然后每得到这些新的对象,都会再次看下这些新对象是不是配置类/候补配置类,如果是的话,则又丢给parse方法,去递归解析;

注脚


  1. ASM技术简介:区别于类似于Class.forName()通过反射技术得到类信息,通过字节码技术直接解析文件,此时,该类文件不会走jvm的生命周期(如不会执行内部的static方法),使得Spring管理文件更加优秀、并且效率比反射要更高; ↩︎ ↩︎ ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值