Spring5 源码分析-容器刷新-@ComponentScan

上一篇:Spring5 源码分析-容器刷新-@PropertySourcejava

功能说明

@ComponentScan能够完成对指定路径下的类文件扫描,将符合Spring规则(@Component @Service @Controller @Repository @Named(JSR-250))或者自定义规则(自定义的FilterType)的类定义注册到容器中(即将各个类的BeanDefinition添加到DefaultListableBeanFactory.beanDefinitionMaps当中)。能够用@ComponentScan的属性指定多个代理模式、是否懒加载、ResourcePattern、includeFilters 、 excludeFitlersapp

@ComponentScan属性ide

    value和basePackages效果同样,都是用来指定扫描的包路径源码分析

    basePackageClasses:添加指定class所在包路径做为扫描包路径post

    nameGenerator:指定beanName名称生成器,通常不会去修改它测试

    scopeResolver:指定Scope元数据提取器,通常不会去修改它this

    scopedProxy:指定Bean的代理模式,默认为DEFAULT,其效果和NO同样,还有TARGET_CLASS(CGLIB)和INTERFACES(JDK动态代理)lua

    resoucePattern:过滤包路径,好比包扫描路径是com.jv.scan,你能够设置一个pattern,将com.jv.scan.inner目录过滤掉,结合includeFilters和excludeFilters能够实现更灵活的包扫描spa

    useDefaultFilters:默认为true,谁也不会去改这个值,由于它是Spring支持@Component注解的关键点.net

    lazyInit:默认为false,若是你想全局设置为懒加载,则能够设置为true

    includeFilters:符合Filter指定规则的类会被扫描

    excludeFilters:符合Filter指定规则的类不会被扫描

注:对于能够在Bean上面单独设置的属性,若是针对某一个类单独设置了,在@ComponentScan上设置的会被覆盖掉。好比lazyInit和scopeProxyMode

举例Demo

    这个简单,任何一个Spring项目都会在配置类上添加@ComponentScan注解,至于上面的各个属性也很方便测试,不复杂。因为时间关系,没有将测试的代码一个一个整理并贴出来。里面可能会比较经常使用的就是使用value,basePackages,basePackageClasses去指定扫描包路径,还有设置includeFilters和excludeFilters用以控制自定义的扫描规则

    注:若是没有指定任何的包扫描路径,则使用的是配置类所在包的路径

源码分析

方法按照以下顺序refresh()->invokeBeanFactoryPostProcessors()->ConfigurationClassPostProcessor->postProcessBeanDefinitionRegistry()-ConfigurationClassParse.doProcessConfigurationClass()...ComponentScanAnnotationParse.parse()...ClassPathBeanDefinitionScanner.doScan

在ConfigurationClassParse.doProcessConfigurationClass方法中的代码段

// Process any @ComponentScan annotations  处理@ComponentScan注解,完成符合Spring规则类定义注册
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately  当即执行扫描
				//不会扫描到本身(@Configuration修饰了的类)
				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
				// 检查扫描到全部类中是否还有被@Configuration修饰的类,若是还有,则须要再按照配置类的流程处理一遍
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						//若是扫描的包路径下还有被@Configuration修饰的类,则递归进行处理
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

ComponentScanAnnotationParse.parse方法

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
		/**
		 * 这个初始化包含了两个很是重要的功能
		 * 1.添加默认的@Component @Named AnnotationFilterType,是Spring能够扫描@Component @Service @Controller @Configuration的关键
		 * 2.设置参数对象environment
		 */

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

		//默认不使用代理,能够设置为JDK的动态代理或者基于CGLIB的动态代理
		ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
		if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
			scanner.setScopedProxyMode(scopedProxyMode);
		}
		else {
			//ScopeMetadataResolver 从BeanDefinition中将类的Scope元数据提取出来并返回
			Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
			scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
		}
		//获取类文件过滤模式,默认是处理全部类路径下的全部class文件,能够自定义pattern只让某一个目录下的class文件有效
		scanner.setResourcePattern(componentScan.getString("resourcePattern"));
		//添加includeFilter过滤器,Spring会处理符合过滤器规则的class
		for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
				scanner.addIncludeFilter(typeFilter);
			}
		}
		//添加excludeFilter过滤器,Spring会排出符合过滤器规则的class
		for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
				scanner.addExcludeFilter(typeFilter);
			}
		}
		//全部的class的BeanDefinition的lazyInit都会被设置为true,意思就是全部的都会在调用getBean的时候才会被实例化
		boolean lazyInit = componentScan.getBoolean("lazyInit");
		if (lazyInit) {
			scanner.getBeanDefinitionDefaults().setLazyInit(true);
		}

		Set<String> basePackages = new LinkedHashSet<>();
		String[] basePackagesArray = componentScan.getStringArray("basePackages");
		//添加扫描包路径-由basePackages指定的相对路径
		for (String pkg : basePackagesArray) {
			String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
			Collections.addAll(basePackages, tokenized);
		}
		//添加扫描包路径-由basePackageClasses类所在的包路径
		for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
			basePackages.add(ClassUtils.getPackageName(clazz));
		}
		//若是前面二者都没有指定,则使用当前处理类的所在包路径。。。估计这也是为何SpringBoot默认是扫描的启动类所在目录的缘由
		if (basePackages.isEmpty()) {
			basePackages.add(ClassUtils.getPackageName(declaringClass));
		}
		//添加一个默认ExcludeFilter,用于再也不二次处理配置类
		scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
			@Override
			protected boolean matchClassName(String className) {
				return declaringClass.equals(className);
			}
		});
		//扫描
		return scanner.doScan(StringUtils.toStringArray(basePackages));
	}

ClassPathBeanDefinitionScanner.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) {
			//为被扫描到的全部类生成BeanDefinition
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			//遍历全部的候选者并进行处理最终注册到容器中
			for (BeanDefinition candidate : candidates) {
				//提取Scope元数据
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());

				//生成Spring规则的beanName
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					//设置全局默认值,好比lazyInit=false(默认为false,能够在@ComponentScan中设置为true),autowireMode,InitMethod等
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					/**
					 * 这里很重要,建立BeanDefinition的ProxyDefinition
					 * 1.若是ScopedProxyMode是DEFAULT或者NO, 则不会建立ProxyDefinition
					 * 2.若是ScopedProxyMode不是TARGET_CLASS,则建立的ProxyDefinition会用于JDK动态代理
					 * 3.若是ScopedProxyMode是TARGET_CLASS,则建立的ProxyDefinition会用于CGLIB动态代理
					 */

					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					//注册BeanDefinition
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

下一篇:Spring5 源码分析-容器刷新-@Import(普通类)

相关文章
相关标签/搜索