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元数据:此处为singletonScopeMetadata 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所以为trueif (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/**/*.classString 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 {//读取类的 注解信息 和 类信息 ,两大信息储存到 MetadataReaderMetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);// 根据TypeFilter过滤排除组件。因为AppConfig没有标准@Component或者子注解,所以肯定不属于候选组件 返回false// 注意:这里一般(默认处理的情况下)标注了默认注解的才会true,什么叫默认注解呢?就是@Component或者派生注解。还有javax....的,这里省略啦if (isCandidateComponent(metadataReader)) {//把符合条件的 类转换成 BeanDefinitionScannedGenericBeanDefinition 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的子类。就像我们自定义扫描器那样,自定定义了 过滤器的过滤规则。