• 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    核心类

    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);
        }
    
    • 1
    • 2
    • 3

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

    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
    
    • 1

    所以直接进入了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);
    		}
    	};
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

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

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

    学过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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    接着是GenericApplicationContext

        public GenericApplicationContext() {
        	// 是否自定义的类加载器
            this.customClassLoader = false;
            // 是否已刷新
            this.refreshed = new AtomicBoolean();
            // BeanFactory
            this.beanFactory = new DefaultListableBeanFactory();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    接着到本类中:

        public AnnotationConfigServletWebServerApplicationContext() {
        	// 有注解的类
            this.annotatedClasses = new LinkedHashSet();
            // Bean 定义读取器
            this.reader = new AnnotatedBeanDefinitionReader(this);
            // Bean 定义扫描器
            this.scanner = new ClassPathBeanDefinitionScanner(this);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最后,通过构造函数,应用上下文就创建成功了,但是大部分属性都是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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

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

        public void setEnvironment(ConfigurableEnvironment environment) {
            super.setEnvironment(environment);
            this.reader.setEnvironment(environment);
            this.scanner.setEnvironment(environment);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

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

    第三个是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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

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

        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.addApplicationListener(new RSocketPortInfoApplicationContextInitializer.Listener(applicationContext));
        }
    
    • 1
    • 2
    • 3

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

        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.addApplicationListener(this);
        }
    
    • 1
    • 2
    • 3

    第七个是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());
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    至此所有初始化器的任务完成了,我们的上下文又多了不少东西,有监听器、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});
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

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

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

  • 相关阅读:
    期货开户手续费的组成和收费模式
    Java的基础框架之SpringMvc第二讲(SSM框架整合和拦截器)
    ⑭霍兰德RS*型如何选专业?高考志愿填报选专业
    高校为什么需要大数据挖掘平台?
    【Git】bad signature 0x00000000 index file corrupt. fatal: index file corrupt
    中文转拼音的方法
    【无标题】
    Mac/Wins Matlab如何查看APPs源码
    2407. 最长递增子序列 II(dp)
    CSS之垂直水平居中的背后
  • 原文地址:https://blog.csdn.net/qq_43437874/article/details/125510420