spring boot默认扫描的路径

2023-10-29

一般来说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);

        @Override
        public 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路径,那么这个类是什么呢。这个类就是你的启动类。所以真正设置扫描路径的代码在这。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spring boot默认扫描的路径 的相关文章

  • 判断机器IP是公网ip还是内网ip

    首先是恭喜开通blog 对于ip是否是公网ip 网上已经有很多文章进行了描述 但我每次都记不太住 总要查找一下才又清楚 因此决定在这里记录下来 方便以后查询 ip地址分为五类 E类为保留为今后使用 D类为组播地址 用于主机网络地址的就是A类
  • windows套接字I/0模型-IOCP完成端口模型

    在 Windows 网络编程中 IOCP Input Output Completion Port 是一种高性能的 I O 模型 可以使应用程序能够处理大量并发 I O 操作 IOCP 模型主要通过事件通知和回调函数来处理异步 I O 操作
  • 最长01交替子串(浪潮笔试题)

    题意 给一个只有0和1的字符串 允许反转一个连续区间 即0变成1 1变成0 求最长的01交替串多长 允许不连续 我最先想到的是动态规划解法 状态设计方面 首先一个串的状态会有以0结尾和以1结尾两种 然后题目中说允许反转一个连续区间 那么根据
  • 【JavaEE】_JavaScript(WebAPI)

    目录 1 DOM 1 1 DOM基本概念 1 2 DOM树 2 选中页面元素 2 1 querySelector 2 2 querySelectorAll 3 事件 3 1 基本概念 3 2 事件的三要素 3 3 示例 4 操作元素 4 1
  • vue3使用高德地图完成标注点聚合

随机推荐

  • Linux内核提权漏洞CVE-2023-32233复测及POC脚本

    近日 研究人员发现了Linux内核的NetFilter框架中的新漏洞 CVE 2023 32233 该漏洞可被本地用户用于将权限提升为root 并完全控制系统 问题的根源在于tfilter nf tables是如何处理批处理请求的 经过身份
  • C++引用(&)解析:引用的作用,什么时候使用引用,引用与指针的不同

    引用是什么 什么时候使用引用 做函数的参数 做函数的返回值 引用与指针的不同 引用是什么 引用是变量的别名 理论上不开辟新的空间 符号 是类型标识符的一部分 被引用的数据类型 变量名称 如下图中 变量a 与变量ra 指向同一块内存空间 由此
  • 使用python的tkinter模块制作一个计算器

    使用tkinter模块制作的一个简单的计算器 由于刚学这个模块 所以做的不是很好 截图 点击计算后算式区的值会改成结果 可自行在数字触发的函数中添加数字输入就进行计算回显的功能 代码如下 import tkinter as tk impor
  • Python学习笔记(十):包、模块

    一 包 模块 Python中的包相当于文件夹 模块就是 py文件 不同包下有相同名称的模块时 为了区分 可在模块里加上 包名 模块名 路径 这个路径叫做命名空间 注 如果想让一个普通的文件夹变为包 则需要在这个文件夹内添加 init py文
  • 原子操作实现无锁栈

    atomic3 cpp 使用CAS操作实现一个无锁栈 include
  • Vulkan® A Specification Core And Extension::Vulkan文档核心部分和扩展部分对照与翻译说明

    2020 7 1 更 修改和完善某些描述 Vulkan版本 1 2 137 1 2 145 Vulkan文档分成两种 核心部分 核心部分 扩展部分 核心部分是必定要翻译的 对于扩展部分 本人并不是所有扩展都见过 所以对于扩展部分尽可能的翻译
  • Kanzi Shader入门

    1 版本 kanzi默认支持Opengl ES 2 0 在qnx平台可以支持到ES 3 0 2 着色器 kanzi只支持 顶点着色器 和 片段着色器 3 kanzi studio 无法直接使用shader 需要通过画刷和材质间接使用 在 普
  • strongSwan报文交互过程

    通过上篇案例 我们已经初步掌握了如何通过strongSwan配置两台Linux主机之间的IPsec隧道 今天我们再来看一下strongSwan配置IPsec的报文交互过程和转发性能 组网图还是上次的拓扑 首先查看一下在配置完strongSw
  • 使用FFmpeg命令进行hls切片,得到的ts文件时长不准确

    一 问题描述 使用如下FFmpeg命令进行hls切片 ffmpeg i video2 mp4 hls time 1 f hls out m3u8 由于使用了参数 hls time 1 所以预计生成的ts文件时长会是1秒左右 但结果却是最终生
  • 关于把数据库放在阿里云上,实现共享

    本地的数据库只能实现在自己的机器上访问 别人的电脑无法实现访问 因此通过借助阿里云 把数据库放在阿里云上 从而多人都可以通过阿里云的ip来进行访问操作 在试验过程中 难度最大的就是对阿里云一窍不通 不知从何下手 下面进行一下总结 实现的基本
  • django<model模块和分页功能模块>

    model模块使用 import os django os environ setdefault DJANGO SETTINGS MODULE wxy django settings django setup 指定django的配置 fro
  • dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique:

    C Program Files Java jdk1 8 0 181 bin java exe Dmaven multiModuleProjectDirectory E Software xiaoye CRUD parent Dmaven h
  • Java内存区域(栈、堆、方法区)详细解说

    参考文献 深入学习java虚拟机 概览 java虚拟机在执行java程序的过程中会把它所管理的内存划分成若干个不同的数据区域 这些区域各有用途 以及创建和销毁的时间 有的区域随着虚拟机的进程的启动而存在 有的则依赖用户线程的启动和结束而建立
  • 简单JS解密爬取股票信息案例

    前言 今天给大家带来一个使用JS解密爬取股票信息的案例 本案例比较简单 主要介绍如何找出JS加密的过程 希望通过本案例可以带给大家学习JS的一些思路 本案例网站 https webapi cninfo com cn marketData 一
  • Jetty, “No multipart config for servlet” problem

    I m writing handler for file transfer The request is multipart HTTP message The message is correct tested on other serve
  • Python爬虫:解决SSL证书验证问题

    如果目标网站没有设置好HTTPS证书 又或者网站的HTTPS证书不被CA机构认可 用浏览器访问的话 就可能会出现SSL证书错误的提示 用requests库来请求这类网站的话 会直接抛出SSLError错误 requests exceptio
  • ssh命令详解

    基础命令学习目录 SSH 远程连接工具 连接原理 ssh服务是一个守护进程 demon 系统后台监听客户端的连接 ssh服务端的进程名为sshd 负责实时监听客户端的请求 IP 22端口 包括公共秘钥等交换等信息 ssh服务端由2部分组成
  • C++知识积累:explicit关键字的作用

    explicit意为 显式的 该关键字主要是用于防止类构造函数出现隐式类型转换的情况 且只适用于仅含一个参数的构造函数 我们先来看第一个问题 什么是防止构造函数出现隐式转换呢 来看下面的例子 class A public A int a c
  • React-router v5和v6的区别对比

    以下是两个版本之间的区别 一 首先是注册路由的时候v5的Switch改为了Routes v5 代码如下 import Route Switch from react router dom 引入react router div 注册路由 编写
  • spring boot默认扫描的路径

    一般来说spring boot默认的扫描路径是启动类当前的包和子包 SpringBootApplication EnableTransactionManagement proxyTargetClass true MapperScan bas