Springboot启动扫描包的原理
参考链接1
参考链接2
所参照代码为Springboot2.1.1
默认情况下,扫描范围是主类xxxApplication所在包及其子目录,可以在后面的具体实现中看到。
从主类中的SpringApplication.run(xxxApplication.class, args);
一直点击进入run方法的实现,这里可以看到run方法里有几个关于context的方法分别是:
createApplicationContext()
prepareContext(xxx,xx)
refreshContext(context)
publicConfigurableApplicationContextrun(String... args){...
try{
...
context =createApplicationContext();
...
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
....
catch(Throwable ex){
}
逐个分析:
1.createApplicationContext()
这个方法返回一个类型为AnnotationConfigServletWebServerApplicationContext
的context,可以点进去看到这个class为AnnotationConfigServletWebServerApplicationContext
类型。
protectedConfigurableApplicationContextcreateApplicationContext(){...
case SERVLET:
contextClass =Class.forName(**DEFAULT_SERVLET_WEB_CONTEXT_CLASS**);
break;
case...
}
}
....
return(ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
该类的一个父类GenericApplicationContext
中,创建了一个beanFactory
,这个beanFactory实现了BeanDefinitionRegistry
的接口,后面会用到。
publicGenericApplicationContext(){this.beanFactory =newDefaultListableBeanFactory();
}
createApplicationContext返回的语句中调用了AnnotationConfigServletWebServerApplicationContext
类的构造函数,
publicAnnotationConfigServletWebServerApplicationContext(){this.reader =newAnnotatedBeanDefinitionReader(this);
this.scanner =newClassPathBeanDefinitionScanner(this);
}
进入构造函数中的第一个AnnotatedBeanDefinitionReader
,一直点到registerAnnotationConfigProcessors
的方法:
publicstaticSet<BeanDefinitionHolder>registerAnnotationConfigProcessors(BeanDefinitionRegistry registry,@NullableObject source){
//创建了一个beanFactory
DefaultListableBeanFactory beanFactory =unwrapDefaultListableBeanFactory(registry);
if(beanFactory !=null){
.....
}
Set<BeanDefinitionHolder> beanDefs =newLinkedHashSet<>(8);
if(!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)){
RootBeanDefinition def =newRootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
//添加了一个类型为CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME的beanDefinition,下面一段代码可以看到它的名字为internalConfigurationAnnotationProcessor,(ConfigurationClassPostProcessor.class)为类型的beanDefinition 这个后面会用到
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
....
}
publicstaticfinalString CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME ="org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
接着进入到构造函数中的new ClassPathBeanDefinitionScanner(),
publicClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry,boolean useDefaultFilters,Environment environment,@NullableResourceLoader resourceLoader){
....
//入口处默认useDefaultFilters为true
if(useDefaultFilters){
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
进入registerDefaultFilters
方法,
protectedvoidregisterDefaultFilters(){//这里把Component.class作为默认的includeFilter加入了过滤器
this.includeFilters.add(newAnnotationTypeFilter(Component.class));
ClassLoader cl =ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try{
......
}
2.prepareContext()
prepareContext()方法里有个load函数,进入load函数的具体实现,
privateintload(Object source){Assert.notNull(source,"Source must not be null");
if(source instanceofClass<?>){
returnload((Class<?>) source);
}
if(source instanceofResource){
returnload((Resource) source);
}
if(source instanceofPackage){
....
}
因为主类的run方法传入的是xxxxApplication.class,所以这里走第一个分支
privateintload(Class<?> source){.....
//isComponent里面判断是否有Component的注解,启动类是有的,所以这里将启动类加载进来了
if(isComponent(source)){
this.annotatedReader.register(source);
return1;
}
return0;
}
3.refreshContext()
点击进入refresh的具体实现,看到一个invokeBeanFactoryPostProcessors的方法
@Overridepublicvoidrefresh()throwsBeansException,IllegalStateException{
synchronized(this.startupShutdownMonitor){
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try{
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//这里写着注册bean
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
....
进入其实现,这里传入的beanFactory的就是1.createApplicationContext()中第一个方法创建的的beanFactory
protectedvoidinvokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory){PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory,getBeanFactoryPostProcessors());
.....
}
进入这个方法,因为1.createApplicationContext中创建的beanFactory实现了BeanDefinitionRegistry接口,所以我们进入这个分支看一看,
publicstaticvoidinvokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory,List<BeanFactoryPostProcessor> beanFactoryPostProcessors){
Set<String> processedBeans =newHashSet<>();
//BeanDefinitionRegistry
if(beanFactory instanceofBeanDefinitionRegistry){
.....
for(BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors){
//BeanDefinitionRegistryPostProcessor
if(postProcessor instanceofBeanDefinitionRegistryPostProcessor){
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
.....
}
还是1.createApplicationContext()中创建的ConfigurationClassPostProcessor类,刚好实现了BeanDefinitionRegistryPostProcessor,所以这里我们直接看ConfigurationClassPostProcessor累的postProcessBeanDefinitionRegistry方法,可以看到这里调用了processConfigBeanDefinitions方法,
publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){....
processConfigBeanDefinitions(registry);
}
进入这个方法:
publicvoidprocessConfigBeanDefinitions(BeanDefinitionRegistry registry){.....
Set<BeanDefinitionHolder> candidates =newLinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed =newHashSet<>(configCandidates.size());
do{
parser.parse(candidates);
parser.validate();
.....
}
进入parse(candidates),这里再前面创建ConfigurationClassPostProcessor的时候给它生成了一个Holder,而这个beanDefinition的类型就是AnnotatedBeanDefinition,所以走这个分支的parse方法
publicvoidparse(Set<BeanDefinitionHolder> configCandidates){for(BeanDefinitionHolder holder : configCandidates){
BeanDefinition bd = holder.getBeanDefinition();
try{
//这个分支
if(bd instanceofAnnotatedBeanDefinition){
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
elseif(bd instanceofAbstractBeanDefinition&&((AbstractBeanDefinition) bd).hasBeanClass()){
....
}
一路点下去,进入doProcessConfigurationClass方法,发现这里就是用ComponentScan来解析bean对象的
protectedfinalSourceClassdoProcessConfigurationClass(ConfigurationClass configClass,SourceClass sourceClass)throwsIOException{
.....
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans =AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(),ComponentScans.class,ComponentScan.class);
if(!componentScans.isEmpty()&&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(),ConfigurationPhase.REGISTER_BEAN)){
for(AnnotationAttributes componentScan : componentScans){
// 进入这个方法
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
}
}
在这个parse方法的最后,可以看到basePackages.isEmpty()的判断,当前这个basePackages就是空的,因为没有配置@ComponentScan的这个属性,这里就会给basePackages添加这个启动类的名字的最后一个点之前的路径,比如启动类是A.B.C.D.class, 那么路径就是A.B.C.D,加入它所在的包名
publicSet<BeanDefinitionHolder>parse(AnnotationAttributes componentScan,finalString declaringClass){....
if(basePackages.isEmpty()){
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(newAbstractTypeHierarchyTraversingFilter(false,false){
@Override
protectedbooleanmatchClassName(String className){
return declaringClass.equals(className);
}
});
return scanner.doScan(StringUtils.toStringArray(basePackages));
}
最后进入doScan方法,这里有个findCandidateComponents的方法,得到这些候选的bean就会注册到Spring容器中。
protectedSet<BeanDefinitionHolder>doScan(String... basePackages){....
for(String basePackage : basePackages){
//这个方法
Set<BeanDefinition> candidates =findCandidateComponents(basePackage);
for(BeanDefinition candidate : candidates){
ScopeMetadata scopeMetadata =this.scopeMetadataResolver.resolveScopeMetadata(candidate);
....
return beanDefinitions;
}
最后进入scanCandidateComponents方法,这里Spring会通过传入的路径来遍历下面的每一个class,这个方法所在类为ClassPathScanningCandidateComponentProvider,不过所有的basePackage上面那个方法(所在类ClassPathBeanDefinitionScanner)打断点查看。
privateSet<BeanDefinition>scanCandidateComponents(String basePackage){Set<BeanDefinition> candidates =newLinkedHashSet<>();
try{
String packageSearchPath =ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage)+"/"+this.resourcePattern;
Resource[] resources =getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for(Resource resource : resources){
if(traceEnabled){
logger.trace("Scanning "+ resource);
}
if(resource.isReadable()){
try{
MetadataReader metadataReader =getMetadataReaderFactory().getMetadataReader(resource);
//这里判断了是否是Component.class
if(isCandidateComponent(metadataReader)){
ScannedGenericBeanDefinition sbd =newScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if(isCandidateComponent(sbd)){
.....
}
由于上面提到过的默认的includeFilters中加入了Componet.class,所以在启动类的同级目录下,添加Component注解及其子注解的类会被加入到Spring容器中。
/** * Determine whether the given class does not match any exclude filter
* and does match at least one include filter.
* @param metadataReader the ASM ClassReader for the class
* @return whether the class qualifies as a candidate component
*/
protectedbooleanisCandidateComponent(MetadataReader metadataReader)throwsIOException{
for(TypeFilter tf :this.excludeFilters){
if(tf.match(metadataReader,getMetadataReaderFactory())){
returnfalse;
}
}
for(TypeFilter tf :this.includeFilters){
if(tf.match(metadataReader,getMetadataReaderFactory())){
returnisConditionMatch(metadataReader);
}
}
returnfalse;
}
结论
最终实现类路径组件扫描的是ClassPathScanningCandidateComponentProvider类下的scanCandidateComponents方法,查看全部扫描包可以在它的上级调用方法ClassPathBeanDefinitionScanner类的doScan(String... basePackages)方法打断点查看。
作者:WAHAHA402
链接:https://www.jianshu.com/p/260cca0ec712
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
以上是 Springboot启动扫描包的原理 的全部内容, 来源链接: utcz.com/z/514476.html