Spring IoC 源码分析 (基于注解) (二) 之 包扫描,java面试sql题和答案

//初始化

this();

//扫描包、注册bean

scan(basePackages);

refresh();

}

上文我们分析了this()方法,会去初始化AnnotatedBeanDefinitionReader读取器和ClassPathBeanDefinitionScanner扫描器,并初始化扫描过滤规则。

接下来我们看一下scan(basePackages)方法:

一直跟踪下去,发现调用了ClassPathBeanDefinitionScanner类中的scan()方法

//调用类路径Bean定义扫描器入口方法

public int scan(String… basePackages) {

//获取容器中已经注册的Bean个数

int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

//启动扫描器扫描给定包

doScan(basePackages);

// Register annotation config processors, if necessary.

//注册注解配置(Annotation config)处理器

if (this.includeAnnotationConfig) {

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

}

//返回注册的Bean个数

return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);

}

可以看到主要是doScan(basePackages)方法实现了扫描的逻辑,我们继续跟踪进去看下

//类路径Bean定义扫描器扫描给定包及其子包

protected Set doScan(String… basePackages) {

Assert.notEmpty(basePackages, “At least one base package must be specified”);

//创建一个集合,存放扫描到Bean定义的封装类

Set beanDefinitions = new LinkedHashSet<>();

//遍历扫描所有给定的包

for (String basePackage : basePackages) {

//调用父类ClassPathScanningCandidateComponentProvider的方法

//扫描给定类路径,获取符合条件的Bean定义

Set candidates = findCandidateComponents(basePackage);

//遍历扫描到的Bean

for (BeanDefinition candidate : candidates) {

//获取@Scope注解的值,即获取Bean的作用域

ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);

//为Bean设置作用域

candidate.setScope(scopeMetadata.getScopeName());

//为Bean生成名称

String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

//如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值,

//设置Bean的自动依赖注入装配属性等

if (candidate instanceof AbstractBeanDefinition) {

postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);

}

//如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解

if (candidate instanceof AnnotatedBeanDefinition) {

//处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过

AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);

}

//根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突

if (checkCandidate(beanName, candidate)) {

BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);

//根据注解中配置的作用域,为Bean应用相应的代理模式

definitionHolder =

AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

beanDefinitions.add(definitionHolder);

//向容器注册扫描到的Bean

registerBeanDefinition(definitionHolder, this.registry);

}

}

}

return beanDefinitions;

}

这一大段代码基本上就是spring扫描识别注解,并注册Bean到IOC容器中的代码。

在第10行有一个findCandidateComponents(basePackage)方法,这个方法里就是具体的扫描逻辑。

继续跟踪:

ClassPathScanningCandidateComponentProvider类

//扫描给定类路径的包

public Set findCandidateComponents(String basePackage) {

//spring5.0开始 索引 开启的话生成文件META-INF/spring.components 后面加载直接从本地文件读取(一般不建议开启 spring.index.ignore=true)

if (this.componentsIndex != null && indexSupportsIncludeFilters()) {

return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);

}

else {

return scanCandidateComponents(basePackage);

}

}

这里有一个if判断,我们默认走的是else里的分支,即scanCandidateComponents(basePackage)方法。

private Set scanCandidateComponents(String basePackage) {

Set candidates = new LinkedHashSet<>();

try {

//补全扫描路径,扫描所有.class文件 classpath*:com/mydemo/**/*.class

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 {

//通过ASM获取class元数据,并封装在MetadataReader元数据读取器中

MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);

//判断该类是否符合@CompoentScan的过滤规则

//过滤匹配排除excludeFilters排除过滤器(可以没有),包含includeFilter中的包含过滤器(至少包含一个)。

if (isCandidateComponent(metadataReader)) {

//把元数据转化为 BeanDefinition

ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);

sbd.setResource(resource);

sbd.setSource(resource);

//判断是否是合格的bean定义

if (isCandidateComponent(sbd)) {

if (debugEnabled) {

logger.debug("Identified candidate component class: " + resource);

}

//加入到集合中

candidates.add(sbd);

}

else {

//不合格 不是顶级类、具体类

if (debugEnabled) {

logger.debug("Ignored because not a concrete top-level class: " + resource);

}

}

}

else {

//不符@CompoentScan过滤规则

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;

}

这里就是主要的扫描逻辑,代码中的注释已经说的很清楚了。

主要过程:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

Kafka进阶篇知识点

image

Kafka高级篇知识点

image

44个Kafka知识点(基础+进阶+高级)解析如下

image

由于篇幅有限,小编已将上面介绍的**《Kafka源码解析与实战》、Kafka面试专题解析、复习学习必备44个Kafka知识点(基础+进阶+高级)都整理成册,全部都是PDF文档**

得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)**
[外链图片转存中…(img-WfY2VLbo-1711786544082)]

Kafka进阶篇知识点

[外链图片转存中…(img-YndYn5uL-1711786544082)]

Kafka高级篇知识点

[外链图片转存中…(img-iBUV5epG-1711786544083)]

44个Kafka知识点(基础+进阶+高级)解析如下

[外链图片转存中…(img-jon2Yupi-1711786544083)]

由于篇幅有限,小编已将上面介绍的**《Kafka源码解析与实战》、Kafka面试专题解析、复习学习必备44个Kafka知识点(基础+进阶+高级)都整理成册,全部都是PDF文档**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值