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面试专题
Java并发面试专题
Kafka面试专题
MongDB面试专题
MyBatis面试专题
MySQL面试专题
Netty面试专题
RabbitMQ面试专题
Redis面试专题
Spring Cloud面试专题
SpringBoot面试专题
zookeeper面试专题
常见面试算法题汇总专题
计算机网络基础专题
设计模式专题
[外链图片转存中…(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)]