Spring Boot启动过程源码分析

来源:互联网 发布:柯洁评论黑嘉嘉 知乎 编辑:程序博客网 时间:2024/06/05 00:13
转载自:http://blog.csdn.net/dm_vincent/article/details/76735888
              http://blog.csdn.net/dm_vincent/article/details/77151122

Spring Boot的启动始于:
SpringApplication.run(DemoApplication.class, args);
相应实现:
public static ConfigurableApplicationContext run(Object source, String... args) {return run(new Object[] { source }, args);}
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {return new SpringApplication(sources).run(args);}
它实际上会构造一个SpringApplication的实例,然后运行它的run方法:
public SpringApplication(Object... sources) {initialize(sources);}
private void initialize(Object[] sources) {if (sources != null && sources.length > 0) {this.sources.addAll(Arrays.asList(sources));}this.webEnvironment = deduceWebEnvironment();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}
deduceWebEnvironment()是推断应用类型是Standard还是Web,方法是在当类路径中找是否存在"javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext"的类来判断的。

然后设置初始化器,会从类路径的META-INF/spring.factories处读取相应配置文件,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。以spring-boot这个包为例,它的META-INF/spring.factories部分定义如下所示:
# Application Context Initializersorg.springframework.context.ApplicationContextInitializer=\org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\org.springframework.boot.context.ContextIdApplicationContextInitializer,\org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
因此这几个类名会实例化出来放在SpringApplication 的initializers中。

之后设置监听器,同上读取配置文件中Key为:org.springframework.context. ApplicationListener的value,然后加载实例化后,设置在listeners中。
# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.ClearCachesApplicationListener,\org.springframework.boot.builder.ParentContextCloserApplicationListener,\org.springframework.boot.context.FileEncodingApplicationListener,\org.springframework.boot.context.config.AnsiOutputApplicationListener,\org.springframework.boot.context.config.ConfigFileApplicationListener,\org.springframework.boot.context.config.DelegatingApplicationListener,\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\org.springframework.boot.logging.ClasspathLoggingApplicationListener,\org.springframework.boot.logging.LoggingApplicationListener

然后设置mainApplicationClass:
private Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) {// Swallow and continue}return null;}
它通过构造一个运行时异常,通过异常栈中方法名为main的栈帧来得到入口类的名字。至此,对于SpringApplication实例的初始化过程就结束了。

完成了实例化,下面开始调用run方法:
public ConfigurableApplicationContext run(String... args) {                // 计时工具StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;FailureAnalyzers analyzers = null;                // 设置java.awt.headless系统属性为true - 没有图形化界面configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);    ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);                               // Banner打印器,就是启动Spring Boot的时候打印在console上的    Banner printedBanner = printBanner(environment);                    // 创建Spring上下文    context = createApplicationContext();    analyzers = new FailureAnalyzers(context);                    // Spring上下文前置处理    prepareContext(context, environment, listeners, applicationArguments,printedBanner);    refreshContext(context);                    // Spring上下文后置处理     afterRefresh(context, applicationArguments);    listeners.finished(context, null);    stopWatch.stop();    if (this.logStartupInfo) {    new StartupInfoLogger(this.mainApplicationClass)    .logStarted(getApplicationLog(), stopWatch);    }    return context;}catch (Throwable ex) {handleRunFailure(context, listeners, analyzers, ex);throw new IllegalStateException(ex);}}
createApplicationContext()即创建Spring的主要组件ApplicationContext,对于Web应用,上ApplicationContext就是AnnotationConfigEmbeddedWebApplicationContext。refreshContext(context)即刷新ApplicationContext:
private void refreshContext(ConfigurableApplicationContext context) {refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}}
最终会调用到我们熟知的AbstractApplicationContext的refresh()方法。

上下文后置处理afterRefresh()方法:
protected void afterRefresh(ConfigurableApplicationContext context,ApplicationArguments args) {callRunners(context, args);}private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<Object>();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);for (Object runner : new LinkedHashSet<Object>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}}}
所谓的后置操作,就是在容器完成刷新后,依次调用注册的Runners。Runners可以是两个接口的实现类:org.springframework.boot.ApplicationRunner与org.springframework.boot.CommandLineRunner。


基于上面的了解,我们就可以在Spring Boot启动添加自定义的初始化器、监听器等。
添加定制化的初始化器:
public static void main(String[] args) {    // 创建SpringApplication的实例    SpringApplication app = new SpringApplication(Application.class);    // 添加定制的初始化器    app.addInitializers(new CustomApplicationContextInitializer());    // 执行SpringApplication实例的run方法    app.run(args);}
public class CustomApplicationContextInitializer implements ApplicationContextInitializer {    private static final Logger LOGGER = LoggerFactory.getLogger(CustomApplicationContextInitializer.class);    @Override    public void initialize(ConfigurableApplicationContext applicationContext) {        LOGGER.info("自定义的初始化器的initialize方法被执行了");    }}

添加定制化的监听器:
app. addListeners(new CustomApplicationListener());
public class CustomApplicationListener implements ApplicationListener {    private static final Logger LOGGER = LoggerFactory.getLogger(CustomApplicationListener.class);    public void onApplicationEvent(ApplicationEvent event) {        // 监听ApplicationStartingEvent        if (event instanceof ApplicationStartedEvent) {            logInfo("ApplicationStartedEvent listened");        }        // 监听ApplicationEnvironmentPreparedEvent        else if (event instanceof ApplicationEnvironmentPreparedEvent) {            logInfo("ApplicationEnvironmentPreparedEvent listened");        }        // 监听ApplicationPreparedEvent        else if (event instanceof ApplicationPreparedEvent) {            logInfo("ApplicationPreparedEvent listened");        }        // 监听ApplicationReadyEvent        else if (event instanceof ApplicationReadyEvent) {            logInfo("ApplicationReadyEvent listened");        }        // 监听ApplicationFailedEvent        else if (event instanceof ApplicationFailedEvent) {            logInfo("ApplicationFailedEvent listened");        }    }    private void logInfo(String log) {        if (LOGGER.isInfoEnabled()) {            LOGGER.info(log);        }    }}

添加定制化的后置Runners:
@Component@Order(2)public class CustomApplicationRunner implements ApplicationRunner {  private static final Logger LOGGER = LoggerFactory.getLogger(CustomApplicationRunner.class);  @Override  public void run(ApplicationArguments arg) {      if (LOGGER.isInfoEnabled()) {          LOGGER.info("自定义ApplicationRunner运行了");      }  }}
@Component@Order(1)public class CustomCommandLineRunner implements CommandLineRunner {  private static final Logger LOGGER = LoggerFactory.getLogger(CustomCommandLineRunner.class);  @Override  public void run(String... strings) throws Exception {      if (LOGGER.isInfoEnabled()) {          LOGGER.info("自定义CommandLineRunner运行了");      }  }}
       自定义Runner的添加方式和上述的初始化器以及监听器有点不同,就是Runners直接通过@Component注解添加即可,借助于包扫描机制,它们会被注册到容器中。至于它们运行的顺序,则可以通过上述的@Order注解来完成标注,数值越小的Runner优先级越高,因为源码中有AnnotationAwareOrderComparator.sort(runners),因此上面代码中,CustomCommandLineRunner将先于CustomApplicationRunner被执行。

启动打印Banner:
扩展的方式也很简单,看源代码就清楚该怎么扩展了:
private Banner printBanner(ConfigurableEnvironment environment) {if (this.bannerMode == Banner.Mode.OFF) {return null;}ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader: new DefaultResourceLoader(getClassLoader());SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);if (this.bannerMode == Mode.LOG) {return bannerPrinter.print(environment, this.mainApplicationClass, logger);}return bannerPrinter.print(environment, this.mainApplicationClass, System.out);}




原创粉丝点击