自学内容网 自学内容网

Spring 5.x 源码之ClassPathBeanDefinitionScanner

Spring 5.x 源码之ClassPathBeanDefinitionScanner

AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的初始化是spring上线文初始化的起点,很多预加载的类会在spring接下来的初始化中发挥重要作用;

下面就是重点看看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) {
// TODO 这个是重点,会把该包下面所有的Bean都扫描进去
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// TODO 拿到Scope元数据:此处为singleton
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// TODO 生成Bean的名称,默认为首字母小写. 此处为"myTestService"
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// TODO 此处为扫描的Bean ,为ScannedGenericBeanDefinition ,因为继承GenericBeanDefinition的父类AbstractBeanDefinition, 实现AnnotatedBeanDefinition所以为true
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
// TODO 也是完善比如Bean上的一些注解信息:比如@Lazy、@Primary、@DependsOn、@Role、@Description   @Role注解用于Bean的分类分组,没有太大的作用
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);
// TODO 注意 注意 注意:这里已经吧Bean注册进去工厂了,所有doScan()方法不接收返回值,也是没有任何问题的
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
        return scanCandidateComponents(basePackage);
    }
}
scanCandidateComponents:根据basePackage扫描候选的组件们(非常重要)
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
// 1.根据指定包名 生成包搜索路径
//通过观察resolveBasePackage()方法的实现, 我们可以在设置basePackage时, 使用形如${}的占位符, Spring会在这里进行替换 只要在Enviroment里面就行
// 本次值为:classpath*:com/fsx/config/**/*.class
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;

//2. 资源加载器 加载搜索路径下的 所有class 转换为 Resource[]
// 拿着上面的路径,就可以getResources获取出所有的.class类,这个很强大~~~
// 真正干事的为:PathMatchingResourcePatternResolver#getResources方法
// 此处能扫描到两个类AppConfig(普通类,没任何注解标注)和RootConfig。所以接下里就是要解析类上的注解,以及过滤掉不是候选的类(比如AppConfig)

// 注意:这里会拿到类路径下(不包含jar包内的)的所有的.class文件 可能有上百个,然后后面再交给后面进行筛选~~~~~~~~~~~~~~~~(这个方法,其实我们也可以使用)
// 当然和getResourcePatternResolver和这个模版有关
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

// 记录日志(下面我把打印日志地方都删除)
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();

// 接下来的这个for循环:就是把一个个的resource组装成
for (Resource resource : resources) {
//文件必须可读 否则直接返回空了
if (resource.isReadable()) {
try {

//读取类的 注解信息 和 类信息 ,两大信息储存到  MetadataReader
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);

// 根据TypeFilter过滤排除组件。因为AppConfig没有标准@Component或者子注解,所以肯定不属于候选组件  返回false
// 注意:这里一般(默认处理的情况下)标注了默认注解的才会true,什么叫默认注解呢?就是@Component或者派生注解。还有javax....的,这里省略啦
if (isCandidateComponent(metadataReader)) {

//把符合条件的 类转换成 BeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);

// 再次判断 如果是实体类 返回true,如果是抽象类,但是抽象方法 被 @Lookup 注解注释返回true (注意 这个和上面那个是重载的方法) 
// 这和上面是个重载方法  个人觉得旨在处理循环引用以及@Lookup上
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
}
} 
}
}
}
return candidates;
}

// 备注:此时ComponentScan这个注解还并没有解析

MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
new CachingMetadataReaderFactory().getMetadataReader(resource);
super.getMetadataReader(resource);
org.springframework.core.type.classreading.SimpleMetadataReaderFactory#getMetadataReader(org.springframework.core.io.Resource)
public MetadataReader getMetadataReader(Resource resource) throws IOException {
return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);

CachingMetadataReaderFactory(); 读取工厂类
SimpleMetadataReader读取器
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);

ClassPathBeanDefinitionScanner#postProcessBeanDefinition
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
beanDefinition.applyDefaults(this.beanDefinitionDefaults);
if (this.autowireCandidatePatterns != null) {
beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
}
}

public void applyDefaults(BeanDefinitionDefaults defaults) {
setLazyInit(defaults.isLazyInit());
setAutowireMode(defaults.getAutowireMode());
setDependencyCheck(defaults.getDependencyCheck());
setInitMethodName(defaults.getInitMethodName());
setEnforceInitMethod(false);
setDestroyMethodName(defaults.getDestroyMethodName());
setEnforceDestroyMethod(false);
}

理解ClassPathBeanDefinitionScanner的工作原理,可以帮助理解Spring IOC 容器的初始化过程。
同时对理解MyBatis 的 Mapper 扫描 也是有很大的帮助。
因为 MyBatis 的MapperScannerConfigurer的底层实现也是一个ClassPathBeanDefinitionScanner的子类。就像我们自定义扫描器那样,自定定义了 过滤器的过滤规则。


原文地址:https://blog.csdn.net/lvyuanj/article/details/137450378

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!