Spring基于注解的扫描底层流程
调用流程(基于调试记录下来的)
1.refresh方法下的invokeBeanFactoryPostProcessors(beanFactory);
2.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
3.invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);【ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法】
4.processConfigBeanDefinitions(registry);
5.parser.parse(candidates);【parser是ConfigurationClassParser类对@ComponentScan @Configuration支持】
6.processConfigurationClass(new ConfigurationClass(metadata, beanName));
7.sourceClass = doProcessConfigurationClass(configClass, sourceClass);
8.Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
9.scanner.doScan(StringUtils.toStringArray(basePackages));【scanner指ComponentScanAnnotationParser类】
10.Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
11.scanCandidateComponents(basePackage);
核心方法分析
doProcessConfigurationClass方法分析
protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first // 处理内部类 // 在解析一个配置类时,如果类上有@Component,则会判断内部类是不是lite配置类并进行解析,并且会记录为被导入的 processMemberClasses(configClass, sourceClass, filter); } // 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"); } } // Process any @ComponentScan annotations // 会进行扫描,得到的BeanDefinition会注册到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 // 这里就会进行扫描,得到的BeanDefinition会注册到Spring容器中 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(); } // 检查扫描出来的BeanDefinition是不是配置类(full和lite) if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations // getImports(sourceClass)会拿到@Import导入的类 // 如果导入的是普通类,那么会直接把它当做配置类来解析 // 如果导入的是普通ImportSelector,那么会将返回的类再次调用processImports() // 如果导入的是特殊ImportSelector,DeferredImportSelector,那么暂时不会处理,会在解析完所有当前这轮配置类后进行导入,将返回的类再次调用processImports() // 如果导入的是ImportBeanDefinitionRegistrar,那么暂时不会处理,会在解析完所有当前这轮配置类后,将配置类解析成为BeanDefinition之后进行调用 processImports(configClass, sourceClass, getImports(sourceClass), filter, true); // 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); } } // Process individual @Bean methods // 解析配置类中的@Bean,但并没有真正处理@Bean,只是暂时找出来 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces // 解析配置类所实现的接口中的@Bean,但并没有真正处理@Bean,只是暂时找出来 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; }
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[] resources = getResourcePatternResolver().getResources(packageSearchPath); for (Resource resource : resources) { if (resource.isReadable()) { try { //包装了类的基本信息的对象 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); //如果类上面有includeFilters注解 if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } } } catch (Throwable ex) {} } } } catch (IOException ex) {} return candidates; }
汇总说明
1. 首先,通过ResourcePatternResolver获得指定包路径下的所有 .class 文件(Spring源码中将此文件包装成了Resource对象)
2. 遍历每个Resource对象
3. 利用MetadataReaderFactory解析Resource对象得到MetadataReader(在Spring源码中MetadataReaderFactory具体的实现类为CachingMetadataReaderFactory,MetadataReader的具体实现类为SimpleMetadataReader)
4. 利用MetadataReader进行excludeFilters和includeFilters,以及条件注解@Conditional的筛选(条件注解并不能理解:某个类上是否存在@Conditional注解,如果存在则调用注解中所指定的类的match方法进行匹配,匹配成功则通过筛选,匹配失败则pass掉。)
5. 筛选通过后,基于metadataReader生成ScannedGenericBeanDefinition
6. 再基于metadataReader判断是不是对应的类是不是接口或抽象类【能作为Bean的不能是抽象的或者是接口,需要是独立的,能产生对象的】
7. 如果筛选通过,那么就表示扫描到了一个Bean,将ScannedGenericBeanDefinition加入结果集
额外部分说明
MetadataReader类分析
MetadataReader表示类的元数据读取器,主要包含了一个AnnotationMetadata,功能有
1. 获取类的名字、
2. 获取父类的名字
3. 获取所实现的所有接口名
4. 获取所有内部类的名字
5. 判断是不是抽象类
6. 判断是不是接口
7. 判断是不是一个注解
8. 获取拥有某个注解的方法集合
9. 获取类上添加的所有注解信息
10. 获取类上添加的所有注解类型集合
值得注意的是,CachingMetadataReaderFactory解析某个.class文件得到MetadataReader对象是利用ASM技术,并没有加载这个类到JVM。并且,最终得到的ScannedGenericBeanDefinition对象,beanClass属性存储的是当前类的名字,而不是class对象。(beanClass属性的类型是Object,它即可以存储类的名字,也可以存储class对象)【这也就是为什么下一步会有加载类这个步骤】
doScan方法分析
代码展示(删减了部分日志记录与异常抛出部分)
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { //扫描到有注解的类并封装成BeanDefinition对象 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) { //支持了@Lazy @DependOn注解 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); //这里不重要 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); //BeanDefinition注册 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
汇总说明
1.基于findCandidateComponents方法获得的ScannedGenericBeanDefinition的Set集合
2.通过扫描BeanDefinition的Metadata属性获取元数据上的信息,包括一些注解进行额外处理,如@Lazy @DependOn注解
3.然后把处理过的BeanDefinition注册到容器当中
额外部分说明
ComponentScanAnnotationParser类作为扫描器怎么具备注册功能?
它中的属性 registry(private final BeanDefinitionRegistry registry;【BeanDefinitionRegistry虽然是接口,基于多态的性质,填充的具体实现类是DefaultListableBeanFactory类,也就是我们常说的Spring容器的具体对象】)
这个属性对象具备了注册的功能:
它会调用 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); 注册BeanDefinition到容器。
它会调用 registry.registerAlias(beanName, alias); 注册BeanDefinition的别名。
BeanDefinitionHolder的作用与意义(个人理解)
BeanDefinition本身并不会存储 beanName与aliases(别名),它所存储的是我们扫描出来的某个类对象元数据的一些信息,而且它也是一个接口,具体的实现类很多,采用 BeanDefinitionHolder 更像是一种装饰器模式的思维,用于存储BeanDefinition本身信息之外,还存储一些额外信息。
重复扫描会不会带来问题?
是不会的,因为 if (checkCandidate(beanName, candidate)) 这一步存在判断。
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) { return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean (newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) || // scanned same file twice newDefinition.equals(existingDefinition)); // scanned equivalent class twice }
判断来源是否一致,是否和已存在的兼容。如果是的话,返回false,其实就会避开再次注册。
doProcessConfigurationClass