SpringBoot(七) 启动配置原理

注意关注:几个重要的事件回调机制

这两个需要配置在META-INF/spring.factories:ApplicationContextInitializerSpringApplicationRunListener

这两个只需要放在ioc容器中:ApplicationRunnerCommandLineRunner

此次讲解的SpringBoot版本是:2.2.6

1、启动配置流程讲解

SpringApplication.run(主程序类)

new SpringApplication(主程序类)
	•判断是否web应用
	•加载并保存所有ApplicationContextInitializer(META-INF/spring.factories),
	•加载并保存所有ApplicationListener
	•获取到主程序类
    
run()
	•回调所有的SpringApplicationRunListener(META-INF/spring.factories)的starting
	•获取ApplicationArguments
	•准备环境&回调所有监听器(SpringApplicationRunListener )的environmentPrepared
	•打印banner信息
	•创建ioc容器对象
  		- AnnotationConfigEmbeddedWebApplicationContext(web环境容器)
  		- AnnotationConfigApplicationContext(普通环境容器)
	•准备环境
		–执行ApplicationContextInitializer. initialize()
		–监听器SpringApplicationRunListener回调contextPrepared
		–加载主配置类定义信息
		–监听器SpringApplicationRunListener回调contextLoaded
	•刷新启动IOC容器;
		–扫描加载所有容器中的组件
		–包括从META-INF/spring.factories中获取的所有EnableAutoConfiguration组件
	•回调容器中所有的ApplicationRunner、CommandLineRunner的run方法
	•监听器SpringApplicationRunListener回调finished

启动流程主要分两步:1、创建SpringApplication对象 2、执行run方法;

程序入口:主程序的main方法:

public static void main(String[] args) {
    SpringApplication.run(SpringBoot07StarterApplication.class, args);
}

打断点之后,debug进去:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}

1.1 创建SpringApplication对象

我们先来看:new SpringApplication(primarySources) 创建SpringApplication对象:

public SpringApplication(Class<?>... primarySources) {
    //调用的就是下面的SpringApplication方法
    this((ResourceLoader)null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = new HashSet();
    this.isCustomEnvironment = false;
    this.lazyInitialization = false;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //保存主配置类到 primarySources 中
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    //判断当前是否是web应用,参加 1.1.1 
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //从各个jar包的类路径下找到META-INF/spring.factories文件配置的所有ApplicationContextInitializer;然后保存起来,参加1.1.2 
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener  参加1.1.2 
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
     //从多个配置类中找到有 main 方法的主配置类
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

1.1.1 判断是否web应用的方法

static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    //非web应用
                    return NONE;
                }
            }
			//表示是web应用
            return SERVLET;
        }
    }

NONE:非web应用, SERVLET:是web应用。

1.1.2 保存类路径下所有ApplicationContextInitializer 方法

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return this.getSpringFactoriesInstances(type, new Class[0]);
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = this.getClassLoader();
    //(SpringFactoriesLoader.loadFactoryNames 加载类路径下的 META-INF/spring.factories
    Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}


//SpringFactoriesLoader 类有如下方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        try {
            //找的路径就是:META-INF/spring.factories
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            LinkedMultiValueMap result = new LinkedMultiValueMap();

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    int var10 = var9.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String factoryImplementationName = var9[var11];
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }

            cache.put(classLoader, result);
            return result;
        } catch (IOException var13) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
        }
    }
}

只有一个web模块的SpringBoot,默认加载的 ApplicationContextInitializer 是:

在这里插入图片描述

默认加载的 ApplicationListener 是:

在这里插入图片描述

1.2 运行run方法

扫描所有的组件,扫描所有的@Bean注解等,给我们初始化

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    this.configureHeadlessProperty();
    //从类路径下META-INF/spring.factories 获取SpringApplicationRunListeners;
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    //回调所有的获取到的SpringApplicationRunListener的starting()方法
    listeners.starting();

    Collection exceptionReporters;
    try {
        //封装命令行参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //准备环境 详情参加:1.2.1 
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        
        //打印Banner,就是启动的时候 SpringBoot的标识
        Banner printedBanner = this.printBanner(environment);
        
        //创建ApplicationContext;决定创建web的ioc还是普通的ioc
        context = this.createApplicationContext();
        
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        
        //准备上下文环境;将environment保存到ioc中;而且调用方法:applyInitializers();
        //applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
        //回调所有的SpringApplicationRunListener的contextPrepared(); 参加 1.2.3 
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        
        //刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);
        //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置),详情参加:1.2.4 
        this.refreshContext(context);
        
        //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
        //ApplicationRunner先回调,CommandLineRunner再回调
        this.afterRefresh(context, applicationArguments);
        
        stopWatch.stop();
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
        }
	    //所有的SpringApplicationRunListener回调finished方法
        listeners.started(context);
        this.callRunners(context, applicationArguments);
    } catch (Throwable var10) {
        this.handleRunFailure(context, var10, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }

    try {
        listeners.running(context);
        //整个SpringBoot应用启动完成以后返回启动的ioc容器;
        return context;
    } catch (Throwable var9) {
        this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var9);
    }
}

1.2.1 准备环境的方法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
    this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach((Environment)environment);
    
    //创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
    listeners.environmentPrepared((ConfigurableEnvironment)environment);
    this.bindToSpringApplication((ConfigurableEnvironment)environment);
    if (!this.isCustomEnvironment) {
        environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
    }

    ConfigurationPropertySources.attach((Environment)environment);
    return (ConfigurableEnvironment)environment;
}

listeners.environmentPrepared((ConfigurableEnvironment)environment); 表示环境准备完成

1.2.2 创建ApplicationContext(IOC)的方法

创建ApplicationContext;决定创建web的ioc还是普通的ioc,如果是web的ioc,返回就是:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
            }
        } catch (ClassNotFoundException var3) {
            throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
        }
    }
	//使用反射创建 IOC 容器
    return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

SERVLET:表示web应用

1.2.3 准备上下文环境的方法

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    this.postProcessApplicationContext(context);
    //回调之前保存的所有的ApplicationContextInitializer的initialize方法
    this.applyInitializers(context);
    //回调所有的SpringApplicationRunListener的contextPrepared();
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        this.logStartupInfo(context.getParent() == null);
        this.logStartupProfileInfo(context);
    }

    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    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());
    }

    Set<Object> sources = this.getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    this.load(context, sources.toArray(new Object[0]));
    //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
    listeners.contextLoaded(context);
}

this.applyInitializers(context);:回调之前保存的所有的ApplicationContextInitializer的initialize方法
listeners.contextPrepared(context);:回调所有的SpringApplicationRunListener的contextPrepared() listeners.contextLoaded(context); :回调所有的SpringApplicationRunListener的contextLoaded();

1.2.4 刷新容器,IOC容器初始化方法

调用的是父类:AbstractApplicationContext 的 refresh方法

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();
            }

        }
}

1.3 事件监听机制

这两个需要配置在META-INF/spring.factories:ApplicationContextInitializerSpringApplicationRunListener

这两个只需要放在ioc容器中:ApplicationRunnerCommandLineRunner

下面我们就来测试一下这四个;

使用SpringBoot的向导创建一个简单的web工程;在resources下面创建META-INF/spring.factories,因为要配置:ApplicationContextInitializerSpringApplicationRunListener

1.3.1 自定义ApplicationContextInitializer

package com.springboot.starter.initializer;

import com.springboot.starter.service.HelloService;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

public class ZdwApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        /**
         * HelloService helloService = configurableApplicationContext.getBean(HelloService.class);
         * 在这里无法拿到自定义的Bean,此时还没有刷新IOC容器,只是做了SpringApplication的初始化操作,
         * 如果从这里取,就会报错:java.lang.IllegalStateException: org.springframework.boot.web.servlet.context.
         * AnnotationConfigServletWebServerApplicationContext@176b75f7 has not been refreshed yet
         */
        System.out.println("-----------------------监听IOC容器的启动-----------------------");
    }
}

1.3.2 自定义SpringApplicationRunListener

package com.springboot.starter.listener;

import com.springboot.starter.service.HelloService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

public class ZdwSpringApplicationRunListeners implements SpringApplicationRunListener {
    private final SpringApplication application;
    private final String[] args;

    public ZdwSpringApplicationRunListeners(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
    }

    @Override
    public void starting() {
        System.out.println("ZdwSpringApplicationRunListeners...starting...");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        String osname = environment.getProperty("os.name");//可以拿到操作系统的名称
        System.out.println("ZdwSpringApplicationRunListeners...environmentPrepared..."+osname);
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        //HelloService helloService = context.getBean(HelloService.class);  这里也无法拿到自定义的Bean
        System.out.println("ZdwSpringApplicationRunListeners...contextPrepared...");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        //HelloService helloService = context.getBean(HelloService.class);  这里也无法拿到自定义的Bean
        System.out.println("ZdwSpringApplicationRunListeners...contextLoaded...");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        //这里可以拿到自定义的Bean,因为他是在 this.refreshContext(context); 刷新容器之后运行的
        //刷新ioc容器的时候,会加载所有的自定义Bean
        HelloService helloService = context.getBean(HelloService.class);
        System.out.println("ZdwSpringApplicationRunListeners...started..."+helloService);
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        HelloService helloService = context.getBean(HelloService.class);
        System.out.println("ZdwSpringApplicationRunListeners...running..."+helloService);
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        HelloService helloService = context.getBean(HelloService.class);
        System.out.println("ZdwSpringApplicationRunListeners...failed..."+helloService);
    }
}

1.3.3 自定义ApplicationRunner

package com.springboot.starter.runner;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component  //添加到容器中
public class ZdwApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ZdwApplicationRunner...run------");
    }
}

1.3.4 自定义CommandLineRunner

package com.springboot.starter.runner;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component  //添加到容器中
public class ZdwCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("ZdwCommandLineRunner...run=======");
    }
}

启动主程序类,看控制台打印:

ZdwSpringApplicationRunListeners...starting...
ZdwSpringApplicationRunListeners...environmentPrepared...Windows 10

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

-----------------------监听IOC容器的启动-----------------------
ZdwSpringApplicationRunListeners...contextPrepared...

2020-04-04 19:57:29.803  INFO 6600 --- [           main] c.s.s.SpringBoot07StarterApplication     : Starting SpringBoot07StarterApplication on DESKTOP-INR89GT with PID 6600 (D:\IDEA\IDEA20190301_WorkSpace\SpringBoot\spring-boot-07-starter\target\classes started by ZDW in D:\IDEA\IDEA20190301_WorkSpace\SpringBoot\spring-boot-07-starter)
2020-04-04 19:57:29.824  INFO 6600 --- [           main] c.s.s.SpringBoot07StarterApplication     : No active profile set, falling back to default profiles: default

ZdwSpringApplicationRunListeners...contextLoaded...

2020-04-04 19:57:31.505  INFO 6600 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-04-04 19:57:31.517  INFO 6600 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-04-04 19:57:31.517  INFO 6600 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-04-04 19:57:31.636  INFO 6600 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-04-04 19:57:31.637  INFO 6600 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1692 ms
2020-04-04 19:57:31.848  INFO 6600 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-04 19:57:32.034  INFO 6600 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-04-04 19:57:32.039  INFO 6600 --- [           main] c.s.s.SpringBoot07StarterApplication     : Started SpringBoot07StarterApplication in 2.919 seconds (JVM running for 5.544)

ZdwSpringApplicationRunListeners...started...com.springboot.starter.service.HelloService@44d70181
ZdwApplicationRunner...run------
ZdwCommandLineRunner...run=======
ZdwSpringApplicationRunListeners...running...com.springboot.starter.service.HelloService@44d70181

1.4 总结

•Spring Boot启动扫描所有jar包的META-INF/spring.factories中配置的EnableAutoConfiguration组件
•spring-boot-autoconfigure.jar\META-INF\spring.factories有启动时需要加载的EnableAutoConfiguration组件配置
•配置文件中使用debug=true可以观看到当前启用的自动配置的信息
•自动配置会为容器中添加大量组件
•Spring Boot在做任何功能都需要从容器中获取这个功能的组件
•Spring Boot 总是遵循一个标准;容器中有我们自己配置的组件就用我们配置的,没有就用自动配置默认注册进来的组件;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值