上一篇: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
这个简单,任何一个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; }