zoukankan      html  css  js  c++  java
  • 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...
    
  • 相关阅读:
    字符串replaceAll()方法报错:java.util.regex.PatternSyntaxException:Unclosed group near index...
    eclipse导入Tomcat8源码
    [白话解析] 深入浅出朴素贝叶斯模型原理及应用
    [白话解析] 深入浅出一致性Hash原理
    [白话解析] 深入浅出贝叶斯定理
    [白话解析] 深入浅出边缘计算
    [梁山好汉说IT] 用实例来深入理解容器概念
    [梁山好汉说IT] 梁山好汉和抢劫银行
    [梁山好汉说IT] 梁山好汉和秒杀系统
    [梁山好汉说IT] 区块链在梁山的应用
  • 原文地址:https://www.cnblogs.com/zk2020/p/15551406.html
Copyright © 2011-2022 走看看