“在工作中我们可能需要在程序启动的时候扫描特定的注解,做一些初始化或预加载的工作,Spring 中各种配置类、Bean的加载都离不开注解类的解析。本文带你分析此类的工作原理”
Spring中注解扫描是以 ClassPathScanningCandidateComponentProvider
类为基础,本文带你分析此类的工作原理。
首先我们看构造函数,useDefaultFilters 一般需要扫描自定义的包需要传 false,然后创建一个 StandardEnvironment,这个我们用不上可以直接忽略。
public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) {
this(useDefaultFilters, new StandardEnvironment());
}
然后下面这两个接口可以添加 TypeFilter
,通过 TypeFilter 的 match 方法来确定扫描的类是否符合条件。
public void addIncludeFilter(TypeFilter includeFilter) {
this.includeFilters.add(includeFilter);
}
public void addExcludeFilter(TypeFilter excludeFilter) {
this.excludeFilters.add(0, excludeFilter);
}
比较常用的就是 AnnotationTypeFilter
,里面包含一个属性 Class 即你需要匹配的注解类。AnnotationTypeFilter 继承 AbstractTypeHierarchyTraversingFilter,看名字中 Hierarchy 我们知道,此抽象类实现了层次的查找
,不仅会查找当前类,还会查找他的父类或接口。
AnnotationTypeFilter 只能匹配类上
的注解,比如你需要匹配方法上的注解,你可以继承 AbstractTypeHierarchyTraversingFilter 来自定义自己的匹配规则。
然后调用ClassPathScanningCandidateComponentProvider.findCandidateComponents 方法开始扫包。调用流程如下:
①:入参是你需要扫描的包,例如:com.codermeng
②:会将上一步入参 basePackage 转换成带协议前缀和类通配符的路径,例如:classpath*:
com/codermeng/**/*.class
。getResources 返回一个 List 包含所有符合条件的 class 文件相对路径,例如:/Users/xxx/target/test-classes/com/codermeng/ClassScanTest.class。
③:这一步就会遍历 Resource List ,对每个 Resource 进行字节码解析
。解析完之后会根据配置的 excludeFilter
和 includeFilter
对每个 class 的进行过滤,符合条件的会封装成 BeanDefinition 返回。
ClassReader 进行字节码解析完会返回一个 AnnotationMetadata,里面有方法判断注解是否在类上存在,是否在方法上存在,此接口常用方法摘抄如下:
// 判断类上是否存在此名称的 Annotation
boolean hasAnnotation(String annotationName);
// 判断类上是否存在此名称的 MetaAnnotation
boolean hasMetaAnnotation(String metaAnnotationName);
// 判断类的方法上是否存在此名称的 Annotation
boolean hasAnnotatedMethods(String annotationName);
// 获取包含此 Annotation 的方法
Set<MethodMetadata> getAnnotatedMethods(String annotationName);
微信搜索 CoderMeng
关注获取更多干货文章
大家觉得写的不错欢迎点赞
、评论
、分享
支持。
推荐阅读
CoderMeng:JVM锁粗化和循环zhuanlan.zhihu.com