单刀直入ComponentScan

何为单刀直入,为何单刀直入

以大家都知道的一个知识点,逐层剖析最终与spring的核心框架相遇,很多讲解spring源码的文章都是上来一大堆晦涩难懂的基础知识,结果让人望而却步,本系列文章旨在由一个每个人都知道的点并且平时都会用到的知识,剖析spring实现的方式,从而与spring诸多的概念相遇,从具体的应用再到概念的理解,面窄一些但是更容易懂一些。

今天说的就是 @interface ComponentScan

看到这个注解大家都不陌生,知道她是干啥的吧,就是扫描指定路径下的包含对应注解的类然后加入spring容器中,那么是如何实现的呢,今天我们一起来深度了解一下。
有一些基础的朋友可能知道,这种spring的自定义注解跟自定义标签类似,都是需要有一个对应的parse类来的,要不怎么解读她呢,就是要获取注解中指定的属性,那么这个类就要闪亮登场了,ComponentScanAnnotationParser,她具体的工作就是要解析ComponentScan注解中指定的属性值.

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

	Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
	boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
	scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
			BeanUtils.instantiateClass(generatorClass));

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

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

以上是parse类的源码,主要完成ComponentScan注解的解析,同时通过ClassPathBeanDefinitionScanner 根据注解中配置的属性进行扫描,这个parse类有一个承上启下的作用,向上要介绍spring在启动的时候是如何执行这个parse类的,我们知道包的扫描是spring启动时就开始的,向下要介绍,到底是如何进行包的扫描并且把扫描到的包加载到容器中的,我们接下来先说"向下"的过程。
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) {
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					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);
				}
			}
		}
		return beanDefinitions;
	}

我们在阅读代码的时候一定要阅读注释哈,可能有些英语不太好的朋友,可以通过微信扫一扫里面的翻译功能哈,比如我就一直用这个,哈哈,这里面有几句关键,Set candidates = findCandidateComponents(basePackage); 找到合适的组件,那么什么事合适的,我们都知道符合条件的是加了@Component @Controler @Service 等注解的类,就是在这里确定的,感兴趣的朋友可以自己继续向下看,也可以等我明天继续描述下,因为里面东西还不少,最终把臊面到的类准变味beanDefinition这个不陌生吧,然后通过beanRegister接口给注册进去的,这个可以自己看下。接下来说一说"向上"如何处理,一般这种启动注册bean的事都是谁干呢,可能有点基础的朋友都会想到 BeanDefinitionRegistryPostProcessor 他是BeanFactoryPostProcessor的子接口,感兴趣的可以继续看看BeanDefinitionRegistryPostProcessor 是怎么回事,spring是合适调用的他呢,这个想对简单点哈,AbstractApplicationContext这个类一般不会陌生吧,里面有个家喻户晓的方法refresh ,这个方法中有一个invokeBeanFactoryPostProcessors(beanFactory); 就是在这进行调用,的,然后通过层层调用最终调用到我们的ComponentScanAnnotationParser中的parse方法,完成上面说的注解的解析和扫描到符合条件的类,并最终注册到spring容器中的,里面涉及到的内容比较多,不放在一篇文章里了,接下来的文章会围绕着"向上"和“向下”来说,这里就主要把这条线串起来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值