【源码】Spring —— ClassPathBeanDefinitionScanner 解读

92 阅读4分钟

【源码】Spring —— ClassPathBeanDefinitionScanner 解读

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

Spring容器类 AnnotationConfigApplicationContext,有两个成员属性 AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner 用于解析并注册对应的 BeanDefinition 对象到容器中。本章节,介绍 ClassPathBeanDefinitionScanner

版本

Spring 5.2.x

ClassPathBeanDefinitionScanner

AnnotatedBeanDefinitionReader 不同的是,ClassPathBeanDefinitionScanner 是基于路径扫描的,也就是说我们也可以用以下方式初始化容器

AnnotationConfigApplicationContext ac =
				new AnnotationConfigApplicationContext("com.xsn.config");

属性

	private final BeanDefinitionRegistry registry;

	private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();

	@Nullable
	private String[] autowireCandidatePatterns;

	/**
	 * public static final AnnotationBeanNameGenerator INSTANCE 
	 * 			= new AnnotationBeanNameGenerator();
	 */
	private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;

	private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

	// 是否注册基于注解相关的 后置处理器
	private boolean includeAnnotationConfig = true;
  • BeanDefinitionRegistry,注册中心,此处就是容器本身
  • BeanDefinitionDefaults,一个简易的 BeanDefinition 封装
  • AnnotationBeanNameGenerator,用于给被标注了 @Component 以及被标注 @Component 注解的其他注解(比如 @Service 等)所标注的 bean,以及被其他规范下如 @ManagedBean @Named 等注解标注的 bean 生成对应的 beanName,如果无法从注解的属性中解析,则根据一定规则处理类名生成,比如 ExampleClass -> exampleClass
  • AnnotationScopeMetadataResolver,用于解析对应的 ScopeMetadata
  • ConditionEvaluator,用于解析 Condition 相关注解,判断是否注册目标 BeanDefinition

构造

	// 略去其他空壳构造
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		this.registry = registry;

		// 这里会根据 useDefaultFilters 选择是否注册对应的 TypeFilter
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		setEnvironment(environment);
		setResourceLoader(resourceLoader);
	}

	// 包含和排除的 Type,父类属性
	private final List<TypeFilter> includeFilters = new LinkedList<>();
	private final List<TypeFilter> excludeFilters = new LinkedList<>();
  • 根据 useDefaultFilters 选择是否注册对应的 TypeFilter
  • 属性 includeFilters excludeFilters 决定是否 包含|排除 对应的元素
  • 维护 environment resourceLoader

BeanDefinition 的注册(scan)

	public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		doScan(basePackages);

		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}
		
		// 返回注册的 BeanDefinition 数
		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

该方法注册对应的 BeanDefinition 并返回对应的数目,其中值得一提的是会根据 includeAnnotationConfig(默认 true)选择是否注册以下 BeanDefinition

  • ConfigurationClassPostProcessor,不存在对应 BeanDefinition 则注册,支持 @Configuration 注解的解析
  • AutowiredAnnotationBeanPostProcessor,不存在对应 BeanDefinition 则注册,支持 @Autowired @Value 等注解的解析
  • CommonAnnotationBeanPostProcessor,引入 了JSR-250 相关依赖且不存在对应 BeanDefinition 则注册,支持 @Resource 等注解的解析
  • PersistenceAnnotationBeanPostProcessor,引入了 JPA 相关依赖且不存在对应 BeanDefinition 则注册,支持 JPA 相关后处理
  • EventListenerMethodProcessor,不存在对应 BeanDefinition 则注册,负责对 @EventListener 注解标注的方法进行后处理
  • DefaultEventListenerFactory,不存在对应 BeanDefinition 则注册,主要由 EventListenerMethodProcessor 使用,用来基于 @EventListener 注解标注的方法获取对应 ApplicationListener

doScan

	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {

		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {

			// 获取 basePackages 类路径下所有的 BeanDefinition
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {

				// 获取 BeanDefinition 的 ScopeMetadata 属性
				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);
				}

				// 根据 元数据 解析 Lazy Primary DependsOn Role Description 属性
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}

				// 检验是否需要注册
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);

					// 如果需要代理则代理处理
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);

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

		// 不同于 AnnotatedBeanDefinitionReader 的 register方法
		// 此处返回所有 BeanDefinition
		return beanDefinitions;
	}
  • 从父类的 findCandidateComponents 方法获取所有 BeanDefinition,该方法下面会重点解读
  • ScopeMetadata 解析
  • beanName 生成
  • postProcessBeanDefinition 方法对 BeanDefinition 做了初步处理:1)根据给定的 beanDefinitionDefaults 设置部分属性 2)如果指定了 autowireCandidatePatterns,依此设置 autowireCandidate 属性
  • 根据 元数据 解析 Lazy Primary DependsOn Role Description 属性
  • 校验目标 BeanDefinition 是否需要注册,即是否已经存在一个可以与之兼容的 BeanDefinition
  • 如果校验通过,封装目标 BeanDefinition 并调用 AnnotationConfigUtils.applyScopedProxyMode 方法,逻辑简述:根据之前解析的 ScopeMetadata 判断是否需要代理,如果需要代理,则会值得目标类型为 ScopedProxyFactoryBean:这主要是针对作用域实例需要代理的场景
  • 最后注册目标 BeanDefinition

ClassPathScanningCandidateComponentProvider#findCandidateComponents

	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			// 获取指定 basePackage 下的所有类
			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 中获取对应的 ClassMetadata AnnotationMetadata
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);

						// 先进行 excludeFilters includeFilters Condition 过滤
						if (isCandidateComponent(metadataReader)) {

							// 创建的是 ScannedGenericBeanDefinition
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setSource(resource);

							// 根据解析的 AnnotationMetadata 校验,必须满足
							// 1.isIndependent(顶层类或者静态内部类)
							// 2.是个具体的类或者是个含有标注了 Lookup 注解方法的抽象类
							
							// ...
							
						}
						else {
							// ...
						}
					}
					catch (Throwable ex) {
						// ...
					}
				}
				else {
					// ...
				}
			}
		}
		catch (IOException ex) {
			// ...
		}
		return candidates;
	}

	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())) {

				// Condition 过滤
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();

		// isIndependent(顶层类或者静态内部类)
		// && 是个具体的类或者是个含有标注了 Lookup 注解方法的抽象类
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}

重点聚焦于方法 scanCandidateComponents,流程总结:

  • 根据路径加载所有的类资源 Resource 集合

  • 遍历上述集合,基于 MetadataReaderFactory 获取对应的 MetadataReaderSpring 中一般使用的都是 CachingMetadataReaderFactory,它构造的是 SimpleMetadataReader

  • 先根据 excludeFilters includeFilters conditionEvaluator 过滤是否匹配

  • 如果通过,则构造对应的 ScannedGenericBeanDefinition,这里的构造很简单,只是额外 sbd.setSource(resource)

  • 再次进行过滤,此处的过滤逻辑为:

    • 1)目标类是否 TOP LEVEL CLASSNESTED CLASS并且
    • 2)目标类不能是一个接口或抽象类,或者是个含有被 Lookup 直接注解(元注解)标注方法的抽象类

总结

这是基于路径扫描 BeanDefinition 的常用类,比如 @ComponentScan 注解对应路径的扫描就是由它来完成的