1.上下文刷新时,SpringContext将调用BeanFactory后置处理器扫描Class

ConfigurationClassPostProcessor // BeanFactory后置处理器
       postProcessBeanFactory   // 上下文刷新时调用该方法
         processConfigBeanDefinitions
           ConfigurationClassParser parse.parse(main.class)   // Parse each @Configuration class

SpringBoot/SpringContext初始化加载过程可参考逻辑图:
SpringApplication启动过程
通过搜索“ConfigurationClassPostProcessor”找到对应节点

2.parse解析方法将解析类声明的各种注解

ConfigurationClassParser
       parse
         processConfigurationClass  // 递归地处理配置类及其超类层次结构
        do {
               sourceClass = doProcessConfigurationClass { // 返回父类Class, 没有则返回null
                     processMemberClasses // 先递归地处理任何(嵌套)类
                     processPropertySource // 处理注解@PropertySource
                      scannedBeanDefinitions = componentScanParser.parse // 处理注解@ComponentScan
                     for(BeanDefinitionHolder holder : scannedBeanDefinitions) {
                         parse(holder) // 递归
                     }
                     processImports // 处理注解@Import
                     configClass.addImportedResource // 处理注解@ImportResource
                     configClass.addBeanMethod // 处理@Bean声明的方法
                     processInterfaces // 处理接口上的默认方法
                     return sourceClass.getSuperClass // 返回父类给sourceClass
               }
        } while(sourceClass != null)

3.处理注解@ComponentScan, 并调用scanner扫描Class

ComponentScanAnnotationParser componentScanParser.parse
       ClassPathBeanDefinitionScanner **scanner.doScan**
doScan调用
this.resourcePatternResolver.getResources(packageSearchPath)来所搜索指定包下全部的class
搜索路径示例: classpath*:com/simple/boot/**/*.class
然后进行过滤, 组装成一个个BeanDefinition后注册到BeanFactory

ClassPathBeanDefinitionScanner 在Dubbo、MyBatis与Spring的集成中有应用到, 可以参考如下类:

// Dubbo中, 用于扫描DubboService
com.alibaba.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner 

// MyBatis中, 用于扫描Mapper 
org.mybatis.spring.mapper.ClassPathMapperScanner

问题记录

1.如何通过包名查找类资源的?

PathMatchingResourcePatternResolver#doFindAllClassPathResources
中调用ClassLoader#getResources("包路径, 如: com/simple/"),ClassLoader为Launcher$AppClassLoader, 结果示例:

result = 
0 = "URL [file:/D:/simple-boot/target/classes/com/simple/]"
1 = "URL [jar:file:/D:/simple-boot/lib/simple-depend-1.0-SNAPSHOT.jar!/com/simple/]"

这样会查出类路径下, 所有符合包名前缀的rootDirURL, 然后循环Root路径扫描子路径

2.怎么查找文件夹里面的class?

PathMatchingResourcePatternResolver#doFindPathMatchingFileResources
如问题1中, 根路径URL [file:/D:/simple-boot/target/classes/com/simple/]将匹配到该查询方法
该方法递归目录进行搜索

3.怎么查找jar包里面的class?

PathMatchingResourcePatternResolver#doFindPathMatchingJarResources
如问题1中, 根路径URL [jar:file:/D:/simple-boot/lib/simple-depend-1.0-SNAPSHOT.jar!/com/simple/]将匹配到该查询方法
将jar文件获取或创建为java.util.jar.JarFile的实例, 通过Enumeration<JarEntry> entries = jarFile.entries()方法循环所有文件

其他一些记录

PathMatchingResourcePatternResolver#getResources(String locationPattern)
中, 包名转换成locationPattern的方式:

String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + '/' + this.resourcePattern;

假设basePackage="com.simple", 则将packageSearchPath会拼装成classpath*:com/simple/**/*.class


YYGP
25 声望11 粉丝

写BUG