Spring IoC 源码分析 (基于注解) (二) 之 包扫描

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;

}

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

主要过程:

  • 根据包路径,扫描所有.class文件

  • 根据包路径,生成.class对应的Resource对象

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

  • 判断该类是否符合过滤规则

  • 判断该类是否为独立的类、具体的类

  • 加入到集合中

我们来详细看下过滤的方法 isCandidateComponent(metadataReader)

//判断元信息读取器读取的类是否符合容器定义的注解过滤规则

//@CompoentScan的过滤规则支持5种 (注解、类、正则、aop、自定义)

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {

//如果读取的类的注解在排除注解过滤规则中,返回false

for (TypeFilter tf : this.excludeFilters) {

if (tf.match(metadataReader, getMetadataReaderFactory())) {

return false;

}

}

//如果读取的类的注解在包含的注解的过滤规则中,则返回ture

for (TypeFilter tf : this.includeFilters) {

//判断当前类的注解是否match规则

if (tf.match(metadataReader, getMetadataReaderFactory())) {

//是否有@Conditional注解,进行相关处理

return isConditionMatch(metadataReader);

}

}

//如果读取的类的注解既不在排除规则,也不在包含规则中,则返回false

return false;

}

接着跟踪 tf.match()方法

AbstractTypeHierarchyTraversingFilter类

public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)

throws IOException {

// This method optimizes avoiding unnecessary creation of ClassReaders

// as well as visiting over those readers.

//检查当前类的注解是否符合规律规则

if (matchSelf(metadataReader)) {

return true;

}

//check 类名是否符合规则

ClassMetadata metadata = metadataReader.getClassMetadata();

if (matchClassName(metadata.getClassName())) {

return true;

}

//如果有继承父类

if (this.considerInherited) {

String superClassName = metadata.getSuperClassName();

if (superClassName != null) {

// Optimization to avoid creating ClassReader for super class.

Boolean superClassMatch = matchSuperClass(superClassName);

if (superClassMatch != null) {

if (superClassMatch.booleanValue()) {

return true;

}

}

else {

// Need to read super class to determine a match…

try {

if (match(metadata.getSuperClassName(), metadataReaderFactory)) {

return true;

}

}

catch (IOException ex) {

logger.debug(“Could not read super class [” + metadata.getSuperClassName() +

“] of type-filtered class [” + metadata.getClassName() + “]”);

}

}

}

}

//如果有实现接口

if (this.considerInterfaces) {

for (String ifc : metadata.getInterfaceNames()) {

// Optimization to avoid creating ClassReader for super class

Boolean interfaceMatch = matchInterface(ifc);

if (interfaceMatch != null) {

if (interfaceMatch.booleanValue()) {

return true;

}

}

else {

// Need to read interface to determine a match…

try {

if (match(ifc, metadataReaderFactory)) {

return true;

}

}

catch (IOException ex) {

logger.debug(“Could not read interface [” + ifc + “] for type-filtered class [” +

metadata.getClassName() + “]”);

}

}

}

}

return false;

}

这里面最主要的是 matchSelf(metadataReader) 方法

AnnotationTypeFilter类

protected boolean matchSelf(MetadataReader metadataReader) {

//获取注解元数据

AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();

//check 注解及其派生注解中是否包含@Component

//获取当前类的注解 metadata.hasAnnotation @Controller

//获取当前类的注解及其派生注解 metadata.hasAnnotation @Controller包含的@Component@Documented等等

return metadata.hasAnnotation(this.annotationType.getName()) ||

(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));

}

在这段代码代码中,可以解决我们之前的疑惑“Spring是怎么发现@Configuration、@Controller、@Service这些注解修饰的类的?

原来@Configuration、@Controller、@Service这些注解其实都是@Component的派生注解,我们看这些注解的代码会发现,都有@Component注解修饰。而spring通过metadata.hasMetaAnnotation()方法获取到这些注解包含@Component,所以都可以扫描到。如下:

然后我们再看回 scanCandidateComponents(basePackage)方法,接下来有一个 isCandidateComponent(sbd)方法,如下:

//是否是独立的类、具体的类

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {

AnnotationMetadata metadata = beanDefinition.getMetadata();

return (metadata.isIndependent() && (metadata.isConcrete() ||

(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));

}

这个方法的作用是,判断该类是否为

  • 顶层的类(没有父类或静态内部类)

  • 具体的类(不是抽象类或接口)

至此,ClassPathBeanDefinitionScanner类中的doScan(basePackages)方法中的findCandidateComponents(basePackage)方法已经结束了,即我们的包扫描也结束了,已经把扫描到的类存入到了集合中,结下来就是解析注册Bean的过程了。

总结: 通过这篇文章,我们可以回答之前的一些问题了:

  • Spring是怎么发现@Bean、@Controller、@Service这些注解修饰的类的? 通过 matchSelf(metadataReader)方法,判断这些注解中是否包含@Component

最后

即使是面试跳槽,那也是一个学习的过程。只有全面的复习,才能让我们更好的充实自己,武装自己,为自己的面试之路不再坎坷!今天就给大家分享一个Github上全面的Java面试题大全,就是这份面试大全助我拿下大厂Offer,月薪提至30K!

我也是第一时间分享出来给大家,希望可以帮助大家都能去往自己心仪的大厂!为金三银四做准备!
一共有20个知识点专题,分别是:

Dubbo面试专题

JVM面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Java并发面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Kafka面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MongDB面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MyBatis面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

MySQL面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Netty面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

RabbitMQ面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Redis面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

Spring Cloud面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

SpringBoot面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

zookeeper面试专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

常见面试算法题汇总专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

计算机网络基础专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

设计模式专题

这个GItHub上的Java项目开源了,2020最全的Java架构面试复习指南

[外链图片转存中…(img-q8799SC8-1714456399766)]

Redis面试专题

[外链图片转存中…(img-1MtRygb1-1714456399766)]

Spring Cloud面试专题

[外链图片转存中…(img-V6R0OGw3-1714456399766)]

SpringBoot面试专题

[外链图片转存中…(img-308kjDyq-1714456399767)]

zookeeper面试专题

[外链图片转存中…(img-OsYZ2qaR-1714456399767)]

常见面试算法题汇总专题

[外链图片转存中…(img-ieoPnaHd-1714456399767)]

计算机网络基础专题

[外链图片转存中…(img-Bv00kxYk-1714456399767)]

设计模式专题

[外链图片转存中…(img-HTxqCeiz-1714456399768)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 30
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值