zoukankan      html  css  js  c++  java
  • SpringBoot到底run了什么

    我们详细描述了SpringApplication对象实例的创建过程,本篇文章继续看run方法的执行逻辑吧

    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);
    		//后面还有,本篇文章就解析到这。。。。
    }
    
    1. 第一行使用了StopWatch来记录开始时间
    2. 设置了java.awt.headless环境变量,在网上了解了一下这个变量的相关信息

    Headless模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式

    个人理解为是一些图形相关的组件能否使用的开关,欢迎各位大佬指正

    1. 接着遍历所有构造SpringApplication实例时加载的SpringApplicationRunListener,调用它们的started方法

    这里构造时仅仅加载了一个EventPublishingRunListener类,所以咱们就来解析一下这个东东

    public void starting() {
        this.initialMulticaster.multicastEvent(
                new ApplicationStartingEvent(this.application, this.args));
    }
    

    可以看到这里调用了SimpleApplicationEventMulticaster类的multicastEvent方法并且传入了ApplicationStartingEvent对象,看名字就知道了这个是SpringBoot启动事件

    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }
    

    其中获取监听器使用的是getApplicationListeners方法,这个方法中主要就是从最启动时获取的所有监听器和这个事件做了下匹配,返回通过匹配的监听器集合

    接着就是看是否设置线程池参数,如果有线程池则使用线程池的线程进行操作,否则将同步调用监听器

    1. 把所有的命令行启动参数封装成ConfigurableEnvironment对象

    2. 准备运行时环境

    private ConfigurableEnvironment prepareEnvironment(
    		SpringApplicationRunListeners listeners,
    		ApplicationArguments applicationArguments) {
    	ConfigurableEnvironment environment = getOrCreateEnvironment();
    	configureEnvironment(environment, applicationArguments.getSourceArgs());
    	listeners.environmentPrepared(environment);
    	if (!this.webEnvironment) {
    		environment = new EnvironmentConverter(getClassLoader())
    				.convertToStandardEnvironmentIfNecessary(environment);
    	}
    	return environment;
    }
    
    获取或创建环境getOrCreateEnvironment

    方法名就很直观,有就直接获取,没有就新建

    private ConfigurableEnvironment getOrCreateEnvironment() {
    	if (this.environment != null) {
    		return this.environment;
    	}
    	if (this.webApplicationType == WebApplicationType.SERVLET) {
    		return new StandardServletEnvironment();
    	}
    	return new StandardEnvironment();
    }
    

    上篇文章中说过了,咱们是Servlet环境,所以当前方法是返回一个StandardServletEnvironment对象,这个对象的构造过程中调用了customizePropertySources方法(它父类的父类调用的)

    protected void customizePropertySources(MutablePropertySources propertySources) {
    	propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
    	propertySources.addLast(new StubPropertySource("servletContextInitParams"));
    	if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
    		propertySources.addLast(new JndiPropertySource("jndiProperties"));
    	}
    	super.customizePropertySources(propertySources);
    }
     //这是它父类的
    protected void customizePropertySources(MutablePropertySources propertySources) {
    	propertySources.addLast(new MapPropertySource("systemProperties", getSystemProperties()));
    	propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment()));
    }
    

    可以看出StandardServletEnvironmentpropertySources中添加了两个StubPropertySource对象,而它的父类添加了一个包含java系统属性和一个操作系统环境变量的对象

    配置 configureEnvironment
    protected void configureEnvironment(ConfigurableEnvironment environment,
        String[] args) {
        // 配置PropertySources
        configurePropertySources(environment, args);
        // 配置Profiles
        configureProfiles(environment, args);
    }
    

    分别看一下两个方法

    配置PropertySources
    protected void configurePropertySources(ConfigurableEnvironment environment,
            String[] args) {
        MutablePropertySources sources = environment.getPropertySources();
        if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
            // 存在默认配置将其放到最后位置
            sources.addLast(
                    new MapPropertySource("defaultProperties", this.defaultProperties));
        }
        // 如果存在命令行参数则将原有的替换掉
        if (this.addCommandLineProperties && args.length > 0) {
            String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
            if (sources.contains(name)) {
                PropertySource<?> source = sources.get(name);
                CompositePropertySource composite = new CompositePropertySource(name);
                composite.addPropertySource(new SimpleCommandLinePropertySource(
                        "springApplicationCommandLineArgs", args));
                composite.addPropertySource(source);
                sources.replace(name, composite);
            }
            else {
                // 将其放到第一位置
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
        }
    }
    

    这里就体现出了这个命令行参数比应用配置文件的优先级高的情况了

    配置Profiles

    从PropertySources中查找spring.profiles.active属性,存在则将其值添加activeProfiles集合中

    protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
        environment.getActiveProfiles();
        Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
        profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
        environment.setActiveProfiles(StringUtils.toStringArray(profiles));
    }
    
    发布EnvirongmentPreparedEvent事件
    绑定环境
    protected void bindToSpringApplication(ConfigurableEnvironment environment) {
        try {
            Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
        }
        catch (Exception ex) {
            throw new IllegalStateException("Cannot bind to SpringApplication", ex);
        }
    }
    
    转换环境

    如果web环境变更为NONE则将StandardServletEnvironment转换为StandardEnvironment

    ConfigurationPropertySources.attach(environment)
    public static void attach(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        MutablePropertySources sources = ((ConfigurableEnvironment) environment)
            .getPropertySources();
        PropertySource<?> attached = sources.get("configurationProperties");
        if (attached != null && attached.getSource() != sources) {
            sources.remove("configurationProperties");
            attached = null;
        }
        if (attached == null) {
            sources.addFirst(new ConfigurationPropertySourcesPropertySource(
                "configurationProperties",
                new SpringConfigurationPropertySources(sources)));
        }
    }
    

    最终这个sources对象的第一个位置放的是它自己,循环引用,这个具体的含义

    public ConfigurableApplicationContext run(String... args) {
               //。。。
            //接上文继续
    		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, listeners, exceptionReporters, ex);
    		throw new IllegalStateException(ex);
    	}
    	listeners.running(context);
    	return context;
    }
    
    1. 获取系统属性spring.beaninfo.ignore
    private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
        if (System.getProperty(
            CachedIntrospectionResults."spring.beaninfo.ignore") == null) {
            Boolean ignore = environment.getProperty("spring.beaninfo.ignore",
                                                     Boolean.class, Boolean.TRUE);
            System.setProperty(CachedIntrospectionResults."spring.beaninfo.ignore",
                               ignore.toString());
        }
    }
    

    但是这个属性的作用还真不知道。。

    1. 打印banner

    2. 根据当前环境创建ApplicationContext

    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                    case SERVLET:
                        contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                        break;
                    case REACTIVE:
                        contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                        break;
                    default:
                        contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                    "Unable create a default ApplicationContext, "
                    + "please specify an ApplicationContextClass",
                    ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    

    基于咱们的Servlet环境,所以创建的ApplicationContext为AnnotationConfigServletWebServerApplicationContext

    1. 加载SpringBootExceptionReporter,这个类里包含了SpringBoot启动失败后异常处理相关的组件
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Set<String> names = new LinkedHashSet<>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                                                           classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    
    1. 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);
        }
    
        context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }
    
        // 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);
    }
    
    1. 第一行,将context中相关的environment全部替换
    public void setEnvironment(ConfigurableEnvironment environment) {
        super.setEnvironment(environment);            // 设置context的environment
        this.reader.setEnvironment(environment);    // 实例化context的reader属性的conditionEvaluator属性
        this.scanner.setEnvironment(environment);    // 设置context的scanner属性的environment属性
    }
    
    1. 上下文后处理
    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
        if (this.beanNameGenerator != null) {
            context.getBeanFactory().registerSingleton(
                    AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                    this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext) context)
                        .setResourceLoader(this.resourceLoader);
            }
            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader) context)
                        .setClassLoader(this.resourceLoader.getClassLoader());
            }
        }
    }
    

    这一块默认beanNameGeneratorresourceLoader都是空的,只有当我们自定义这两个对象时才会把容器内的bean替换

    1. 执行所有的ApplicationContextInitializerinitialize方法
    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);
        }
    }
    
    1. listeners.contextPrepared(context)这是个空方法,没有实现,一个Spring的扩展点
    2. 打印profile
    3. 注册bean:springApplicationArguments
    4. 发布事件
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (ApplicationListener<?> listener : this.application.getListeners()) {
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        this.initialMulticaster.multicastEvent(
            new ApplicationPreparedEvent(this.application, this.args, context));
    }
    

    这里不仅发布了ApplicationPreparedEvent事件,还往实现了ApplicationContextAware接口的监听器中注入了context容器

    1. load,其实就是创建了一个BeanDefinitionLoader对象
    protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug(
                "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        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();
    }
    
    1. 容器的初始化refreshContext
      这个方法最后还是调用的AbstractApplicationContext类的refresh方法,由于篇幅过长这里就不展开了,感兴趣的同学可以参考这篇文章:基于注解的SpringIOC源码解析
    public void refresh() throws BeansException, IllegalStateException {
       synchronized (this.startupShutdownMonitor) {
          // 记录容器的启动时间、标记“已启动”状态、检查环境变量
          prepareRefresh();
          // 初始化BeanFactory容器、注册BeanDefinition
          ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
          // 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
          prepareBeanFactory(beanFactory);
          try {
             // 扩展点
             postProcessBeanFactory(beanFactory);
             // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
             invokeBeanFactoryPostProcessors(beanFactory);
             // 注册 BeanPostProcessor 的实现类
             registerBeanPostProcessors(beanFactory);
             // 初始化MessageSource
             initMessageSource();
             // 初始化事件广播器
             initApplicationEventMulticaster();
             // 扩展点
             onRefresh();
             // 注册事件监听器
             registerListeners();
             // 初始化所有的 singleton beans
             finishBeanFactoryInitialization(beanFactory);
             // 广播事件
             finishRefresh();
          }
          catch (BeansException ex) {
             if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                      "cancelling refresh attempt: " + ex);
             }
             // 销毁已经初始化的的Bean
             destroyBeans();
             // 设置 'active' 状态
             cancelRefresh(ex);
             throw ex;
          }
          finally {
             // 清除缓存
             resetCommonCaches();
          }
       }
    }
    
    1. afterRefresh 这里没有任何实现,Spring留给我们的扩展点

    2. 停止之前启动的计时装置,然后发送ApplicationStartedEvent事件

    3. 调用系统中ApplicationRunner以及CommandLineRunner接口的实现类,关于这两个接口的使用可以参考我的这篇文章:Java项目启动时执行指定方法的几种方式

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
    
    1. 异常处理

    2. 发送ApplicationReadyEvent事件

  • 相关阅读:
    520了,用32做个简单的小程序
    sql使用手册
    大厂Redis高并发场景设计,面试问的都在这!
    如何根据普通ip地址获取当前地理位置
    理解Python闭包,这应该是最好的例子
    520了,用32做个简单的小程序
    适合 C++ 新手学习的开源项目——在 GitHub 学编程
    寄存器(内存访问)01 零基础入门学习汇编语言13
    寄存器(CPU工作原理)07 零基础入门学习汇编语言12
    数组08 零基础入门学习C语言30
  • 原文地址:https://www.cnblogs.com/zhaokejin/p/15626393.html
Copyright © 2011-2022 走看看