入口:在解析配置类时,处理@ComponentScans注解,就会调用doScan方法!
关键类及方法:ClassPathBeanDefinitionScanner-->scan-->doScan
下面分析doScan方法
1.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);
// 处理每个candidate
for (BeanDefinition candidate : candidates) {
// 解析scope注解
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);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// 解析@Lazy、@Primary、@DependsOn、@Role、@Description,设置到beanDefinition中
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 检查Spring容器中是否已经存在该beanName
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;
}
1.findCandidateComponents,后面详细讲这个方法
传入包路径,扫描,并进行条件匹配,得到BeanDefinition,然后将BeanDefinition放到candidates集合中,将其作为candidates(候选),后面继续筛选。
它中包含两个分支,一是addCandidateComponentsFromIndex,表示使用Spring.components文件加快扫描速度;二是多数进入的分支,方法是scanCandidateComponents。两者的内部实现基本相同。
2.拿到所有BeanDefinition(for循环),然后去解析
3.resolveScopeMetadata
解析scope注解,将其设置到BeanDefinition的属性中
4.generateBeanName
首先解析@Component注解有没有指定bean的名字,如果没有指定,就返回null;
然后生成默认的名字,将命名首字母小写(属于JDK部分)作为beanName。当第一个字母和第二个字母都是大写时,则直接使用该类名作为beanName。
5.postProcessBeanDefinition
继续设置BeanDefinition的默认值,比如,初始化方法、懒加载属性等。
6.processCommonDefinitionAnnotations
继续解析注解,设置到BeanDefinition对应的属性上,包括,@Lazy、@Primary、@DependsOn、@Role、@Description
7.checkCandidate(beanName, candidate)
检查Spring容器中是否已经存在该beanName,验证是否兼容。
如果兼容,返回false表示不会重新注册到Spring容器中,如果不兼容则会抛异常。(说明存在相同BeanName的Bean)。
判断兼容的方法是isCompatible,判断扫描的bean是否兼容,如果扫描的类文件完全相同(通过resource来判断),则bean是兼容的。
8.registerBeanDefinition
注册<beanName,BeanDefinition>到BeanDefinitionMap中去
2.findCandidateComponents方法分析
核心:找到所有候选bean!
流程说明:核心方法是scanCandidateComponents,通过扫描得到BeanClassName,这些都可以生成BeanDefinition。
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);//
}
}
它中包含两个分支,一是addCandidateComponentsFromIndex,表示使用Spring.components文件加快扫描速度;二是多数进入的分支,方法是scanCandidateComponents。两者的内部实现基本相同。
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 = getMetadataReaderFactory().getMetadataReader(resource);
// excludeFilters、includeFilters判断
if (isCandidateComponent(metadataReader)) { // @Component-->includeFilters判断 过滤器进行判断
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);// 构造beanDefinition
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);// 将可以成为bean的beanDefition加入候选
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
1.getResources
找到所有的.class文件(找到所有的类)。
2.metadataReader
元数据读取器:读取注解的信息、类的信息(类的名字、父子类信息)等,底层用的ASM技术。(此时并没有将这个类加载到JVM)
3.isCandidateComponent(metadataReader)
判断当前类是否可以跳过(shouldSkip)。
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
// 符合includeFilters的会进行条件匹配,通过了才是Bean,也就是先看有没有 @Component,再看是否符合@Conditional
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
在Spring容器启动的时候,已经设定了excludeFilters和includeFilters(排除过滤器和包含过滤器),此时进行过滤。(一般这个时候只会判断@Component注解,spring启动时只将这一个注解设置了,如果用户没有特殊指定其他的Filter属性)。
通过过滤器过滤后,还需要进行条件匹配(条件注解)。(类加了@Conditional 或者 该类实现了Condition接口),如果类实现了Condition接口,并重写了match方法,那么就会调用match方法进行过滤。
4.ScannedGenericBeanDefinition
构造BeanDefinition。
setBeanClassName(this.metadata.getClassName());这里只是把className设置到BeanDefinition中(此时并不是真正的类,后面创建bean的时候,才会把创建的对象赋值给这个属性)。
5.isCandidateComponent(sbd)
判断该类是否能成为Bean。
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
1)isIndependent:(是否独立)内部类不能成为一个Bean;
2)isConcrete:(isInterface() || isAbstract())接口和抽象类不能成为Bean
3)是抽象类且类中存在使用@Lookup注解的方法, 则该类可以成为Bean,会将Lookup("XXX")中的XXX扫描成为BeanDefinition,并获取该XXX的bean。