一般来说spring boot默认的扫描路径是启动类当前的包和子包

@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(basePackages = {"com.frame.springboot.dao", "com.frame.springboot.base"})
public class SpringbootApplication {public static void main(String[] args) {SpringApplication app = new SpringApplication(SpringbootApplication.class);app.addListeners(new MyApplicationStartedEventListener());app.run(args);}static class MyApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent> {private Logger logger = LoggerFactory.getLogger(MyApplicationStartedEventListener.class);@Overridepublic void onApplicationEvent(ApplicationStartedEvent event) {SpringApplication app = event.getSpringApplication();app.setBannerMode(Banner.Mode.OFF);// 不显示banner信息logger.info("==MyApplicationStartedEventListener==");}}}

例如这个类的包和子类。

接下来就从源码角度来分析这是为什么

首先这个加载过程肯定是从 refreshcontext 中的操作,因此进入。

  public void refresh() throws BeansException, IllegalStateException {synchronized(this.startupShutdownMonitor) {this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try {this.postProcessBeanFactory(beanFactory);this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var9) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);}this.destroyBeans();this.cancelRefresh(var9);throw var9;} finally {this.resetCommonCaches();}}}

这个操作实在初始化factorypostprocessors中进行的。

接下来进入方法

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}}

在这里很明显是把任务委托给了PostProcessorRegistrationDelegate。接下来我们继续点击进入

invokeBeanFactoryPostProcessors的方法很长我只截取我们关心的代码

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {Set<String> processedBeans = new HashSet();int var9;ArrayList currentRegistryProcessors;String[] postProcessorNames;if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory;List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList();List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList();Iterator var6 = beanFactoryPostProcessors.iterator();while(var6.hasNext()) {BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var6.next();if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor)postProcessor;registryProcessor.postProcessBeanDefinitionRegistry(registry);registryProcessors.add(registryProcessor);} else {regularPostProcessors.add(postProcessor);}}
}

这里先来简单的说一下,首先会调用传入的beanFactoryPostProcessors的postProcessBeanDefinitionRegistry。处理一般会调用

ConfigurationClassPostProcessor类,接下来进去看一下postProcessBeanDefinitionRegistry方法

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId = System.identityHashCode(registry);if (this.registriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);} else if (this.factoriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);} else {this.registriesPostProcessed.add(registryId);this.processConfigBeanDefinitions(registry);}
}

在这里最重要的还是processConfigBeanDefinitions方法,但是这个方法实在是太长了截取重要的步骤

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {//省略parser.parse(candidates);
}

这个方法主要是用来解析beandefinitions。进入看一下

   public void parse(Set<BeanDefinitionHolder> configCandidates) {this.deferredImportSelectors = new LinkedList();Iterator var2 = configCandidates.iterator();while(var2.hasNext()) {BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();BeanDefinition bd = holder.getBeanDefinition();try {if (bd instanceof AnnotatedBeanDefinition) {this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) {this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName());} else {this.parse(bd.getBeanClassName(), holder.getBeanName());}} catch (BeanDefinitionStoreException var6) {throw var6;} catch (Throwable var7) {throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7);}}this.processDeferredImportSelectors();}

这由于我们使用的是注解来进行那么进入第一个parse,主要调用了processConfigurationClass 的方法

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);if (existingClass != null) {if (configClass.isImported()) {if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);}return;}this.configurationClasses.remove(configClass);Iterator it = this.knownSuperclasses.values().iterator();while(it.hasNext()) {if (configClass.equals(it.next())) {it.remove();}}}ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass);do {sourceClass = this.doProcessConfigurationClass(configClass, sourceClass);} while(sourceClass != null);this.configurationClasses.put(configClass, configClass);}}

这个方法获取config的class和解析,其中主要方法是doProcessConfigurationClass

    protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass) throws IOException {Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {Iterator var13 = componentScans.iterator();while(var13.hasNext()) {AnnotationAttributes componentScan = (AnnotationAttributes)var13.next();Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());Iterator var7 = scannedBeanDefinitions.iterator();while(var7.hasNext()) {BeanDefinitionHolder holder = (BeanDefinitionHolder)var7.next();if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());}}}}}

只截取了其中的重要部分,看到componentScanParser.parse了,那么基本就想到是要去获取Component组件了,为什么config会和component关系呢,请看一下注解内部里面有一个@component。接下来进去看一下。

    public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {Set<String> basePackages = new LinkedHashSet();String[] basePackagesArray = componentScan.getStringArray("basePackages");String[] var19 = basePackagesArray;int var21 = basePackagesArray.length;int var22;for(var22 = 0; var22 < var21; ++var22) {String pkg = var19[var22];String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ",; \t\n");basePackages.addAll(Arrays.asList(tokenized));}Class[] var20 = componentScan.getClassArray("basePackageClasses");var21 = var20.length;for(var22 = 0; var22 < var21; ++var22) {Class<?> clazz = var20[var22];basePackages.add(ClassUtils.getPackageName(clazz));}if (basePackages.isEmpty()) {basePackages.add(ClassUtils.getPackageName(declaringClass));}
}

这个代码是为了寻找可以扫描的基础包,但是在创建启动类的时候我们并没有设置也就是 basePackages.isEmpty()==true 。接下来的操作是添加 declaringClass 路径,那么这个类是什么呢。这个类就是你的启动类。所以真正设置扫描路径的代码在这。

spring boot默认扫描的路径相关推荐

  1. Spring boot 默认静态资源路径与手动配置访问路径的方法

    这篇文章主要介绍了Spring boot 默认静态资源路径与手动配置访问路径的方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下 在application.propertis中配置 ##端口号 s ...

  2. Spring Boot默认配置文件加载顺序(四)

    通常情况下,Spring Boot 在启动时会将 resources 目录下的 application.properties 或 apllication.yml 作为其默认配置文件,我们可以在该配置文 ...

  3. spring boot—默认日志框架配置

    文章目录 默认日志框架--配置全解 日志格式 日志级别 日志输出到控制台 日志输出到文件 spring boot2.2.4官方文档 默认日志框架–配置全解 #日志记录 logging:#日志级别lev ...

  4. Spring Boot默认异常处理BasicErrorController源码解读

    小伙伴们是不是刚接触Spring Boot做网页开发的时候,如果代码发生异常,会返回一个错误信息页面,如下图 那么这个页面是怎么返回的呢,这里就要接触到一个Spring Boot类BasicError ...

  5. Spring Boot基础学习笔记17:Spring Boot默认缓存

    文章目录 零.学习目标 一.缓存概述 (一)引入缓存管理的重要性 (二)Spring Boot的缓存管理 二.Spring Boot默认缓存 (一)数据准备 (二)创建Spring Boot项目 - ...

  6. Spring Boot 默认数据源 HikariDataSource_Spring Boot 中使用 Hikari

    Spring Boot 默认数据源 HikariDataSource springboot2.x之后,系统的默认数据源由原来的的org.apache.tomcat.jdbc.pool.DataSour ...

  7. spring boot JPA 扫描不到repository问题

    今天做spring boot 集成jpa 的时候 ,项目启动 控制台没有数据库相关的信息,也不能自动建表,还扫描不到repository .最后跟以前的springboot jpa 项目比较 发现ja ...

  8. spring boot中配置虚拟路径,用来映射显示图片

    增加配置,继承 WebMvcConfigurerAdapter,如下: package com.wm.mogu_picture.config;import org.springframework.be ...

  9. Spring Boot————默认缓存应用及原理

    引言 应用程序的数据除了可以放在配置文件中.数据库中以外,还会有相当一部分存储在计算机的内存中,这部分数据访问速度要快于数据库的访问,因此通常在做提升数据访问速度时,会将需要提升访问速度的数据放入到内 ...

  10. Spring boot配置项目访问路径server.context-path不起作用(改为server.servlet.context-path)

    配置Spring boot 访问路径 Spring boot  2.0之前的版本:server.context-path Spring boot  2.0之后的版本:server.servlet.co ...

最新文章

  1. sql 分号变成多行_SQL(2)
  2. python基础代码事例-推公式到写代码-python基础
  3. CSS之Background-size:cover
  4. Divan and a New Project 贪心,模拟(1000)
  5. 灰色的rgb值_一行代码实现图片的灰色效果
  6. JBoss模块很烂,无法在JBoss 7下使用自定义Resteasy / JAX-RS
  7. Visual Studio Code 前端调试不完全指南
  8. vue搭建后可以改下全局配置
  9. stc89c51单片机音乐盒系统设计_基于单片机的火控系统语音报读设计
  10. n型半导体和p型半导体的区别_NPN型三极管和PNP型三极管有什么区别?
  11. matlab画雷达目标航迹,基于MATLAB的雷达目标测量仿真分析
  12. ubuntu流量监控_ubuntu 流量监控
  13. 深度残差网络+自适应参数化ReLU激活函数:调参记录2
  14. IPhone 日历提醒
  15. php函数大小写转化,php大小写转换函数(strtolower、strtoupper)用法介绍
  16. 第9天Sqltie数据库
  17. 字体图标 fa fa html5,Font Awesome 4.2.0的所有图标参考
  18. 如何才能高效的学习,99%的人不知道的高效学习法
  19. 2022年度“十大基础研究关键词”在深发布
  20. Drupal8的详细建站教程

热门文章

  1. OpenAPI 开发者挑战赛第三期
  2. 细数那些年ZStack拿过奖的案例
  3. 【破损识别】基于matlab GUI机器视觉+SVM玉米种子破损识别(带面板)【含Matlab源码 1651期】
  4. 【语音识别】基于matlab GUI语音识别信号灯图像模拟控制(带面板)【含Matlab源码 757期】
  5. 【目标跟踪】基于matlab Kalman滤波目标跟踪【含Matlab源码 388期】
  6. 【TWVRP】基于matlab粒子群算法求解带时间窗的车辆路径规划问题【含Matlab源码 334期】
  7. java小数正负数据类型_Java - day001 - 8种基本数据类型
  8. 香草 jboss 工具_使用Tensorflow创建香草神经网络
  9. TypeError: __init__() got an unexpected keyword argument 'serialized_options'(安装protobuf 3.6.0)
  10. python请求url非阻塞_python 如何连续读取一个由服务器发来的非阻塞请求?