继续接着上篇文章讲SpringBoot的启动流程,这篇是真正开始分析启动的完整过程。
1 2 3 4 5 6 @SpringBootApplication public class WjApplication { public static void main (String[] args) { SpringApplication.run(WjApplication.class, args); } }
一、创建SpringApplication
对象 首先进入SpringApplication.run
方法,最终发现是执行如下代码,new
了一个SpringApplication
对象,并且执行它的run
方法。
1 2 3 public static ConfigurableApplicationContext run (Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); }
再看下SpringApplication
的构造函数,发现主要是对成员变量做了些初始化的操作,后面会进一步介绍它们的作用。
1 2 3 4 5 6 7 8 9 10 11 12 public SpringApplication (ResourceLoader resourceLoader, Class<?>... primarySources) { this .resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null" ); this .primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this .webApplicationType = WebApplicationType.deduceFromClasspath(); this .bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class)); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = deduceMainApplicationClass(); }
二、执行run方法 进入实例化SpringApplication
对象的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 public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null ; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this .mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); context.setApplicationStartup(this .applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this .logStartupInfo) { new StartupInfoLogger(this .mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null ); throw new IllegalStateException(ex); } return context; }
1.初始化上下文 1 2 3 4 5 DefaultBootstrapContext bootstrapContext = this .createBootstrapContext(); ConfigurableApplicationContext context = null ;
DefaultBootstrapContext
继承了BootstrapRegistry
和BootstrapContext
分别用于注册和提供注册过的需要在应用上下文加载完成前使用的类的实例。看下启动上下文bootstrapContext
的创建方法createBootstrapContext
源码。
注:实际DefaultBootstrapContext
是实现了ConfigurableBootstrapContext
,而ConfigurableBootstrapContext
继承了BootstrapRegistry
和BootstrapContext
1 2 3 4 5 6 7 8 9 10 private DefaultBootstrapContext createBootstrapContext () { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); this .bootstrappers.forEach((initializer) -> { initializer.intitialize(bootstrapContext); }); return bootstrapContext; }
我们看下最开始SpringApplication
的构造函数可以发现bootstrappers
是通过this.getSpringFactoriesInstances(Bootstrapper.class)
来获取的,我们看下源码。
1 2 3 4 5 6 7 8 9 10 11 private <T> Collection<T> getSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = this .getClassLoader(); Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = this .createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
(1)获取类加载器 判断resourceLoader
资源加载器是否为null,不是则用它的类加载器,否则是默认的加载器,从SpringApplication
的构造函数可以发现当前的资源加载器是null,所以是使用默认的类加载器。
1 2 3 public ClassLoader getClassLoader () { return this .resourceLoader != null ? this .resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader(); }
1 2 3 public SpringApplication (Class<?>... primarySources) { this ((ResourceLoader)null , primarySources); }
(2)加载全限定名 可以看下loadFactoryNames
的源码loadFactoryNames
调用了loadSpringFactories
方法,我们可以看到这个方法就是去获取META-INF/spring.factories
中的类名,前面也说过这个文件也是SpringBoot自动加载的核心。
1 2 3 4 5 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ;
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 public static List<String> loadFactoryNames (Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null ) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {... try { Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } ... }
(3)创建实例 看下createSpringFactoriesInstances
源码。很简单,就是通过反射的方式获取实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private <T> List<T> createSpringFactoriesInstances (Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); } } return instances; }
2.执行程序运行监听器 1 2 3 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this .mainApplicationClass);
首先看下getRunListeners
方法的代码SpringApplicationRunListeners
的作用就是让用户可以在SpringBoot启动时在各个阶段可以加入自己的代码逻辑。
1 2 3 4 5 6 7 8 private SpringApplicationRunListeners getRunListeners (String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this , args), this .applicationStartup); }
简单看下SpringApplicationRunListeners
的代码,可以看到主要有一下阶段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void starting (ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {}void environmentPrepared (ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {}void contextPrepared (ConfigurableApplicationContext context) {}void contextLoaded (ConfigurableApplicationContext context) {}void started (ConfigurableApplicationContext context) {}void running (ConfigurableApplicationContext context) {}void failed (ConfigurableApplicationContext context, Throwable exception) {}
再看下第二行代码,执行listeners
的start
方法,有一个mainApplicationClass
成员变量,在一开始的构造函数里有赋值操作,调用了deduceMainApplicationClass
方法,看下它的源码。所以它返回了main函数所在类的全限定名,在这个例子就是com.konb.wj.WjApplication
。然后start
方法上面也说了,当run()方法开始执行时就调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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) { } return null ; }
3.准备environment 准备environment
环境用了一行代码,主要操作都在prepareEnvironment
方法中,看下该方法的源码。
1 2 ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); configureAdditionalProfiles(environment); bindToSpringApplication(environment); if (!this .isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
(1)获取environment 首先看下getOrCreateEnvironment
的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private ConfigurableEnvironment getOrCreateEnvironment () { if (this .environment != null ) { return this .environment; } switch (this .webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default : return new StandardEnvironment(); } }
(2)配置environment 看下configureEnvironment
的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 protected void configureEnvironment (ConfigurableEnvironment environment, String[] args) { if (this .addConversionService) { ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService) conversionService); } configurePropertySources(environment, args); configureProfiles(environment, args); }
看下configurePropertySources
的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected void configurePropertySources (ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); DefaultPropertiesPropertySource.ifNotEmpty(this .defaultProperties, sources::addLast); if (this .addCommandLineProperties && args.length > 0 ) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource( new SimpleCommandLinePropertySource("springApplicationCommandLineArgs" , args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } } }