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的方法

@Override

publicvoidrefresh()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

回到顶部