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