美文网首页
SpringBoot 原理 (二): run

SpringBoot 原理 (二): run

作者: 龙白一梦 | 来源:发表于2016-01-05 14:13 被阅读8450次

    SpringApplication 在各种赋值都完成, 初始化部分已完成,下一步是根据初始化的各种配置,对应用进行启动,包括bean的加载,配置文件的读入等等, 调用的方法为:

    springApplication.run(args);
    

    源码(版本为Spring-boot-1.3.0.M5)如下:

    public ConfigurableApplicationContext run(String... args) {
     StopWatch stopWatch = new StopWatch();
     stopWatch.start();
     ConfigurableApplicationContext context = null;
     configureHeadlessProperty();
     SpringApplicationRunListeners listeners = getRunListeners(args);
     listeners.started();
     try {
     context = doRun(listeners, args);
     stopWatch.stop();
     if (this.logStartupInfo) {
     new StartupInfoLogger(this.mainApplicationClass).logStarted(
     getApplicationLog(), stopWatch);
     }
     return context;
     }
     catch (Throwable ex) {
     try {
     listeners.finished(context, ex);
     this.log.error("Application startup failed", ex);
     }
     finally {
     if (context != null) {
     context.close();
     }
     }
     ReflectionUtils.rethrowRuntimeException(ex);
     return context;
     }
     }
    

    1. StopWatch 监控

    用来记录任务的启动 结束时间,是一个非线程的安全的,如果自己使用要考虑多线程的情况.

    2. 配置Headless模式, configureHeadlessProperty

    private void configureHeadlessProperty() {
     System.setProperty(
     SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
     System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
     Boolean.toString(this.headless)));
     }
    

    用来设置java.awt.headless 属性是true 还是false, java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true.

    3. SpringApplicationRunListener 对ApplicationListener事件处理

    在初始化的时候 我们加载了一批 ApplicationListener,包括springboot自启动的 ,也可以有用户定义ApplicationListener,SpringApplicationRunListener就负责来加载ApplicationListener中的业务.

    /**
      * Called immediately when the run method has first started. Can be used for very
      * early initialization.
      */
     void started();
    
     /**
      * Called once the environment has been prepared, but before the
      * {@link ApplicationContext} has been created.
      * @param environment the environment
      */
     void environmentPrepared(ConfigurableEnvironment environment);
    
     /**
      * Called once the {@link ApplicationContext} has been created and prepared, but
      * before sources have been loaded.
      * @param context the application context
      */
     void contextPrepared(ConfigurableApplicationContext context);
    
     /**
      * Called once the application context has been loaded but before it has been
      * refreshed.
      * @param context the application context
      */
     void contextLoaded(ConfigurableApplicationContext context);
    
     /**
      * Called immediately before the run method finishes.
      * @param context the application context
      * @param exception any run exception or null if run completed successfully.
      */
     void finished(ConfigurableApplicationContext context, Throwable exception);
    

    ApplicationListener可以实现四中event类型, 分别在不同的时候加载, ApplicationEnvironmentPreparedEvent,ApplicationPreparedEvent,ApplicationFailedEvent , ApplicationStartedEvent.
    在run的方法中分别使用在:

    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.started();
    listeners.environmentPrepared(environment);
    listeners.contextPrepared(context);
    listeners.finished(context, null);
    

    4. doRun 方法实现

    private ConfigurableApplicationContext doRun(SpringApplicationRunListeners listeners,
     String... args) {
     ConfigurableApplicationContext context;
     // Create and configure the environment
     ConfigurableEnvironment environment = getOrCreateEnvironment();
     configureEnvironment(environment, args);
     listeners.environmentPrepared(environment);
     if (this.showBanner) {
     printBanner(environment);
     }
    
     // Create, load, refresh and run the ApplicationContext
     context = createApplicationContext();
     if (this.registerShutdownHook) {
     try {
     context.registerShutdownHook();
     }
     catch (AccessControlException ex) {
     // Not allowed in some environments.
     }
     }
     context.setEnvironment(environment);
     postProcessApplicationContext(context);
     applyInitializers(context);
     listeners.contextPrepared(context);
     if (this.logStartupInfo) {
     logStartupInfo(context.getParent() == null);
     }
    
     // Add boot specific singleton beans
     ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
     context.getBeanFactory().registerSingleton("springApplicationArguments",
     applicationArguments);
    
     // Load the sources
     Set<Object> sources = getSources();
     Assert.notEmpty(sources, "Sources must not be empty");
     load(context, sources.toArray(new Object[sources.size()]));
     listeners.contextLoaded(context);
    
     // Refresh the context
     refresh(context);
     afterRefresh(context, applicationArguments);
     listeners.finished(context, null);
     return context;
     }```
    #####a. 创建和配置环境相关 
     i. 创建 environment 
            首先根据初始化的配置创建environment的类型, 后面或者property     和profile都是通过这个类来获取的,然后configureEnvironment 就是用来Property和Profiled的.
         
    ii. 启动说监听器的environmentPrepared方法,会调用所有监听器中监听ApplicationEnvironmentPreparedEvent的方法,具体逻辑在上一步中已经描述了.
    
     iii. printBanner 
              打印启动的banner,工业化的项目中可以对此进行定制,使用Banner类即可.
    
    #####b. 创建 加载 刷新 运行ApplicationContext 
     i.创建context
       ```java
    protected ConfigurableApplicationContext createApplicationContext() {
     Class<?> contextClass = this.applicationContextClass;
     if (contextClass == null) {
     try {
     contextClass = Class
     .forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS
     : DEFAULT_CONTEXT_CLASS);
     }
     catch (ClassNotFoundException ex) {
     throw new IllegalStateException(
     "Unable create a default ApplicationContext, "
     + "please specify an ApplicationContextClass", ex);
     }
     }
     return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
     }   
    

    根据是否是webEnvironment 创建不同的context,是创建的是AnnotationConfigEmbedded,WebApplicationContext, 不是创建的默认的 AnnotationConfig, ApplicationContext.

    ii.注册关闭的钩子,主要实现当JVM关闭时候,做一些关闭处理,如销毁bean,生命周期关闭等, 实现方式是

     Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    

    JVM关闭时会自动调用.

    iii. initializer 初始化 日志启动等其他操作.

    c. 配置spring boot特有的单例bean 如commandline相关
    d.加载资源,就是在初始化过程中source包含的类
    e.context 重新刷新

    这个主要都走spring 框架的AbstractApplicationContext#refresh,是对context中的各种资源进行加载, 由于不是boot特有,这里不仔细描述

    f.监听器实现所有finished操作

    主要的实现ApplicationReadyEvent 或者 ApplicationFailedEvent.

    至此,springboot启动过程分析完成了.

    5. 后记

    本身主要研究分析了boot本身的代码的情况,并没有对spring的代码 和 每个监听器功能进行深入分析后面可开专门文章对此进行补充 ,方便不熟悉spring的同学理解.

    相关文章

      网友评论

          本文标题:SpringBoot 原理 (二): run

          本文链接:https://www.haomeiwen.com/subject/xjlzhttx.html