SpringBoot启动流程工作原理

Springboot工作原理:

工作原理包含很多:ioc,自动配置,启动过程,监听,注解,扩展点,SpringFactoriesLoader工厂加载机制

注解:

点击@SpringBootApplication注解后我们可以发现这是个复合注解,可以发现

@SpringbootApplication=@Configuration+@EnableAutoConfiguration+@ComponentScan这三个注解。

@Configuration:用于定义配置类,可替换XML配置文件,被注解的类内部包含一个或多个 @Bean注解方法。可以被AnnotationConfigApplicationContext或者 AnnotationConfigWebApplicationContext进行扫描。用于构建bean定义以及初始化Spring 容器。

(https://www.jianshu.com/p/21f3e074e91a)

@EnableAutoConfiguration:Springboot自动配置的实现就是通过这个注解实现的。借助 @Import的帮助,将所有符合自动配置条件的bean定义加载到ioc容器中。从classpath 中搜寻所有的META-INF/spring.factories配置文件,并将其中 org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射 (java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的ioc容器配 置类,然后汇总为一个并加载到ioc容器。

(https://blog.csdn.net/kmhysoft/article/details/71056027)

@ComponentScan:创建一个配置类,在配置类上添加@ComponentScan注解,该注解默认 会扫描该类所在的包下所有的配置类,相当于之前的<context:component-scan>。

(https://www.jianshu.com/p/64aac6461d5b)

 

启动过程:

当我们启动springboot中的主方法main时,会经历如下过程:

首先创建SpringApplication实例(执行SpringApplication构造方法):

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//1.构造方法中的逻辑:
   this.resourceLoader = resourceLoader;
//2.一定要指定primarySources,也就是我们的主方法所在的类
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//3.deduce(推断)web类型(Servlet,Reactive,NoWeb)
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
//4.从META-INF/spring.factories中获取ApplicationContextInitializer
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
//5.从META-INF/spring.factories中获取ApplicationListrnre
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//6.推断执行的main方法的定义类
   this.mainApplicationClass = deduceMainApplicationClass();
}

ApplicationContextInitializer是Spring提供的用于应用启动时回调的接口,需要实现此接口的initializeinitialize(ConfigurableApplicationContext applicationContext)方法。当Spring启动(即执行run方法)并初始化环境配置以后,完成应用初始化以前,会回调所有ApplicationContextInitializer的子类(当然是已经配置了的子类)。

当实例初始化完后则就开始执行启动方法(Run)了:

public ConfigurableApplicationContext run(String... args) {
//1.StopWatch开启计时
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//这是设置系统属性java.awt.headles(通知系统进入配置模式)
   configureHeadlessProperty();
//2.获取到META-INF/spring.factories中配置的SpringApplicationRunListener
//这个监听器是监听这个run方法的
//可通过它的接口定义,注解了解它的用途
   SpringApplicationRunListeners listeners = getRunListeners(args);
//启动监听器
   listeners.starting();
   try {
//命令行参数包装为ApplicationArguments
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
//3.准备好了Environment(环境),此刻Environment中都有哪些配置参数呢?
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
//4.打印springboot LoGo图表
      Banner printedBanner = printBanner(environment);
//5.创建ApplicationContext
      context = createApplicationContext();
//6.获取到META-ING/spring.factories中配置的SpringBootExceptionReporter
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
//7.准备ApplicationContext
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
//8.刷新ApplicationContext
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass)
               .logStarted(getApplicationLog(), stopWatch);
      }
//9.发布started事件
      listeners.started(context);
//10.执行所有的Runners
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }
   try {
//11.发布running中事件
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
//12.返回ok的ConfigurableApplicationContext
   return context;
}

到此,算是完成了。

环境准备过程,上面的第3步:

private ConfigurableEnvironment prepareEnvironment(
      SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // Create and configure the environment  》》创建和配置环境
//根据应用类型,创建对应的Environment(环境)对象,会装载环境变量,系统参数,具
//体应用类型配置参数
   ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境:加入命令行参数PropertySource,配置启用的profiles
   configureEnvironment(environment, applicationArguments.getSourceArgs());
//触发RunListener环境准备完成回调
   listeners.environmentPrepared(environment);
//将Environment绑定到SpringApplication
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader())
            .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}

创建ApplicationContext即ConfigurableApplicationContext创建与准备过程:

创建过程,上面的第5步:

protected ConfigurableApplicationContext createApplicationContext() {
//逻辑:如果没有变成式指定contextClass,则根据之前推断的webApplicationType来选择对//应默认的ApplicationContext实现类
   Class<?> contextClass = this.applicationContextClass;
   if (contextClass == null) {
      try {
         switch (this.webApplicationType) {
         case SERVLET:
            contextClass = Class.forName(DEFAULT_SERVLET_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);
}

上面Class.forName里的三个参数如下:

准备过程,上面的第7步:

private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
//1.设置环境对象
   context.setEnvironment(environment);
//2.设置ApplicationContext的beanNameGenerator,resourceLoader,addConversionService(如果通过SpringApplication编程方式指定了他们)
   postProcessApplicationContext(context);
//3.应用初始化器对ApplicationContext进行初始化处理(Initializers在构造SpringApplication时就从spring.factories中加载到了)
   applyInitializers(context);
//4.发布ApplicationContext准备妥当事件
   listeners.contextPrepared(context);
//5.打印startup日志信息
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
//6.添加springboot中特定的单例bean到beanfactory中
   // Add boot specific singleton beans
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
//7.加载PrimarySource其他编程式指定的source配置类中的bean定义
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[0]));
//8.发布ApplicationContext加载Bean定义完毕事件
   listeners.contextLoaded(context);
}

刷新过程,run方法中的第8步:

private void refreshContext(ConfigurableApplicationContext context) {
   refresh(context);
   if (this.registerShutdownHook) {
      try {
         context.registerShutdownHook();
      }
      catch (AccessControlException ex) {
         // Not allowed in some environments.
      }
   }
}

/**
 * Refresh the underlying(刷新底层) {@link ApplicationContext}.
 * @param applicationContext the application context to refresh(应用程序上下文刷新)
 */
protected void refresh(ApplicationContext applicationContext) {
   Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
   ((AbstractApplicationContext) applicationContext).refresh();
}

会加载自动配置的配置类,上文run方法的第10步:

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);
      }
   }
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
   try {
      (runner).run(args);
   }
   catch (Exception ex) {
      throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
   }
}

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
   try {
      (runner).run(args.getSourceArgs());
   }
   catch (Exception ex) {
      throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
   }
}

从代码中可以看出,从ApplicationContext获取了所有ApplicationRunner,CommandLineRunner类型的bean,并执行了他们的run方法。

ApplicationRunner和CommandLineRunner是一样的用途,当ApplicationContext刷新好后,用来执行我们的应用逻辑。

比如:

@SpringBootApplication 
public class SpringBootStudyConfigApplication implements ApplicationRunner { 
public static void main(String[] args) { 
SpringApplication.run(SpringBootStudyConfigApplication.class, args); 
}
@Override 
public void run(ApplicationArguments args) throws Exception { 
System.out.println("***************** " + mybean); 
} 
}

除此之外我们还可以用如下方式来执行我们的逻辑:

@SpringBootApplication 
public class SpringBootStudyConfigApplication implements ApplicationRunner { 
public static void main(String[] args) { 
ConfigurableApplicationContext context = 
SpringApplication.run(SpringBootStudyConfigApplication.class, args); 
MyBean mybean = context.getBean(MyBean.class); 
mybean.doService(); 
} 
} 

待完善,望多提意见。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值