SpringBoot 源码分析准备应用上下文(2)-prepareContext

news2024/11/23 8:44:42

一、入口

/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            //主要看这个方法
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
prepareContext(context, environment, listeners, applicationArguments, printedBanner);

这个方法主要是一些准备工作,用来进行一些赋值操作,在上一步中已经把应用上下文创建出来了,这里就是赋值,会去创建一些 bean 对象存于 IOC容器中,会完成主类(启动类)对象的创建并添加到 IOC 容器中 ,接着看 prepareContext方法的具体实现

二、prepareContext 方法实现

截图:

代码:

 private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        //设置容器环境
        context.setEnvironment(environment);
        //执行容器后置处理
        postProcessApplicationContext(context);
        //应用初始化
        applyInitializers(context);
        //向其他各个监听器发送已经准备好的实践通知
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 将 main 主函数中的 args 参数封装成单例 bean ,注册进容器
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        //加载我们的启动类,将启动类注入容器
        load(context, sources.toArray(new Object[0]));
        //发布容器已加载事件通知
        listeners.contextLoaded(context);
    }

 接着看  applyInitializers 方法实现

2.1 applyInitializers 方法实现

引:applyInitializers(context);

截图:

代码:

/**
     * Apply any {@link ApplicationContextInitializer}s to the context before it is
     * refreshed.
     * @param context the configured ApplicationContext (not refreshed yet)
     * @see ConfigurableApplicationContext#refresh()
     */
    // 之前在 new SpringApplication() 中创建的初始化容器的启动
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected void applyInitializers(ConfigurableApplicationContext context) {
        // 就是把之前创建出来的初始化容器进行启动
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                    ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            // 启动之前创建的初始化容器
            initializer.initialize(context);
        }
    }

 

initializer.initialize(context);这个方法就会启动一些上下文

2.2 load 方法实现

引:load(context, sources.toArray(new Object[0]));

截图:

代码:


    /**
     * Load beans into the application context.
     * @param context the context to load beans into
     * @param sources the sources to load
     */  
   protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        // 创建 BeanDefinitionLoader
        BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }
        loader.load();
    }

 点击loader.load()方法,又调用了load()方法:

截图:

代码:

 /**
     * Load the sources into the reader.
     * @return the number of loaded beans
     */
    int load() {
        int count = 0;
        for (Object source : this.sources) {
            count += load(source);
        }
        return count;
    }

    private int load(Object source) {
        Assert.notNull(source, "Source must not be null");
        if (source instanceof Class<?>) {
            // 从 Class 加载
            return load((Class<?>) source);
        }
        if (source instanceof Resource) {
            // 从 Resource 加载
            return load((Resource) source);
        }
        if (source instanceof Package) {
            // 从 Package 加载
            return load((Package) source);
        }
        if (source instanceof CharSequence) {
            // 从 CharSequence 加载
            return load((CharSequence) source);
        }
        throw new IllegalArgumentException("Invalid source type " + source.getClass());
    }

 接着看 从 Class 加载  load((Class<?>) source)

 代码:

 private int load(Class<?> source) {
        if (isGroovyPresent() && BeanDefinitionLoader.GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
            // Any GroovyLoaders added in beans{} DSL can contribute beans here
            BeanDefinitionLoader.GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, BeanDefinitionLoader.GroovyBeanDefinitionSource.class);
            load(loader);
        }
        // 判断核心启动类 是否标注了 @Component 注解
        if (isComponent(source)) {
            // 将启动类的 BeanDefinition 注册进 beanDefinitionMap 中
            this.annotatedReader.register(source);
            return 1;
        }
        return 0;
    }

2.3  register 方法实现

引:this.annotatedReader.register(source);

截图:

2.4 registerBean 方法实现

引:registerBean(componentClass);

截图:

2.4  doRegisterBean 方法实现

引:doRegisterBean(beanClass, null, null, null, null);

截图:

代码:

 /**
     * Register a bean from the given bean class, deriving its metadata from
     * class-declared annotations.
     * @param beanClass the class of the bean
     * @param name an explicit name for the bean
     * @param qualifiers specific qualifier annotations to consider, if any,
     * in addition to qualifiers at the bean class level
     * @param supplier a callback for creating an instance of the bean
     * (may be {@code null})
     * @param customizers one or more callbacks for customizing the factory's
     * {@link BeanDefinition}, e.g. setting a lazy-init or primary flag
     * @since 5.0
     */
    private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
                                    @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
                                    @Nullable BeanDefinitionCustomizer[] customizers) {

        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }

        abd.setInstanceSupplier(supplier);
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }
        if (customizers != null) {
            for (BeanDefinitionCustomizer customizer : customizers) {
                customizer.customize(abd);
            }
        }

        // BeanDefinitionHolder 就是针对 BeanDefinition 的一个持有对象,里面有两个内容,分别是 BeanDefinition 和 BeanName
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }

 2.5 接着 registerBeanDefinition 方法实现

引:BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);

截图:

代码:


    /**
     * Register the given bean definition with the given bean factory.
     * @param definitionHolder the bean definition including name and aliases
     * @param registry the bean factory to register with
     * @throws BeanDefinitionStoreException if registration failed
     */
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName(); //获取 beanName
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 注册 BeanDefinition

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

 2.6 接着看 registerBeanDefinition 方法实现

引:registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

截图:

 这是一个接口,接着看其具体实现类:

 找到其默认实现类 DefaultListableBeanFactory

这就回到第一步的 registerBeanDefinition 方法

截图:

 代码:

//---------------------------------------------------------------------
	// Implementation of BeanDefinitionRegistry interface
	//---------------------------------------------------------------------
 
	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
 
		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
 
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}
        //在注册 bd 的时候判断该名字有没有被注册
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        //该名字已经被注册
        //spring 默认支持覆盖 bd,但是 spring 会输入一些日志
        //1、两个 bd 相同的情况下
        //2、两个 bd 不同的情况 role 不同
        //3、两个 bd 不相同但是 role 相同
		if (existingDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
            //优先级
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isInfoEnabled()) {
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
            //判断我们的 spring 容器是否开启实例化 bean了
            //如果为null,set 为空,没有开始就创建 bean 不会进入 if
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
                    //一个 map 一个 list ,list里面方法的 map 的 key,也就是 beanName
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
                    //如果注册了 beanDefinition 的名字和手工注册的 bd 集合当中某个相同则删除手动注册的 beanName
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}
        //判断注册的 bd 以及 beanName 是否存在
		if (existingDefinition != null || containsSingleton(beanName)) {
            //清除 allBeanNameByType
            //把单例池当中的 bean 也 remove
			resetBeanDefinition(beanName);
		}
	}
总结:prepareContext()的主要工作:
1、向context完成一些属性的设置
2、将主类生成实例对象,存到容器中。

参考文章:SpringBoot源码深度剖析——@SpringBootApplication注解和new SpringApplication().run()方法深度解密_生活,没那么矫情的博客-CSDN博客

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/628481.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

生成测试报告,在Unittest框架中就是简单

测试套件&#xff08;Test Suite&#xff09;是测试用例、测试套件或两者的集合&#xff0c;用于组装一组要运行的测试&#xff08;多个测试用例集合在一起&#xff09;。 &#xff08;1&#xff09;创建一个测试套件&#xff1a; import unittest suite unittest.TestSuite…

车载测试:详解ADAS传感器(相机)标定数据采集方法

1.基本原理 相机外参标定&#xff0c;通过拍摄多角度棋盘格标定相机外参。 2.外参标定板设计 标定板分为垂直标定板和水平标定板&#xff0c;由于地面的水平标定板不容易被检测到&#xff0c;本文采用垂直标定板进行相机标定。 在标定过程中标定板需要和车身坐标成正交状态…

中国人民大学与加拿大女王大学金融硕士——所有的为时已晚都是恰逢其时

你是否有过同样的感觉&#xff0c;工作之余想学点什么又觉得有点晚了&#xff0c;心里反复纠结&#xff0c;总是没个结果。记得在网上看到过一句话&#xff0c;你觉得为时已晚的时候&#xff0c;恰恰是最早的时候。与其在心里反复琢磨&#xff0c;不如去付诸行动。中国人民大学…

超详细,自动化测试-Allure测试报告动态生成用例/标题(实战撸码)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 pytest 结合 allu…

Android-源码分析-MTK平台BUG解决:客户电池NTC功能(移植高低温报警,关机报警功能)---第一天分析与解决

MTK平台BUG解决&#xff1a;客户电池NTC功能 一、概述二、步骤1&#xff1a;实现目的&#xff1f;2&#xff1a;准备工作&#xff1a;机制原理的学习&#xff08;1&#xff09;MTK充电温度保护机制&#xff08;2&#xff09;MTKthermal高温充电机制 3&#xff1a;定位查找与源码…

提高自动化测试效率 , WEB自动化框架的基础封装模块!

目录 前言 一、环境搭建 1. Python环境 2. Selenium安装 3. Chrome浏览器 二、基础封装模块介绍 1. 代码框架介绍 2. 使用示例 三、总结 前言 在软件测试中&#xff0c;WEB自动化测试已成为不可或缺的一部分。WEB自动化测试涉及到大量的代码编写&#xff0c;为了提高…

SUSTechPOINTS三维点云标注工具使用

官方地址&#xff1a;SUSTechPOINTS 官方中文教程 相关文章&#xff1a; OpenPCDet安装、使用方式及自定义数据集训练 安装 git clone https://github.com/naurril/SUSTechPOINTS cd SUSTechPOINTS pip install -r requirement.txt wget https://github.com/naurril/SUSTec…

【全栈开发】基于Spring BootVueAndroid扫码授权登录

文章目录 一、引言二、设计1、移动端&#xff08;Android&#xff09;&#xff08;1&#xff09;库&#xff08;2&#xff09;依赖&#xff08;3&#xff09;使用 2、前端&#xff08;Vue&#xff09;&#xff08;1&#xff09;库&#xff08;2&#xff09;使用 3、后端&#x…

Home Assistant-开源智能家居系统

Home Assistant&#xff08;以下简称HA&#xff09; 它是个开源的智能家居平台&#xff0c;一个系统平台软件&#xff0c;像TB 1.它把家中的智能家居设备整合到HA中&#xff0c;它能够接入的设备非常的多比如小米、博联、易微联、飞利浦、特斯拉…&#xff0c;也可以接入软件&…

Python远程连接Ubuntu20.4下的Mariadb数据库进行操作

文章目录 前言一、ubuntu20.4安装mariadb10.51、更换数据源2、安装mariadb3、设置密码4、设置管理用户5、设置远程登录6、修改端口 二、mariadb10.5建库建表创建数据库2.建表 三、Python代码及环境准备1、Python2、环境 四、总结五、参考资料 前言 环境&#xff1a; 1、Ubuntu2…

Chromium浏览器渗透测试工具EvilSelenium简单入门

EvilSelenium是一款基于Selenium的渗透测试工具&#xff0c;该工具基于武器化的Selenium实现其功能&#xff0c;可以帮助广大研究人员针对基于Chromium的浏览器进行安全分析和渗透测试。 功能介绍 1、通过autofill获取存储的凭证信息&#xff1b; 2、获取Cookie数据&#xf…

高考答题卡怎么被机器识别?基于OpenCV答题卡识别模拟-米尔ARM+FPGA异构开发板

本篇测评由优秀测评者“筑梦者与梦同行”提供。 01. 前言MYD-JX8MMA7SDK发布说明 根据下图文件内容可以知道myir-image-full系统支持的功能&#xff0c;其支持OpenCV&#xff0c;也就不用在格外安装相关驱动包等&#xff0c;省了很多事情。 02. MYD-JX8MMA7软件评估指南 本文…

Java中Object类常用的11个方法

Java中Object类常用的11个方法 先看下 Object 的类结构&#xff08;快捷键&#xff1a;alt7&#xff09;&#xff1a; 1. getClass 方法&#xff08;获取类的class对象。&#xff09; public final native Class<?> getClass();final 方法、获取对象的运行时 class …

学生成绩管理系统(PowerDesigner+MyEclipse+SQL Server)

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;69学生 获取完整源码源文件论文报告数据库表等 系统中用户共有管理员、教师和学生三种&#xff0c;分别对应不同的权限。 管理员 &#xff08;1&#xff09;院系的开设&#xff1b; &#xff08;2&#xff09;教师基本信息…

VScode的插件和.json文件和快捷键

文章目录 1. 插件了解插件的配置的修改Remote DevelopmentFilter LineC/C 和 C intellisense&#xff08;弃用&#xff09;cpp-check-lint 2. VScode中的.json文件2.1 tasks.jsontasks.json文件的格式tasks.json文件中任务的配置arg参数选择 案例&#xff1a; 2.2 lauch.json参…

数字逻辑复习重点总结

文章目录 前言第一章第二章第三章第四章第五章第六章第七章&#xff1a;第八章总结 前言 因为要期末考试了所以就将知识点进行了总结&#xff0c;把期末要考的知识点分章节进行划分&#xff0c;以至于我能取得一个好成绩。 第一章 进制转换 8421码、2421码、余3码、格雷码&am…

Creating Serial Numbers (C#)

此示例展示如何使用Visual C#编写的Add-ins为文件数据卡生成序列号。 注意事项&#xff1a; SOLIDWORKS PDM Professional无法强制重新加载用.NET编写的Add-ins&#xff0c;必须重新启动所有客户端计算机&#xff0c;以确保使用Add-ins的最新版本。 SOLIDWORKS PDM Professio…

购买一套WMS仓储管理系统要多少钱

随着电商行业的快速发展&#xff0c;仓储物流行业也逐渐成为了人们关注的焦点。WMS仓储管理系统作为物流管理领域的重要工具&#xff0c;在提高仓库管理效率、降低运营成本方面具有重要作用。那么&#xff0c;购买一套WMS仓储管理系统要多少钱呢&#xff1f; 首先&#xff0c;我…

Vue开发实战(03)-组件化开发

对组件功能的封装&#xff0c;可以像搭积木一样开发网页。 Vue官方的示例图对组件化开发的形象展示。左边是一个网页&#xff0c;可以按照功能模块抽象成很多组件&#xff0c;这些组件就像积木一样拼接成网页。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直…

Lecture 21 Summarisation

目录 Extractive: Single-DocExtractive: Multi-DocAbstractive: Single-DocEvaluationConclusion summarisation Distill the most important information from a text to produce shortened or abridged versionExamples outlines of a documentabstracts of a scientific ar…