IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Boot 2.x源码系列【6】启动流程深入解析之创建、准备上下文 -> 正文阅读

[Java知识库]Spring Boot 2.x源码系列【6】启动流程深入解析之创建、准备上下文

有道无术,术尚可求,有术无道,止于术。

本系列Spring Boot版本2.7.0

前言

紧接上文,下一步是创建和准备上下文

			// 1. 创建上下文
            context = this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            // 2. 准备上下文
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

核心类

ServletWebServerApplicationContext

ServletWebServerApplicationContext从名字上看,它是一个基于Servlet Web 服务的应用上下文,它的类图如下:
在这里插入图片描述
它是Spring Boot 提供的基于Servlet 开发时使用的一个IOC容器,从它的类继承可以看出,它依赖于Spring MVC,而且还提供了两个子类,从名字就知道一个是基于注解,一个是基于XML注册的容器。
在这里插入图片描述

AnnotatedBeanDefinitionReader ClassPathBeanDefinitionScanner

AnnotatedBeanDefinitionReader从名字上理解,是一个注解方式的BeanDefinition读取器,学过Spring 的都知道首先会加载BeanDefinition,然后根据BeanDefinition生成Bean 对象,这个类的主要作用就是就是加载BeanDefinition了。

ClassPathBeanDefinitionScanner的作用也是加载BeanDefinition,不过从它的名字中,我们知道是一个扫描指定类路径中的BeanDefinition扫描器。

上面这两个类作用都是Bean 定义的加载。

1. 创建应用上下文

createApplicationContext方法直接调用工厂类创建对象:

    protected ConfigurableApplicationContext createApplicationContext() {
        return this.applicationContextFactory.create(this.webApplicationType);
    }

这个工厂类是SpringApplication类的成员属性,是ApplicationContextFactory的内部类DEFAULT

this.applicationContextFactory = ApplicationContextFactory.DEFAULT;

所以直接进入了ApplicationContextFactory 的内部匿名实现类中:

	ApplicationContextFactory DEFAULT = (webApplicationType) -> {
		try {
			// 1. 使用SPI 机制获取应用上下文创建工厂,
			// org.springframework.boot.ApplicationContextFactory=\
//org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext.Factory,\
//org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.Factory
			for (ApplicationContextFactory candidate : SpringFactoriesLoader
					.loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) {
				// 2. 调用工厂创建上下文,因为webApplicationType 是Servlet,所以创建的是
				ConfigurableApplicationContext context = candidate.create(webApplicationType);
				if (context != null) {
					return context;
				}
			}
			return AotDetector.useGeneratedArtifacts() ? new GenericApplicationContext()
					: new AnnotationConfigApplicationContext();
		}
		catch (Exception ex) {
			throw new IllegalStateException("Unable create a default ApplicationContext instance, "
					+ "you may need a custom ApplicationContextFactory", ex);
		}
	};

进入到AnnotationConfigServletWebServerApplicationContext内部类创建工厂时,因为类型为SERVLET ,所以会直接new 一个无参构造方法创建应用上下文对象:

    public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
            return webApplicationType != WebApplicationType.SERVLET ? null : new AnnotationConfigServletWebServerApplicationContext();
   }

学过JAVA 的都知道,子类的构造函数会隐式调用父类的无参构造函数,所以我们先看下AnnotationConfigServletWebServerApplicationContext的父类构造器中,都做了些什么。

首先是AbstractApplicationContext

    public AbstractApplicationContext() {
        this.logger = LogFactory.getLog(this.getClass());
        // 容器ID 
        // org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2ce86164
        this.id = ObjectUtils.identityToString(this);
        // 容器名称,同上
        this.displayName = ObjectUtils.identityToString(this);
        // 存放BeanFactoryPostProcessor(后置处理器)
        this.beanFactoryPostProcessors = new ArrayList();
        // 是否激活,初始值False
        this.active = new AtomicBoolean();
        // 是否关闭,初始值False
        this.closed = new AtomicBoolean();
        // 监测器
        this.startupShutdownMonitor = new Object();
        // 应用启动记录器
        this.applicationStartup = ApplicationStartup.DEFAULT;
        // 存放应用监听器
        this.applicationListeners = new LinkedHashSet();
        // new PathMatchingResourcePatternResolver 解析类路径下的资源文件
        this.resourcePatternResolver = this.getResourcePatternResolver();
    }

接着是GenericApplicationContext

    public GenericApplicationContext() {
    	// 是否自定义的类加载器
        this.customClassLoader = false;
        // 是否已刷新
        this.refreshed = new AtomicBoolean();
        // BeanFactory
        this.beanFactory = new DefaultListableBeanFactory();
    }

接着到本类中:

    public AnnotationConfigServletWebServerApplicationContext() {
    	// 有注解的类
        this.annotatedClasses = new LinkedHashSet();
        // Bean 定义读取器
        this.reader = new AnnotatedBeanDefinitionReader(this);
        // Bean 定义扫描器
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

最后,通过构造函数,应用上下文就创建成功了,但是大部分属性都是NULL ,这个创建创建阶段只是完成了初始化对象的创建操作。
在这里插入图片描述

2. 准备上下文

接下来进入到prepareContext准备上下文方法中:

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    	// 1. 将环境设置到上下文中
        context.setEnvironment(environment);
        // 2. 设置转换器服务
        this.postProcessApplicationContext(context);
        // 3. 执行初始化
        this.applyInitializers(context);
        // 4. 调用监听器,发布上下文准备完成事件
        listeners.contextPrepared(context);
        // 5. 关闭引导上下文,并发布 BootstrapContextClosedEvent
        bootstrapContext.close(context);
        // 6. 开始打印启动日志了,第1,2行
        //  Starting App1 using Java 1.8.0_151 on DESKTOP-B9ISFQ7 with PID 4400 (D:\data\project\study-demo\gateway-demo\app-service001\target\classes started by TD in D:\data\project\study-demo)
        // No active profile set, falling back to 1 default profile: "default"
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }
		// 获取上下文中的 BeanFactory,添加一些特殊的单例 Bean 
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 7. 注册 springApplicationArguments Bean
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
        	// 注册 springBootBanner
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
		// beanFactory 之前就是 DefaultListableBeanFactory,所以这里都会进入		
        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
        // // 8. 设置是否允许循环引用,默认False
            ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                // 9. 是否允许允许 Bean 定义覆盖,默认false 
                ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }
		// 10. 是否延长初始化,默认False
        if (this.lazyInitialization) {
        	// 添加 LazyInitializationBeanFactoryPostProcessor, 
        	// 可以以延迟bean的加载并加快SpringBoot的启动速度,但是会存在加载时又爆出问题的问题,无法提前检查
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // 11. 名字上来看,是一个属性源进行排序的后置处理器
        context.addBeanFactoryPostProcessor(new SpringApplication.PropertySourceOrderingBeanFactoryPostProcessor(context));
        // 12. 获取启动类名称=》org.pearl.app.App1
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // 13. 加载
        this.load(context, sources.toArray(new Object[0]));
        // 14. 调用监听器
        listeners.contextLoaded(context);
    }

setEnvironment方法中会将环境对象设置给上下文及读取器和扫描器:

    public void setEnvironment(ConfigurableEnvironment environment) {
        super.setEnvironment(environment);
        this.reader.setEnvironment(environment);
        this.scanner.setEnvironment(environment);
    }

postProcessApplicationContext方法只有最后一行代码实际执行了,设置ConversionService给上下文中的Bean 工厂:

    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    	// Bean 名称生成器,这里为NULL 
        if (this.beanNameGenerator != null) {
            context.getBeanFactory().registerSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator", this.beanNameGenerator);
        }
		// 资源加载器,这里也是NULL
        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext)context).setResourceLoader(this.resourceLoader);
            }

            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader)context).setClassLoader(this.resourceLoader.getClassLoader());
            }
        }
        if (this.addConversionService) {
            context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
        }
    }

applyInitializers方法中会调用之前SPI 机制加载的ApplicationContextInitializer的所有实例的初始化方法,

protected void applyInitializers(ConfigurableApplicationContext context) {
        Iterator var2 = this.getInitializers().iterator();
        // 1. 循环所有的ApplicationContextInitializer 
        while(var2.hasNext()) {
            ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
            // 2. 检验是否能被调用
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            // 3. 调用初始化
            initializer.initialize(context);
        }
    }

在之前我们了解过,一共有7 中应用初始化器,最先进入的是DelegatingApplicationContextInitializer,之前我们说过,它是一个委托的初始化器,会加载context.initializer.classes配置的初始化器,然后进行调用,代码如下:
在这里插入图片描述
第二个进入的是SharedMetadataReaderFactoryContextInitializer,它的主要作用是创建了一个Bean 工厂后置处理器并设置到上下文中。

    public void initialize(ConfigurableApplicationContext applicationContext) {
    	// CachingMetadataReaderFactoryPostProcessor 后置处理器
        BeanFactoryPostProcessor postProcessor = new SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor(applicationContext);
        applicationContext.addBeanFactoryPostProcessor(postProcessor);
    }

第三个是ContextIdApplicationContextInitializer,作用为创建一个ContextId对象,并将其注册到Bean 工厂中,

    public void initialize(ConfigurableApplicationContext applicationContext) {
    	// ID 为 spring.application.name 配置属性,没有就使用 application
        ContextIdApplicationContextInitializer.ContextId contextId = this.getContextId(applicationContext);
        applicationContext.setId(contextId.getId());
     	// 注册   
     	applicationContext.getBeanFactory().registerSingleton(ContextIdApplicationContextInitializer.ContextId.class.getName(), contextId);
    }

第四个是ConfigurationWarningsApplicationContextInitializer,主要是添加了警告后置处理器,检查注解扫描的包是否有问题,如果有,则返回警告:

   public void initialize(ConfigurableApplicationContext context) {
   		// 将ConfigurationWarningsPostProcessor后置处理器注入到应用上下文中
        context.addBeanFactoryPostProcessor(new ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor(this.getChecks()));
    }

第五个是RSocketPortInfoApplicationContextInitializer,主要是添加了一个RSocketServerInitializedEvent事件的监听器到上下文中,可以通过该事件将RSocketServer服务器实际监听的端口设置到环境属性中。

    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addApplicationListener(new RSocketPortInfoApplicationContextInitializer.Listener(applicationContext));
    }

第六个是ServerPortInfoApplicationContextInitializer,它本身也是一个监听器,监听的事件为WebServerInitializedEvent,可以将端口设置到环境中,然后方便通过@Valueenvironment获取本地端口号。

    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addApplicationListener(this);
    }

第七个是ConditionEvaluationReportLoggingListener,也是加了一个监听器,可以打印上下文容器成功刷新或失败的日志报告。

    public void initialize(ConfigurableApplicationContext applicationContext) {
    	// 将上下文赋值给自己
        this.applicationContext = applicationContext;
        // 添加了一个ConditionEvaluationReportListener 监听器
        applicationContext.addApplicationListener(new ConditionEvaluationReportLoggingListener.ConditionEvaluationReportListener());
        // 获取ConditionEvaluationReport
        if (applicationContext instanceof GenericApplicationContext) {
            this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
        }

    }

至此所有初始化器的任务完成了,我们的上下文又多了不少东西,有监听器、ID、后置处理器等。
在这里插入图片描述

接着进入到listeners.contextPrepared(context)方法,调用SpringApplicationRunListeners监听器,开始进入下一步骤阶段。

和之前应用启动时一样,也响应的创建一个步骤,名称为spring.boot.application.context-prepared(上下文准备完成),然后调用那些监听器中ApplicationPreparedEvent事件的响应方法,这里就懒得看了,主要有注册springBootLoggingSystemspringBootLoggerGroups、打印日志的一些工作。

接着prepareContext方法又添加了一些后置处理器和加载了一些Bean ,最后进入load方法,将启动类 BeanDefinition注册。

    protected void load(ApplicationContext context, Object[] sources) {
    	// 1. 打印debug 日志
        if (logger.isDebugEnabled()) {
            logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
		// 2. 创建 BeanDefinition 加载器
        BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources);
        // 3. 下面都为NULL 不进入
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }

        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }

        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }
		// 4. 加载
        loader.load();
    }
    private void load(Class<?> source) {
    	// 传入启动类 Class 
    	// 判断是否使用groovy脚本,这里没有,跳过
        if (this.isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
            GroovyBeanDefinitionSource loader = (GroovyBeanDefinitionSource)BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
            ((GroovyBeanDefinitionReader)this.groovyReader).beans(loader.getBeans());
        }
		// 符合条件
        if (this.isEligible(source)) {
        	// 2. 调用AnnotatedBeanDefinitionReader 注册启动类为 BeanDefinition
        	// 怎么注册Bean ,是Spring 去负责的了,这里就不解释了
            this.annotatedReader.register(new Class[]{source});
        }
    }

此时可以看到当前Bean 工厂中,有6个 BeanDefinition。
在这里插入图片描述

最后调用listeners.contextLoaded进行监听器的处理,这里发布的是ApplicationPreparedEvent事件,可以看到有5个监听器符合,然后就是和之前一样,调用每个监听器的监听方法处理事件,这个阶段好像没做很重要的处理,就不解释了。
在这里插入图片描述
处理完成后,prepareContext 这个阶段就完成了,经过创建和准备工作,我们的上下文已经又多了不少东西,接下来就进入刷新方法了。
在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:36:15  更:2022-07-03 10:36:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 19:23:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码