SpringBoot源码 - 启动

SpringBoot Application启动部分的源码阅读.

SpringApplication

常用的SpringApplication.run(Class, Args)启动Spring应用, 创建或者更新ApplicationContext

静态方法run

使用source类实例化一个SpringApplication实例, 并调用实例方法run.

1
2
3
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
  return new SpringApplication(sources).run(args);
}

初始化initialize

  1. 实例化的时候首先通过尝试加载javax.servlet.Servletorg.springframework.web.context.ConfigurableWebApplicationContext推断当前是否是web环境.

  2. 然后从spring.factories获取ApplicationContextInitializer的实现类.

  3. spring.factories获取ApplicationListener的实现类

  4. 推断出应用的启动类(包含main方法的类): 检查线程栈中元素的方法名是否是main

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    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;
    }

到此实例化就完成了.

实例方法run

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public ConfigurableApplicationContext run(String... args) {
  StopWatch stopWatch = new StopWatch();
  stopWatch.start();
  ConfigurableApplicationContext context = null;
  //默认设置java.awt.headless为true
  configureHeadlessProperty(); 
  //从spring.factories中获取org.springframework.boot.SpringApplicationRunListener的实现类
  SpringApplicationRunListeners listeners = getRunListeners(args);
  //通过EventPublishingRunListener发布started事件
  listeners.started();
  try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(
        args);
    //重点: 创建更新上下文对象
    context = createAndRefreshContext(listeners, applicationArguments);
    //上下文对象更新完调用
    afterRefresh(context, applicationArguments);
    //通过EventPublishingRunListener发布finished事件
    listeners.finished(context, null);
    stopWatch.stop();
    if (this.logStartupInfo) {
      new StartupInfoLogger(this.mainApplicationClass)
          .logStarted(getApplicationLog(), stopWatch);
    }
    return context;
  }
  catch (Throwable ex) {
    handleRunFailure(context, listeners, ex);
    throw new IllegalStateException(ex);
  }
}

SpringApplicationRunListener

监听SpringApplicationrun方法. 通过SpringFactoriesLoader加载, 实现时需要提供public的构造方法接受SpringApplicationString[]为参数. 事件的发生顺序为started -> environmentPrepared -> contextPrepared -> contextLoaded -> finished.

SpringBoot默认使用EventPublishingRunListener这个实现类, 将各个事件封装并发布出去, 最终被ApplicationListener捕获.

1
2
3
4
5
6
7
public interface SpringApplicationRunListener {
void started();
void environmentPrepared(ConfigurableEnvironment environment);
void contextPrepared(ConfigurableApplicationContext context);
void contextLoaded(ConfigurableApplicationContext context);
void finished(ConfigurableApplicationContext context, Throwable exception);
}

创建并更新上下文对象createAndRefreshContext

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
private ConfigurableApplicationContext createAndRefreshContext(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
  ConfigurableApplicationContext context;
  // Create and configure the environment
  //获取或创建环境实例, web环境使用StandardServletEnvironment, 非web环境使用StandardEnvironment
  ConfigurableEnvironment environment = getOrCreateEnvironment();
  //配置环境数据 
  //1. **commandLineArgs**属性从启动参数中解析, 格式"--name=value"
  //2. 配置profiles. 有效的profile(通过**spring.profiles.active**配置) 和 通过SpringApplication.profiles()指定的额外profile
  configureEnvironment(environment, applicationArguments.getSourceArgs());
  //通过EventPublishingRunListener发布environmentPrepared事件
  listeners.environmentPrepared(environment);
  //如果是web环境, 将非web环境实例转换成web环境实例: 
  //使用有效的profile配置和jndiProperties, servletConfigInitParams, servletContextInitParams的配置.
  if (isWebEnvironment(environment) && !this.webEnvironment) {
    environment = convertToStandardEnvironment(environment);
  }
  //输出banner
  if (this.bannerMode != Banner.Mode.OFF) {
    printBanner(environment);
  }

  //创建上下文对象, 没有指定实现类的话(使用SpringApplicationBuilder.contextClass), 使用默认context类. 然后通过反射实例化上下文对象.
  //1. web环境使用org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
  //2. org.springframework.context.annotation.AnnotationConfigApplicationContext
  
  //初始化实例的时候会做很多事, 
  //1. 创建AnnotatedBeanDefinitionReader. 注册相关的Annotation Post Processor, 包括: ConfigurationClassPostProcessor(处理@Configuration标注的类), AutowiredAnnotationBeanPostProcessor, RequiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor, EventListenerMethodProcessor, DefaultEventListenerFactory
  //2. 创建ClassPathBeanDefinitionScanner. 扫描器, 扫描默认的过滤器@Service, @Component, @Registry, @Controller. 同时支持J2EE6的@ManagedBean和@Named
  // Create, load, refresh and run the ApplicationContext
  context = createApplicationContext();
  //设置环境
  context.setEnvironment(environment);
  //后续的处理
  postProcessApplicationContext(context);
  //应用初始化器(ApplicationContextInitializer的实现类), 对上下文对象做更多初始化的操作, 比如: 
  //1. 添加BeanFactoryPostProcessor
  //2 .设置上下文对象id
  //3 .代理配置中context.initializer.classes指定的初始化类
  //4. 添加listener, 在web容器启动后更新环境变量中的端口号(server.ports中的local.server.port)
  applyInitializers(context);
  //通过EventPublishingRunListener发布contextPrepared事件
  listeners.contextPrepared(context);
  //打印启动信息和有效的profile信息
  if (this.logStartupInfo) {
    logStartupInfo(context.getParent() == null);
    logStartupProfileInfo(context);
  }

  //将ApplicationArguments实例注册到BeanFactory中, 名字为springApplicationArguments
  // Add boot specific singleton beans
  context.getBeanFactory().registerSingleton("springApplicationArguments",
      applicationArguments);

  //从source(可以是Resource, Package, CharSequence或者Class. 从run方法进来的为Class)类加载Bean到上下文对象中
  // Load the sources
  Set<Object> sources = getSources();
  Assert.notEmpty(sources, "Sources must not be empty");
  load(context, sources.toArray(new Object[sources.size()]));
  //通过EventPublishingRunListener发布contextLoaded事件
  listeners.contextLoaded(context);

  //更新上下文对象, 调用ApplicationContext.refresh()方法
  // Refresh the context
  refresh(context);
  if (this.registerShutdownHook) {
    try {
      context.registerShutdownHook();
    }
    catch (AccessControlException ex) {
      // Not allowed in some environments.
    }
  }
  return context;
}

更新上下文 ApplicationContext.refresh()

  1. prepareRefresh 记录启动时间, 初始化上下文环境信息中的占位符, 检查必须的属性
  2. obtainFreshBeanFactory 重建内置的BeanFactory, 并加载bean定义
  3. prepareBeanFactory 初始化BeanFactory的标准上下文属性, 如BeanClassLoader, ExpressionResolver, PropertyEditorRegistrar, BeanPostProcessor, LoadTimeWeaverAwarePostProcessor等等.
  4. postProcessBeanFactory 标准初始化后修改上下文内置的BeanFactory
  5. invokeBeanFactoryPostProcessors 实例化并调用注册的BeanFactoryPostProcessor, 基于精确的顺序如果指定了顺序的话. 有些processor是操作Bean定义注册表的(如@Configuration标注的类bean包含其他的bean定义), 会在常规的BeanFactoryPostProcessor的检查发生之前. 在上下文对象的bean定义注册器进行了标准初始化之后进, 所有的常规bean定义都已经被加载了, 但是还没有bean被实例化. 在post-processiong之前可以添加更多的bean定义. @Configuration标注的类中的bean定义会在此时假如到注册器中.
  6. registerBeanPostProcessors 实例化并调用注册的BeanPostProcessor, 如果有顺序的话, 按照顺序来调用.
  7. initMessageSource 初始化名为messageSourceMessageSource实例.
  8. initApplicationEventMulticaster 初始化名为applicationEventMulticasterApplicationEventMulticaster实例, 应用可以用来注册应用事件的监听.
  9. onRefresh 供子类实现添加更多的更新操作.
  10. registerListeners 通过applicationEventMulticaster注册ApplicationListener实现类的监听器.
  11. finishBeanFactoryInitialization 进行上下文的BeanFactory初始化的收尾. 如提前初始化LoadTimeWeaverAware的bean, 冻结配置禁止修改bean定义, 实例化non-lazy-init的bean.
  12. finishRefresh 完成更新, 调用LifecycleProcessor.onRefresh(), 发布ContextRefreshedEvent事件, 将上下文实例暴露在MBean中.

ConfigurationClassPostProcessor

BeanFactoryPostProcessor的实现类, 用于引导@Configuration类. 默认情况下通过使用<context:annotation-config/>或者<context:component-scan/>注册.

注解

@SpringBootApplication

集合了@Configuration, @EnableAutoConfiguration@ComponentScan 属性: exclude, excludeName, scanBasePackage , scanBasePackageClass

@Configuration

类似旧版配置中的xml配置文件, 提供Bean的定义和引入其他xml配置. 分别通过@Bean@Import实现. 在ApplicationContext.refresh()时是用ConfigurationClassPostProcessor进行bean的实例化.

可以与@PropertySource, @Autowired, @Value, @Profile搭配使用.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
    @Value("${bean.name}") String beanName;
    @Autowired DataSource dataSource;
    @Bean
    public MyBean myBean() {
        return new MyBean(beanName);
    }
    @Configuration
    @Profile("test")
    static class DatabaseConfigTest {
        @Bean
        DataSource dataSource() {
            return new EmbeddedDatabaseBuilder().build();
        }
    }
    @Configuration
    @Profile("production")
    static class DatabaseConfigProduction {
        @Bean
        DataSource dataSource() {
            return new EmbeddedDatabaseBuilder().build();
        }
    }
}

@EnableAutoConfiguration

开启Spring上下文对象的自动配置功能, 尝试去猜测和实例化你可能需要的bean. 这个功能是基于classPath来完成的. 比如: 项目中引用了tomcat-embedded.jar, 你可能需要一个TomcatEmbeddedServletContainerFactory实例, 除非定义了自己的EmbeddedServletContainerFactory实例.

@ComponentScan

扫描使用@Configuration标注的类, 类似于Spring XML的<context:component-scan>元素. 使用basePackagesbasePackageClasses属性来指定要扫描的包, 如果没有指定, 则默认从使用了该注解的类的包开始扫描.

@Import

提示@Configuration有更多的类需要引入, 类似xml中的<import>标签. 可以引入@Configuration类, ImportSelector的实现类和ImportBeanDefinitionRegistrar的实现类, 还有常规的Component类.

三者的处理方式不一样: * @Configuration常规方式 * ImportSelector会根据泛型类型从spring.factories找到对应的配置类. * ImportBeanDefinitionRegistrar 可以实现在bean definition级别的处理 (@Bean实例级别)

引入@Configuration类中使用@Bean标注的实例, 可以通过@Autowired注入. Bean和声明Bean的Configuration类本身都可以通过@Autowired注入.

引入XML或者非Configuration, 使用@ImportResource.

Comments

comments powered by Disqus