spring的扫描一般可以通过两种方式:

测试类:

@Component
public class Scan_A {

	@PostConstruct
	public void init(){
		System.out.println("-----------Scan_A");
	}

}

1)、@ComponentSscan注解

public class ComponentScanTest {
	public static void main(String[] args) {


		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ContextConfig.class);
		
	}
}

配置类:(可配@Compnent,也可不配,都会生效)

@ComponentScan("com.spring.demo.scan")
public class ContextConfig {
}

启动此时控制台:


----------Scan_A


则表示扫描到了。

2)、使用api调用扫描

public class ApplicationContextTest {


    public static void main(String[] args) {


        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.scan("com.spring.demo.scan");
		context.refresh();

	}
}

此时运行,照样能打印----------Scan_A

两种方法实例化了各自实例化了自己的扫描器,但最终都是调用doscan方法完成解析的。

(关于两种方式的区别,后面说明,不过我们最常用的应该就是第一种方式)

==========================我们先以第一种情况作为分析========================

上一篇文章中,invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors,registry)代码行中(情况2),ConfigurationClassPostProcessor执行了其子接口实现类。我们说到这个实现类完成对注解类的扫描。我们就以此为入口。

调用链:ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(子接口实现方法)

--------》processConfigBeanDefinitions(registry)

/**
	 * ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口的实现类
	 * 功能:完成对注解的扫描解析,并放入BeanDefinitionMap(此时未进行实例化为bean,应为采用的是ASM字节码技术,所以不会在这里实例化)
	 * registry------容器对象
	 * */
	/**
	 * Build and validate a configuration model based on the registry of
	 * {@link Configuration} classes.
	 */
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		//获取内置的+我们提供的配置类的BeanDefinition名称
		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);
				}
			}
			//标记配置类是全配置类(用了@Configuration)或者半配置类(加了CompnentScan、Component、Import、ImportResource任一个注解)
//很多情况下@Component注解和@Configuration注解在配置功能方面几乎没有什么区别;如果不存在这个@Bean方法的嵌套调用,你完全可以用@Component去替代@Configuration
			//是则加入集合(我们这里一般会有6个配置类,5个内置的+1个我们提供的(当然我们可以提供多个,不过一般是一个)
			// 此时只有我们提供配置类有用到@CompnentScan,所以这里只会加入一个
			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);
		});

		//获取Bean名称的生成策略
		// 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();
		}


		//实例化一个配置类的解析器
		//此时构造方法传入一个componentScanBeanNameGenerator----类型AnnotationBeanNameGenerator(即一个Bean的名称生成策略)
		// 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 {
			//调用parse方法进行解析
			parser.parse(candidates);
			parser.validate();

			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			candidates.clear();
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}

		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
			// Clear cache in externally provided MetadataReaderFactory; this is a no-op
			// for a shared cache since it'll be cleared by the ApplicationContext.
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}

===============

这里做个小插曲讲讲什么是全配置类和半配置类:


        配置类会在ConfifigurationClassPostProcessor#postProcessBeanDefifinitionRegistry 的方法里面被解析


主要看改配置类上面是否加了@Configuration注解如果加了则会标记这个类为 full—— 全配置 类;如果没加@Configuration 但是加了其他注解则标识为 lite—— 半配置类;



static {



candidateIndicators . add ( Component . class . getName ());



candidateIndicators . add ( ComponentScan . class . getName ());



candidateIndicators . add ( Import . class . getName ());



candidateIndicators . add ( ImportResource . class . getName ());



}



加了四个其中任意一个都会作为半配置类。



(注意:只有直接被直接穿进去的配置类才不用做@Compnent、或者@Configuration的注入bean操作,spring会将其实例化为bean,而通过配置类的@CompnentScan扫描出来的必须要加上@Compnent、@Configuration才能注入到spring中)


所以很多情况下 @Component 注解和 @Confifiguration 注解在配置功能方面几乎没有什么区别;如果不


存在这个 @Bean 方法的嵌套调用,你完全可以用 @Component 去替代 @Confifiguration。


不止我们传进去的配置类会被标记为全、半配置类,我们扫描出来的类也会被标记。


那么spring是如何处理的?全配置类和半配置类有什么区别?



1、spring如何处理全配置类?



//判断配置类是全配置类(用了@Configuration)或者半配置类(加了CompnentScan、Component、Import、ImportResource任一个注解)
			//很多情况下@Component注解和@Configuration注解在配置功能方面几乎没有什么区别;如果不存在这个@Bean方法的嵌套调用,你完全可以用@Component去替代@Configuration
			//是则加入集合(我们这里一般会有6个配置类,5个内置的+1个我们提供的(当然我们可以提供多个,不过一般是一个)
			// 此时只有我们提供配置类有用到@CompnentScan,所以这里只会加入一个
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}

我们进去ConfigurationClassUtils.checkConfigurationClassCandidate方法:

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();
		}
		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();
			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());
		//加了Configuration注解,并获取注解的属性proxyBeanMethods(默认值为true)
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			//标记为CONFIGURATION_CLASS_ATTRIBUTE属性为full,即该类被标记了为全配置类
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		//加了Configuration注解 || 是否加了Component、ComponentScan、Import、ImportResource
		//注意这里是||符号
		else if (config != null || isConfigurationCandidate(metadata)) {
			//标记为lite,版配置类
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}

		// It's a full or lite configuration candidate... Let's determine the order value, if any.
		Integer order = getOrder(metadata);
		if (order != null) {
			beanDef.setAttribute(ORDER_ATTRIBUTE, order);
		}

		return true;
	}

此时我们进入isConfigurationCandidate方法看看怎么判断的:

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
		// Do not consider an interface or an annotation...
		if (metadata.isInterface()) {
			return false;
		}

		//可以查看candidateIndicators的值被初始化了
		//static {
		//		candidateIndicators.add(Component.class.getName());
		//		candidateIndicators.add(ComponentScan.class.getName());
		//		candidateIndicators.add(Import.class.getName());
		//		candidateIndicators.add(ImportResource.class.getName());
		//	}
		// Any of the typical annotations found?
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}

		// Finally, let's look for @Bean methods...
		return hasBeanMethods(metadata);
	}

点击这个candidateIndicators发现做了初始化:

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());
	}

至此spring对配置类的标记完成。

2、扫描出来的类也会被标识吗?

在执行ConfigurationClassPostProcessor#processConfigBeanDefinitions时,解析其调用解析方法



parser.parse(candidates);解析@CompnentScan注解时就对扫描出来的类进行了标记:



spring扫描包工具 spring扫描包原理_spring扫描包工具

进入parse()--->processConfigurationClass()--->doProcessConfigurationClass()

spring扫描包工具 spring扫描包原理_sed_02

 标记方法与上面对配置类的标记方法一样的。

 所以说不管是配置类还是扫描出来的类,都会被标记为Full还是lite。

下面我们看下个问题

3、spring如何处理全、半配置类:

上面的方法只是对配置类进行标记为full还是lite(ConfifigurationClassPostProcessor执行postProcessBeanDefifinitionRegistry会进行标记),但没有对其进行处理,而对全/半配置类的处理(ConfifigurationClassPostProcessor的父接口方法postProcessBeanFactory时进行处理)入口:

注意:到执行ConfifigurationClassPostProcessor的父接口方法时,扫描出来的类也会被加到BeanDefinitionMap中了。

spring扫描包工具 spring扫描包原理_后端_03

我们继续点进去,找到这个方法:ConfifigurationClassPostProcessor#postProcessBeanFactory

@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		int factoryId = System.identityHashCode(beanFactory);
		if (this.factoriesPostProcessed.contains(factoryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + beanFactory);
		}
		this.factoriesPostProcessed.add(factoryId);
		if (!this.registriesPostProcessed.contains(factoryId)) {
			// BeanDefinitionRegistryPostProcessor hook apparently not supported...
			// Simply call processConfigurationClasses lazily at this point then.
			processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
		}

		//增强、代理全配置类(Full),生成全配置类的代理类
		//不对半配置类(lite)做代理
		enhanceConfigurationClasses(beanFactory);
		beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
	}

进入代理方法:

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
		Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();

		//执行到这里时,扫描出来的类也被加到BeanDefinitionMap了
		for (String beanName : beanFactory.getBeanDefinitionNames()) {
			BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
			//获取BeanDefinition中全、半配置的标记属性---CONFIGURATION_CLASS_ATTRIBUTE
			// (之前在ConfifigurationClassPostProcessor执行postProcessBeanDefifinitionRegistry会进行标记是否全full、半lite配置)
			Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
			AnnotationMetadata annotationMetadata = null;
			MethodMetadata methodMetadata = null;
			//判断是否加了注解的
			if (beanDef instanceof AnnotatedBeanDefinition) {
				AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDef;
				annotationMetadata = annotatedBeanDefinition.getMetadata();
				methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
			}
			if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
				// Configuration class (full or lite) or a configuration-derived @Bean method
				// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
				// or component class without @Bean methods.
				AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
				if (!abd.hasBeanClass()) {
					boolean liteConfigurationCandidateWithoutBeanMethods =
							(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&
								annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));
					if (!liteConfigurationCandidateWithoutBeanMethods) {
						try {
							abd.resolveBeanClass(this.beanClassLoader);
						}
						catch (Throwable ex) {
							throw new IllegalStateException(
									"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
						}
					}
				}
			}

			//如果被标记了为Full(全配置类),先put到configBeanDefs集合中
			if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
				if (!(beanDef instanceof AbstractBeanDefinition)) {
					throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
							beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
				}
				else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
					logger.info("Cannot enhance @Configuration bean definition '" + beanName +
							"' since its singleton instance has been created too early. The typical cause " +
							"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
							"return type: Consider declaring such methods as 'static'.");
				}
				configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
			}
		}
		if (configBeanDefs.isEmpty()) {
			// nothing to enhance -> return immediately
			return;
		}

		//统一对全配置类的集合configBeanDefs进行处理(使用cglib生成代理类)
		ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
		for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
			AbstractBeanDefinition beanDef = entry.getValue();
			// If a @Configuration class gets proxied, always proxy the target class
			beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
			// Set enhanced subclass of the user-specified bean class
			//生成一个代理类
			Class<?> configClass = beanDef.getBeanClass();
			Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
			if (configClass != enhancedClass) {
				if (logger.isTraceEnabled()) {
					logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
							"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
				}
				//设置BeanDefinition的BeanClass为代理类
				beanDef.setBeanClass(enhancedClass);
			}
		}
	}

至此我们可以知道spring对全配置类做了动态代理,BeanDefinition.setBeanClass(代理类),所以我们注入拿到的对象就是一个代理对象。而对半配置类,spring并没有做任何处理。所以我们注入拿到的是原对象。这也就是常见面试题@Configuration和@Compnent的区别。


可是我们现在又有一个问题,


4、为什么要做代理?


先给结论:加了@Configuration注解是为了保证注入的bean的作用域的正确性:


我们来看两个例子


Scan_D类:


public class Scan_D {


	public Scan_D() {

		System.out.println("create Scan_D");
	}
}

Scan_E类:

public class Scan_E {


	public Scan_E() {

		System.out.println("create Scan_E");
	}
}

配置类:先使用半配置类的方式:

@ComponentScan(value = "com.spring.demo.scan")
public class FullOrLiteConfig {

	@Bean
	public Scan_D scan_d(){
		return new Scan_D();
	}

	@Bean
	public Scan_E scan_e(){
		scan_d();
		return new Scan_E();
	}
}

启动类:

public class ComponentScanTest {
	public static void main(String[] args) {


		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(FullOrLiteConfig.class);

	}
}

此时控制台打印:

create Scan_D
create Scan_D
create Scan_E

可以看到,我们本来默认注入到spring的bean是单例的,只需要实例化一次,而这里Scan_D被实例化了两次,显然这不符合我们对bean作用域的理解。那么我们现在修改配置类为Full方式的:

@ComponentScan(value = "com.spring.demo.scan")
@Configuration
public class FullOrLiteConfig {

	@Bean
	public Scan_D scan_d(){
		return new Scan_D();
	}

	@Bean
	public Scan_E scan_e(){
		scan_d();
		return new Scan_E();
	}
}

再次运行:

create Scan_D
create Scan_E

发现Scan_D只被实例化了一次,所以说全配置类的方式保证了注入的bean的作用域的正确性。

那么spring怎么去实现的?

可以看看我的下一篇文章:

(我们就不在这篇文章里面写了)



==============

继续我们的源码分析,进入parser.parse(candidates)---》parse()-----》processConfigurationClass(ConfigurationClass configClass)

此时可以看到processConfigurationClass方法传进了一个ConfigurationClass类型,该类型就是用来抽象化配置类、半配置类的一些配置信息等,例如:

final class ConfigurationClass {

	private final AnnotationMetadata metadata;

	private final Resource resource;

	@Nullable
	private String beanName;

    //该配置类是被谁导入进来的
	private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);
	//当前配置类中的所有@Bean方法
	private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();

	//xml相关的
	private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
			new LinkedHashMap<>();

	//当前配置类上所有被@Import进来的实现了ImportBeanDefinitionRegistrar的对象
	private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
			new LinkedHashMap<>();

}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}

		//查看缓存是否存在(每解析完一个配置类会放进缓存)
		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		if (existingClass != null) {
			if (configClass.isImported()) {
				if (existingClass.isImported()) {
					existingClass.mergeImportedBy(configClass);
				}
				// Otherwise ignore new imported config class; existing non-imported class overrides it.
				return;
			}
			else {
				// Explicit bean definition found, probably replacing an import.
				// Let's remove the old one and go with the new one.
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}

		// Recursively process the configuration class and its superclass hierarchy.
        //描述配置类的基本信息
		SourceClass sourceClass = asSourceClass(configClass, filter);
		do {
			//解析配置类
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}

看到上面的ConfigurationClass来描述配置类的配置信息,而SourceClass用来描述配置类的基本信息。此时asSourceClass方法会将配置信息一起放到SourceClass中。

进入doProcessConfigurationClass()

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

		//解析配置类是否加了@Compnent注解
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			//如果加了,则处理内部类
			processMemberClasses(configClass, sourceClass, filter);
		}

		//处理配置类@propertySource注解(读取文件)
		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		//解析@ComponentScan注解(@ComponentScans即可扫描多个包)
		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {

			//配置类存在@ComponentScan或@ComponentScans注解
			for (AnnotationAttributes componentScan : componentScans) {

				//解析这个@ComponentScan注解,完成对@Compnent注解的扫描,并返回Set<BeanDefinitionHolder>
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				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

		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

//解析@ImportResource 
		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

//解析@Bean
		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		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;
	}

注意:这里涉及对于配置类上面加了多个注解例如@Import、@ImportResource、@Bean、@PropertySource、@Compnent的处理逻辑,由于该篇不解析这部分内容,所以专门有一篇写如何解析这些注解---------》文章如下:

spring-----如何处理配置类上的注解_先熬半个月的博客-CSDN博客

进入this.componentScanParser.parse(componentScan,sourceClass.getMetadata().getClassName())

开始解析@CompnentScan注解:

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {

		//实例化一个扫描器
		//registry----容器对象
		//useDefaultFilters----是否使用默认的过滤器
		//存在两种过滤器:icludeFilter引入过滤器、excludeFilter排除过滤器
		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

		//看看@CompnentScan注解中的nameGenerator属性(bean名称的生成策略,可以自己配置)是否有配置,有则获取
		Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
		boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
		//设置bean名字的生成策略
		scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
				BeanUtils.instantiateClass(generatorClass));

		//下面的都是对注解的属性的解析(scopedProxy、resourcePattern、includeFilters等等)
		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"));

		//加入Include过滤器
		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));
		}

		//加入Exclude过滤器
		scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
			@Override
			protected boolean matchClassName(String className) {
				return declaringClass.equals(className);
			}
		});


		//
		return scanner.doScan(StringUtils.toStringArray(basePackages));
	}

进入scanner.doScan()

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) {
			
			//使用ASM字节码技术读取对应的目录,解析符合的类(加了@Compnent注解)并返回BeanDefinition集合
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				//根据bean名字生成策略生成bean名称
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				//查看是否已经存在BeanDefinition,不存在则进行注册
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

我们可以继续往findCandidateComponents方法看看,大概是怎么扫描的,再次进入


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[]对象
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();

			//遍历获取到单个Resource对象(即单个文件)
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				try {
					//ASM技术读取文件内容转为MetadataReader对象
					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
					//判断该对象是否被@CompnentScan的excludeFilters排除被排除则false,
					// 被includeFilters或者存在@Compnent则实例化成ScannedGenericBeanDefinition对象放进集合
					//注意:这里判断是否有@Compnent是因为在实例化扫描器ClassPathBeanDefinitionScanner的时候会执行registerDefaultFilters()方法
					//     在该方法中默认就注入了Component到includeFilter中(this.includeFilters.add(new AnnotationTypeFilter(Component.class));)
					//     所以如果有@Compnent注解则在判断是否在includeFilter是返回true。
					if (isCandidateComponent(metadataReader)) {
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						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 (FileNotFoundException ex) {
					if (traceEnabled) {
						logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
					}
				}
				catch (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to read candidate component class: " + resource, ex);
				}
			}
		}
		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;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

可以看到只获取includeFilters中存在的,我们知道我们自己在配置类的@CompnentScan注解中手动加入了一个Scan_C类到includeFilters,所以Scan_C是可以扫到的,那么加了@Compnent注解的Scan_A又是怎么被扫描的?

其实在实例化一个扫描器时,就会默认加入Compnent.Class到IncludeFilters中,我们可以看看代码:进入扫描器类ClassPathBeanDefinitionScanner

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

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

		//加入默认的类到includeFilters
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		setEnvironment(environment);
		setResourceLoader(resourceLoader);
	}

进去registerDefaultFilters方法看看加了哪些类:

protected void registerDefaultFilters() {

//加入Compnent类
		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.
		}
	}

至此则解答了如何扫描出有@Compnent注解的类的疑问。

spring扫描包工具 spring扫描包原理_java_04

spring扫描包工具 spring扫描包原理_spring扫描包工具_05

 总体下来扫描的大概流程:

spring内置的ConfigurationClassPostProcessor执行子接口实现方法(postProcessBeanDefinitionRegistry)
-----》通过容器registry获取spring内置类+传入的配置类
-----》依次判断是否为合格的配置类
-----》如是则获取配置类的部分属性,实例化一个扫描器(扫描器实例化的时候会将Compnent类加入到includeFilters中,为了后判断哪些类用了@Compnent注解)
-----》解析配置类是否加了@Compnent、@Improt、@CompnentScan等等注解,有则进行解析
-----》解析@CompnentScan注解,解析注解中的各种属性(includeFilters、excludeFilters、beanNameGenerator等)
----》扫描包路径,使用ASM技术读取文件内容为MetadataReader对象,并判断是否能对应includeFilters中的类(并排除excludeFilters的类)
----》有则转化为ScannedGenericBeanDefifinition对象,并判断ScannedGenericBeanDefifinition是否接口是否抽象、是否加了LockUp注解等等
-----》然后加入Set<BeanDefinition>并返回,最后对其进行注册(put到BeanDefinitionMap,但还不会实例化为bean)。

=============================第二种情况做分析==============================

第二种情况非常简单,而且和第一种都是扫描器调用了doscan方法进行扫描解析的。


AnnotationConfigApplicationContext会实例化一个扫描器,然后该扫描器直接调用doScan(basePackages)方法。


=====================第一种情况下的excludeFilter、includeFilter===================

我们先看看怎么使用这两个@CompnentScan注解的属性。

新增两个测试类,一个加@Compnent,一个不加。

@Component
public class Scan_B {

	@PostConstruct
	public void init(){
		System.out.println("-----------Scan_B");
	}
}
public class Scan_C {

	@PostConstruct
	public void init(){
		System.out.println("-----------Scan_C");
	}
}

修改下配置类:

/**
 * 排除Scan_B,加入Scan_C
 * */
@ComponentScan(value = "com.spring.demo.scan",
			excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = Scan_B.class)},
			includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = Scan_C.class)})
public class ContextConfig {
}

此时启动ComponentScanTest,查看控制台打印:

spring扫描包工具 spring扫描包原理_java_06

 由此我们可以知道includeFilters 属性的作用是增加型过滤器,而excludeFilters是排除型过滤器。 

》》》》》》》》》》》》》》》》》》》问题解答

1、为什么spring扫描要使用ASM获取字节码技术?

ASM是读取字节码文件,并不会提前加载类或执行类的方法,这样不会对类的生命周期等流程产生影响。且效率较高(比反射等方式效率高)

我们以第一种情况为例,看看完成扫描的时候Scan_A有没有被实例化(如被实例化会执行init方法)前一篇文章我们可以知道情况2执行invokeBeanDefinitionRegistryPostProcessors后会完成扫描,此时我们在这行打上断点。

spring扫描包工具 spring扫描包原理_后端_07

 此时发现Scan_A还没被扫描到,控制台也没打印任何东西,让后我们执行这行代码后:

spring扫描包工具 spring扫描包原理_java_08

spring扫描包工具 spring扫描包原理_spring boot_09

此时Scan_A被扫描到了,但ini方法并没有被实例化,所以spring采用ASM扫描并不会对对象实例化产生影响。(如果此时使用反射等方式,此时就会对对象进行实例化,从而打乱了原先对象实例化的顺序(提前进行实例化了)) 

2、两个扫描器的区别?

两种方式的扫描器类型都是ClassPathBeanDefinitionScanner,且都调用了doscan方法进行扫描解析。不同的是@CompnentScan注解提供了更多属性的配置,例如nameGenerator、includeFilters等。

3、@Inherited注解的作用

配置是否能被子类继承。