本文从SpringBoot启动类进入分析,看看SpringBoot项目启动时都进行了哪些操作
版本:SpringBoot 1.5.8
SpringBoot启动类 1 2 3 4 5 6 @SpringBootApplication public class MainApplication { public static void main (String[] args) { SpringApplication.run(MainApplication.class, args); } }
@SpringBootApplication注解 1 2 3 4 5 6 7 8 9 10 11 12 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { }
这是一个集成注解,集成了三个注解
@SpringBootConfiguration
该注解其实就是@Configuration,标注了该注解的类具备了以Java代码的形式,编写JavaConfig的能力。
@EnableAutoConfiguration
该注解让SpringBoot具备了自动化注入配置的能力,比较复杂,本文暂且略过。
@ComponentScan
该注解实现了自动扫描的能力,类似于XML配置中的<context:component-scan>
可以指定basePackages属性指定要扫描的包,以及扫描的条件。
默认扫描@ComponentScan注解所在类的同级类和同级目录下的所有类 ,所以对于一个Spring Boot项目,一般会把入口类放在顶层目录中,这样就能够保证源码目录下的所有类都能够被扫描到。
启动流程 1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) { SpringApplication.run(MainApplication.class, args); } public static ConfigurableApplicationContext run (Object[] sources, String[] args) { return new SpringApplication (sources).run(args); }
SpringApplicationn构造函数 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 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(); } private final Set<Object> sources = new LinkedHashSet <Object>();private boolean webEnvironment;private List<ApplicationContextInitializer<?>> initializers;private List<ApplicationListener<?>> listeners;
getSpringFactoriesInstance() Initialize方法中 ,Initializers和Listeners的加载过程都是使用到了SpringFactoriesLoader
工厂加载机制。我们进入到getSpringFactoriesInstances
这个方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private <T> Collection<? extends T > getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class <?>[] {}); } private <T> Collection<? extends T > getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Set<String> names = new LinkedHashSet <String>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
ApplicationContextInitializer,应用程序初始化器 Spring有多个initializer,通过在框架启动过程中,遍历调用initialize方法完成初始化工作:
1 2 3 public interface ApplicationContextInitializer <C extends ConfigurableApplicationContext > { void initialize (C applicationContext) ; }
ApplicationListener,应用程序事件(ApplicationEvent)监听器: 1 2 3 public interface ApplicationListener <E extends ApplicationEvent > extends EventListener { void onApplicationEvent (E event) ; }
应用程序事件(ApplicationEvent)有应用程序启动事件(ApplicationStartedEvent),失败事件(ApplicationFailedEvent),准备事件(ApplicationPreparedEvent)等。
ApplicationListener与ApplicationEvent相绑定 。
ConfigServerBootstrapApplicationListener只跟ApplicationEnvironmentPreparedEvent事件绑定,
LiquibaseServiceLocatorApplicationListener只跟ApplicationStartedEvent事件绑定,
LoggingApplicationListener跟所有事件绑定等。
initialize流程
将启动类加入this.source(LinkedHashSet)
通过反射判断是否是Web环境
从所有类中查找META-INF/spring.factories文件,加载其中的初始化类和监听类。通过Key查找
ApplicationContextInitializer.class
ApplicationListener.class
查找运行的主类 默认初始化Initializers都继承自ApplicationContextInitializer。
Run方法 前置知识: SpringApplicaitonRunListener 在分析run方法前,先来了解SpringApplication事件和监听器概念。
这里出现了两个类
SpringApplicationRunListener :一个interface定义了5个方法
void starting(); 在run方法启动时调用,对应事件的类型是ApplicationStartedEvent
void environmentPrepared(ConfigurableEnvironment environment); 在环境变量配置完成后,在ApplicationContext创建前,对应事件的类型是ApplicationEnvironmentPreparedEvent
void contextPrepared(ConfigurableApplicationContext context); ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件
void contextLoaded(ConfigurableApplicationContext context); ApplicationContext创建并加载之后并在refresh之前调用;对应事件的类型是ApplicationPreparedEvent
void finished(ConfigurableApplicationContext context, Throwable exception);run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent
SpringApplicationRunListener: 持有SpringApplicationRunListener集合和1个Log日志类。用于SpringApplicationRunListener监听器的批量执行
private final Log log;
private final List listeners;
EventPublishingRunListener: SpringApplicationRunListener唯一实现类
private final SimpleApplicationEventMulticaster initialMulticaster; 利用组合的方式,作为内部成员变量,用于将监听的过程封装成的SpringApplicationEvent事件,广播出去,广播出去的事件,最终会被ApplicationListener监听。
所以说SpringApplicationRunListener和ApplicationListener之间的关系是通过ApplicationEventMulticaster广播出去的SpringApplicationEvent所联系起来的。
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch (); stopWatch.start(); ConfigurableApplicationContext context = null ; FailureAnalyzers analyzers = null ; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments ( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); Banner printedBanner = printBanner(environment); context = createApplicationContext(); analyzers = new FailureAnalyzers (context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); 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() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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); }
prepareContext() 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 private void prepareContext (ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this .logStartupInfo) { logStartupInfo(context.getParent() == null ); logStartupProfileInfo(context); } context.getBeanFactory().registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { context.getBeanFactory().registerSingleton("springBootBanner" , printedBanner); } Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty" ); load(context, sources.toArray(new Object [sources.size()])); listeners.contextLoaded(context); }
postProcessApplicationContext() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 protected void postProcessApplicationContext (ConfigurableApplicationContext context) { if (this .beanNameGenerator != null ) { context.getBeanFactory().registerSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this .beanNameGenerator); } if (this .resourceLoader != null ) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context) .setResourceLoader(this .resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context) .setClassLoader(this .resourceLoader.getClassLoader()); } } }
applyInitializers() 1 2 3 4 5 6 7 8 9 protected void applyInitializers (ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer." ); initializer.initialize(context); } }
初始化器做的工作,举例如下
ContextIdApplicationContextInitializer会设置应用程序的id;
AutoConfigurationReportLoggingInitializer会给应用程序添加一个条件注解解析器报告等:
refreshContext() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void refreshContext (ConfigurableApplicationContext context) { refresh(context); if (this .registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { } } }
afterRefresh() 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 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); } } } 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); } }
总结 SpringBoot启动的时候,会构造一个SpringApplication的实例,然后调用这个实例的run方法 ,这样就表示启动SpringBoot。
SpringApplication初始化工作
把参数sources设置到SpringApplication属性中 ,这个sources可以是任何类型的参数。本文的例子中这个sources就是MainApplication的class对象
判断是否是web程序 ,并设置到webEnvironment这个boolean属性中
找出所有的初始化器 ,默认有5个,设置到initializers属性中
找出所有的应用程序监听器 ,默认有9个,设置到listeners属性中
找出运行的主类(main class)
调用SpringApplication run方法,启动SpringApplication ,run方法执行的时候会做以下几件事:
构造一个StopWatch,观察SpringApplication的执行
找出所有的SpringApplicationRunListener并封装到SpringApplicationRunListeners中,用于监听run方法的执行。监听的过程中会封装成事件并广播出去让初始化过程中产生的应用程序监听器进行监听
构造Spring容器(ApplicationContext)
创建Spring容器的判断是否是web环境,是的话构造AnnotationConfigEmbeddedWebApplicationContext,否则构造AnnotationConfigApplicationContext
初始化ApplicationContext容器
刷新Spring容器 (完成bean的解析、各种processor接口的执行、条件注解的解析等等
从Spring容器中找出ApplicationRunner和CommandLineRunner接口的实现类并排序后依次执行