Spring Boot 7. 启动配置原理

Spring Boot启动配置原理

启动原理、运行流程、自动配置原理
springboot 2.2.1
代码地址
ssh git@gitee.com:Ding_DangMao/learn-spring-bot.git

    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.2.1.RELEASE</version>
    </parent>

一、启动原理

  • 几个重要的事件回调机制
    配置在 META-INF/spring.factories
    ApplicationContextInitializer
    SpringApplicationRunListener
    只需要放在 ioc容器中
    ApplicationRunner
    CommandLinerRunner
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		//1. 创建 SpringApplication对象 2. 运行 run方法
        return (new SpringApplication(primarySources)).run(args);
    }

运行流程

  1. 创建 SpringApplication对象

    public SpringApplication(Class<?>... primarySources) {
        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");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    	//判断当前应用是不是一个web应用
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
    	//初始化;从类路径下找到 /meta-inf/spring.factories配置的所有 applicationContextInitializer;然后保存起来
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    	//从类路径下找到  /meta-inf/spring.factories 找打配置的所有 ApplicationListener保存起来
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    	//从多个配置类中找到有 main方法主配置类
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
    
    //保存主配置类
    public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
        this.initializers = new ArrayList(initializers);
    }
    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();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    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 {
                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);
            }
        }
    }
    

    image
    image

  2. 运行 run方法
    SpringApplication.run(主程序类)

    public ConfigurableApplicationContext run(String... args) {
		//开始停止的监听
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
		//ioc容器
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
		//System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));
        this.configureHeadlessProperty();
		//获取 SpringApplicationRunListeners !!!从类路径下 META-INF/spring.factories 
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
		//回调所有的  SpringApplicationRunListener的  listener.starting();方法
        listeners.starting();

        Collection exceptionReporters;
        try {
			//封装命令行参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			//准备环境 =》创建环境完成后回调 SpringApplicationRunListener的 
			//listener.environmentPrepared(environment);方法表示环境准备完成
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
			// 打印 beaninfo 图标 System.setProperty("spring.beaninfo.ignore", ignore.toString());
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
			//创建 ApplicationContext!!!决定创建那种类型的 ioc容器
            context = this.createApplicationContext();
			//异常分析报告的
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
			//准备上下文环境:将 environment 保存到 ioc中,而且 applyInitializers()
			//applyInitializers(context) 回调之前保存的所有 ApplicationContextInitializer的initialize(context);方法
			//contextPrepared(context) 回调所有的 SpringApplicationRunListener的listener.contextPrepared(context);方法
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//prepareContext 完全运行完以后 回调所有的 SpringApplicationRun 的 contextLoaded(context);方法
			//刷新上下文 ioc容器初始化,如果是 web应用还会创建嵌入式的 tomcat
			//扫描,创建,加载所有组件的地方
            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);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

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

	//创建 applicationContext对象
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
		//主要决定创建 那种类型的 ioc容器
        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);
            }
        }
		//利用 BeanUtils工具 反射创建 ioc对象
        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }

	//上下文环境
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		//把我们刚刚创建的 environment 保存起来
        context.setEnvironment(environment);
		//后置处理:注册一些小组件
        this.postProcessApplicationContext(context);
		//回调之前保存的所有的 ApplicationContextInitializer的initialize(context);方法
        this.applyInitializers(context);
		//回调所有的 SpringApplicationRunListener的listener.contextPrepared(context);
        listeners.contextPrepared(context);
		//日志记录
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }
		//命令行参数注册进来
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		//包括打印的 springBootBanner 注册
        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]));
        listeners.contextLoaded(context);
    }
    protected void applyInitializers(ConfigurableApplicationContext context) {
        Iterator var2 = this.getInitializers().iterator();
		//获取所有的  Initializers 调用 initialize(context);方法
		//ApplicationContextInitializer 哪里有的那?就是第一次创建 springapplication对象的时候已经拿到了
        while(var2.hasNext()) {
            ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }

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

二、事件监听机制 和配置

  • Spring Boot启动扫描所有jar包的META-INF/spring.factories中配置的EnableAutoConfiguration组件
  • spring-boot-autoconfigure.jar\META-INF\spring.factories有启动时需要加载的EnableAutoConfiguration组件配置
  • 配置文件中使用debug=true可以观看到当前启用的自动配置的信息
  • 自动配置会为容器中添加大量组件
  • Spring Boot在做任何功能都需要从容器中获取这个功能的组件
  • Spring Boot 总是遵循一个标准;容器中有我们自己配置的组件就用我们配置的,没有就用自动配置默认注册进来的组件;
@Component
public class HelloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("HelloApplicationRunner ::" + args);
    }
}
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("HelloCommandLineRunner::" + Arrays.toString(args));
    }
}
public class HelloApplicationContextInitializer implements
        ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("HelloApplicationContextInitializer方法运行了::" + configurableApplicationContext);
    }
}
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {

    public HelloSpringApplicationRunListener(SpringApplication application, String[] args) {
        System.out.println("constructor");
    }

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

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("environmentPrepared...");
    }
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("contextPrepared...");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("contextLoaded...");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("started...");
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("running...");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("failed...");
    }
}

META-INF/spring.factories

org.springframework.context.ApplicationContextInitializer=\
com.cainiao100.springboot.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.cainiao100.springboot.listener.HelloSpringApplicationRunListener

运行结果

constructor
starting...
environmentPrepared...

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

HelloApplicationContextInitializer方法运行了::org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@50a638b5, started on Thu Jan 01 08:00:00 CST 1970
contextPrepared...
2021-11-14 14:22:35.371  INFO 1940 --- [           main] c.cainiao100.springboot.SpringBootMain   : Starting SpringBootMain on DESKTOP-PHJGSH5 with PID 1940 (D:\code\springboot\spring-boot-07\target\classes started by KAlways18 in D:\code\springboot)
2021-11-14 14:22:35.378  INFO 1940 --- [           main] c.cainiao100.springboot.SpringBootMain   : No active profile set, falling back to default profiles: default
contextLoaded...
2021-11-14 14:22:37.411  INFO 1940 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-11-14 14:22:37.424  INFO 1940 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-11-14 14:22:37.425  INFO 1940 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.27]
2021-11-14 14:22:37.650  INFO 1940 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-11-14 14:22:37.650  INFO 1940 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2124 ms
2021-11-14 14:22:37.849  INFO 1940 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-11-14 14:22:38.107  INFO 1940 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-11-14 14:22:38.112  INFO 1940 --- [           main] c.cainiao100.springboot.SpringBootMain   : Started SpringBootMain in 3.577 seconds (JVM running for 5.33)
started...
HelloApplicationRunner ::org.springframework.boot.DefaultApplicationArguments@3ae66c85
HelloCommandLineRunner::[]
running...
posted @ 2021-11-14 12:35  MikiKawai  阅读(92)  评论(0编辑  收藏  举报