diff --git a/docs/SpringBoot/Spring-Boot-Run.md b/docs/SpringBoot/Spring-Boot-Run.md new file mode 100644 index 0000000..7b11a06 --- /dev/null +++ b/docs/SpringBoot/Spring-Boot-Run.md @@ -0,0 +1,760 @@ +# SpringBoot 启动方法 +- Author: [HuiFer](https://github.com/huifer) +- 源码阅读仓库: [SourceHot-spring-boot](https://github.com/SourceHot/spring-boot-read) +## 入口 +- 通常一个简单的SpringBoot基础项目我们会有如下代码 +```java +@SpringBootApplication +@RestController +@RequestMapping("/") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} + +``` + +- 值得关注的有`SpringApplication.run`以及注解`@SpringBootApplication` +### run方法 +```java + public ConfigurableApplicationContext run(String... args) { + // 秒表 + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + ConfigurableApplicationContext context = null; + Collection exceptionReporters = new ArrayList<>(); + configureHeadlessProperty(); + // 获取监听器 + SpringApplicationRunListeners listeners = getRunListeners(args); + // 监听器启动 + listeners.starting(); + try { + // application 启动参数列表 + ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); + ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); + // 配置忽略的bean信息 + configureIgnoreBeanInfo(environment); + Banner printedBanner = printBanner(environment); + // 创建应用上下文 + context = createApplicationContext(); + exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, + new Class[] { ConfigurableApplicationContext.class }, context); + // 准备上下文,装配bean + prepareContext(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, exceptionReporters, listeners); + throw new IllegalStateException(ex); + } + + try { + // 监听器正式运行 + listeners.running(context); + } + catch (Throwable ex) { + handleRunFailure(context, ex, exceptionReporters, null); + throw new IllegalStateException(ex); + } + return context; + } + +``` + +### getRunListeners + +- 获取监听器 + + + +```java + private SpringApplicationRunListeners getRunListeners(String[] args) { + Class[] types = new Class[] { SpringApplication.class, String[].class }; + // 获取 Spring Factory 实例对象 + return new SpringApplicationRunListeners(logger, + getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); + } + + + private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) { + ClassLoader classLoader = getClassLoader(); + // Use names and ensure unique to protect against duplicates + // 读取 spring.factories + Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); + // 创建SpringFactory实例 + List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); + /** + * 排序 {@link Ordered} + */ + AnnotationAwareOrderComparator.sort(instances); + return instances; + } +``` + +### createSpringFactoriesInstances + + ```java + @SuppressWarnings("unchecked") + private List createSpringFactoriesInstances(Class type, Class[] parameterTypes, + ClassLoader classLoader, Object[] args, Set names) { + // 初始化 + List instances = new ArrayList<>(names.size()); + for (String name : names) { + try { + // 通过名字创建类的class对象 + 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; + } + + ``` + +- `SpringFactoriesLoader.loadFactoryNames(type, classLoader)` 是spring提供的方法,主要目的是读取`spring.factories`文件 + - 读取需要创建的内容 + +![image-20200318080601725](/images/SpringBoot/image-20200318080601725.png) + +- 创建完成 + + ![image-20200318080901881](/images/SpringBoot/image-20200318080901881.png) + + + +- `AnnotationAwareOrderComparator.sort(instances)`排序 + + - 通过spring的源码我们知道这个方法是根据`order`的数字大小进行排序,观察 + + `SharedMetadataReaderFactoryContextInitializer` + + ![image-20200318081112670](/images/SpringBoot/image-20200318081112670.png) + + - 同样的再找一个`DelegatingApplicationContextInitializer` + + ![image-20200318081322781](/images/SpringBoot/image-20200318081322781.png) + +- 下图中的所有类都有Order数值返回 + + 排序前: + + ![image-20200318081352639](/images/SpringBoot/image-20200318081352639.png) + + 排序后: + +![image-20200318081458019](/images/SpringBoot/image-20200318081458019.png) + + + + + +### listeners.starting() + +- `SpringApplicationRunListeners` : `org.springframework.boot.SpringApplicationRunListeners` 这个类是`org.springframework.boot.SpringApplicationRunListener`的集合表现形式 + + ``` + class SpringApplicationRunListeners { + + private final List listeners; + SpringApplicationRunListeners(Log log, Collection listeners) { + this.log = log; + this.listeners = new ArrayList<>(listeners); + } + + void starting() { + for (SpringApplicationRunListener listener : this.listeners) { + listener.starting(); + } + } + + } + ``` + + - 这里主要是启动`org.springframework.boot.SpringApplicationRunListener#starting`方法,只有一个实现`org.springframework.boot.context.event.EventPublishingRunListener#starting` + + + + + +### prepareEnvironment + +```java + private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, + ApplicationArguments applicationArguments) { + // Create and configure the environment + // 得到一个环境 + ConfigurableEnvironment environment = getOrCreateEnvironment(); + // 配置环境 + configureEnvironment(environment, applicationArguments.getSourceArgs()); + ConfigurationPropertySources.attach(environment); + listeners.environmentPrepared(environment); + // 绑定springBoot应用 + bindToSpringApplication(environment); + // 是否创建自定义环境 + if (!this.isCustomEnvironment) { + environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, + deduceEnvironmentClass()); + } + ConfigurationPropertySources.attach(environment); + return environment; + } + +``` + + + +### configureIgnoreBeanInfo + +- 获取`spring.beaninfo.ignore`并且设置到环境信息中 + +```java + private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) { + if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) { + Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE); + System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString()); + } + } +``` + + + + + +### printBanner + +```java + 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); + } + +``` + +``` + Banner print(Environment environment, Class sourceClass, PrintStream out) { + Banner banner = getBanner(environment); + banner.printBanner(environment, sourceClass, out); + return new PrintedBanner(banner, sourceClass); + } + +``` + +- 最终输出内容类:`org.springframework.boot.SpringBootBanner` + + ```JAVA + class SpringBootBanner implements Banner { + + private static final String[] BANNER = { "", " . ____ _ __ _ _", + " /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\", + " \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", " ' |____| .__|_| |_|_| |_\\__, | / / / /", + " =========|_|==============|___/=/_/_/_/" }; + + private static final String SPRING_BOOT = " :: Spring Boot :: "; + + private static final int STRAP_LINE_SIZE = 42; + + @Override + public void printBanner(Environment environment, Class sourceClass, PrintStream printStream) { + for (String line : BANNER) { + printStream.println(line); + } + String version = SpringBootVersion.getVersion(); + version = (version != null) ? " (v" + version + ")" : ""; + StringBuilder padding = new StringBuilder(); + while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) { + padding.append(" "); + } + + printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(), + AnsiStyle.FAINT, version)); + printStream.println(); + } + + } + ``` + +### createApplicationContext + +```JAVA + protected ConfigurableApplicationContext createApplicationContext() { + // 获取上下文类 + Class contextClass = this.applicationContextClass; + if (contextClass == null) { + try { + // 根据不同类型选择创建的实例 + switch (this.webApplicationType) { + case SERVLET: + contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); + break; + case REACTIVE: + contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); + break; + default: + contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); + } + } + catch (ClassNotFoundException ex) { + throw new IllegalStateException( + "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); + } + } + return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); + } + +``` + + + +- `this.applicationContextClass` 初始化方法 + +```JAVA + public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { + this.resourceLoader = resourceLoader; + Assert.notNull(primarySources, "PrimarySources must not be null"); + this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); + // 设置 web应用类型 + this.webApplicationType = WebApplicationType.deduceFromClasspath(); + setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); + setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); + this.mainApplicationClass = deduceMainApplicationClass(); + } +``` + +- `org.springframework.boot.WebApplicationType#deduceFromClasspath` + +```JAVA + static WebApplicationType deduceFromClasspath() { + if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) + && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { + return WebApplicationType.REACTIVE; + } + for (String className : SERVLET_INDICATOR_CLASSES) { + if (!ClassUtils.isPresent(className, null)) { + return WebApplicationType.NONE; + } + } + return WebApplicationType.SERVLET; + } + +``` + + + + + +### exceptionReporters + +![image-20200318085243888](/images/SpringBoot/image-20200318085243888.png) + + + + + +### prepareContext + +```JAVA + 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); + } + // Add boot specific singleton beans + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + // 单例对象注册 + beanFactory.registerSingleton("springApplicationArguments", applicationArguments); + if (printedBanner != null) { + beanFactory.registerSingleton("springBootBanner", printedBanner); + } + if (beanFactory instanceof DefaultListableBeanFactory) { + ((DefaultListableBeanFactory) beanFactory) + .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); + } + if (this.lazyInitialization) { + context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); + } + // Load the sources + Set sources = getAllSources(); + Assert.notEmpty(sources, "Sources must not be empty"); + // 加载上下文 + load(context, sources.toArray(new Object[0])); + // 监听器做加载上下文操作 + listeners.contextLoaded(context); + } + +``` + +- `set`方法就不说了 + + + +### postProcessApplicationContext + +```java + protected void postProcessApplicationContext(ConfigurableApplicationContext context) { + if (this.beanNameGenerator != null) { + // 注册 beanName 的生成器 + 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()); + } + } + if (this.addConversionService) { + // 转换服务 + context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()); + } + } + +``` + +- 看一下最终设置完成后的context + + ```java + context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance()); + ``` + + + +![image-20200318090128983](/images/SpringBoot/image-20200318090128983.png) + +![image-20200318090312626](/images/SpringBoot/image-20200318090312626.png) + + + + + + + +### applyInitializers + +- 初始化应用上下文 + +```java + @SuppressWarnings({ "rawtypes", "unchecked" }) + 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); + } + } + +``` + + + +- 初始化 `List> listeners`: `setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));` + + + +- 获取 `List> listeners`: `public Set> getListeners() { return asUnmodifiableOrderedSet(this.listeners);}` + + + +- 数据结果 + +![image-20200318090935285](/images/SpringBoot/image-20200318090935285.png) + +- 子类的具体实现不展开了 + + + + + + + +### getAllSources + +```java + public Set getAllSources() { + Set allSources = new LinkedHashSet<>(); + if (!CollectionUtils.isEmpty(this.primarySources)) { + allSources.addAll(this.primarySources); + } + if (!CollectionUtils.isEmpty(this.sources)) { + allSources.addAll(this.sources); + } + return Collections.unmodifiableSet(allSources); + } + +``` + + + +- `primarySources` 就是我们的项目启动类,在`SpringApplication`的构造器中有`this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))` + +![image-20200318091558233](/images/SpringBoot/image-20200318091558233.png) + + + + + + + +### load + +- 加载bean到应用上下文 + +```java + protected void load(ApplicationContext context, Object[] sources) { + if (logger.isDebugEnabled()) { + logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); + } + // bean定义加载器 + BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); + if (this.beanNameGenerator != null) { + // 设置 beanName生成器 + loader.setBeanNameGenerator(this.beanNameGenerator); + } + if (this.resourceLoader != null) { + // 设置 资源加载器 + loader.setResourceLoader(this.resourceLoader); + } + if (this.environment != null) { + // 设置环境 + loader.setEnvironment(this.environment); + } + // 加载 + loader.load(); + } + +``` + + + +```java + int load() { + int count = 0; + for (Object source : this.sources) { + count += load(source); + } + return count; + } + +``` + + + +```java +private int load(Object source) { + Assert.notNull(source, "Source must not be null"); + if (source instanceof Class) { + return load((Class) source); + } + if (source instanceof Resource) { + return load((Resource) source); + } + if (source instanceof Package) { + return load((Package) source); + } + if (source instanceof CharSequence) { + return load((CharSequence) source); + } + throw new IllegalArgumentException("Invalid source type " + source.getClass()); + } + +``` + +- 通过前文我们已经知道 `source`就是一个class + + ![image-20200318092027020](/images/SpringBoot/image-20200318092027020.png) + + + +```java + private int load(Class source) { + if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { + // Any GroovyLoaders added in beans{} DSL can contribute beans here + GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class); + load(loader); + } + // 是否为组件 + if (isComponent(source)) { + this.annotatedReader.register(source); + return 1; + } + return 0; + } + +``` + +- 我们的启动类是一个组件,直接注册完成返回1 + + + + + +### listeners.contextLoaded(context) + +- 监听器行为: 在上下文资源加载后做一些事情 + + + + + +### refreshContext + +- 上下文刷新 + + + +```java + private void refreshContext(ConfigurableApplicationContext context) { + refresh(context); + if (this.registerShutdownHook) { + try { + context.registerShutdownHook(); + } + catch (AccessControlException ex) { + // Not allowed in some environments. + } + } + } + +``` + +```java + /** + * Refresh the underlying {@link ApplicationContext}. + * @param applicationContext the application context to refresh + */ + protected void refresh(ApplicationContext applicationContext) { + Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); + ((AbstractApplicationContext) applicationContext).refresh(); + } +``` + + + +- 最终来到了`org.springframework.context.support.AbstractApplicationContext#refresh`方法,此方法是spring的一个方法,此处不在阐述 + + + + + +### afterRefresh + +- 刷新上下文之后做的事情,空的没有实现 + + ``` + protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { + } + + ``` + + + + + +### stopWatch.stop() + +- 秒表结束 + + + +### listeners.started(context) + +- 各类监听器启动 + + + + +### callRunners + +- 两种runner启动`ApplicationRunner` 和 `CommandLineRunner` + + + +```JAVA + private void callRunners(ApplicationContext context, ApplicationArguments args) { + List runners = new ArrayList<>(); + runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); + runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); + AnnotationAwareOrderComparator.sort(runners); + for (Object runner : new LinkedHashSet<>(runners)) { + if (runner instanceof ApplicationRunner) { + callRunner((ApplicationRunner) runner, args); + } + if (runner instanceof CommandLineRunner) { + callRunner((CommandLineRunner) runner, args); + } + } + } + +``` + + + + + +```JAVA +private void callRunner(ApplicationRunner runner, ApplicationArguments args) { + try { + (runner).run(args); + } + catch (Exception ex) { + throw new IllegalStateException("Failed to execute ApplicationRunner", ex); + } + } +``` + + + + + + + +### listeners.running(context) + +- 监听器正式开始工作 \ No newline at end of file diff --git a/images/SpringBoot/image-20200318080601725.png b/images/SpringBoot/image-20200318080601725.png new file mode 100644 index 0000000..1304335 Binary files /dev/null and b/images/SpringBoot/image-20200318080601725.png differ diff --git a/images/SpringBoot/image-20200318080901881.png b/images/SpringBoot/image-20200318080901881.png new file mode 100644 index 0000000..98f7cc9 Binary files /dev/null and b/images/SpringBoot/image-20200318080901881.png differ diff --git a/images/SpringBoot/image-20200318081112670.png b/images/SpringBoot/image-20200318081112670.png new file mode 100644 index 0000000..a4b5293 Binary files /dev/null and b/images/SpringBoot/image-20200318081112670.png differ diff --git a/images/SpringBoot/image-20200318081322781.png b/images/SpringBoot/image-20200318081322781.png new file mode 100644 index 0000000..a3d24f5 Binary files /dev/null and b/images/SpringBoot/image-20200318081322781.png differ diff --git a/images/SpringBoot/image-20200318081352639.png b/images/SpringBoot/image-20200318081352639.png new file mode 100644 index 0000000..ca38b0e Binary files /dev/null and b/images/SpringBoot/image-20200318081352639.png differ diff --git a/images/SpringBoot/image-20200318081458019.png b/images/SpringBoot/image-20200318081458019.png new file mode 100644 index 0000000..89ecaa7 Binary files /dev/null and b/images/SpringBoot/image-20200318081458019.png differ diff --git a/images/SpringBoot/image-20200318085243888.png b/images/SpringBoot/image-20200318085243888.png new file mode 100644 index 0000000..af4959b Binary files /dev/null and b/images/SpringBoot/image-20200318085243888.png differ diff --git a/images/SpringBoot/image-20200318090128983.png b/images/SpringBoot/image-20200318090128983.png new file mode 100644 index 0000000..71005b3 Binary files /dev/null and b/images/SpringBoot/image-20200318090128983.png differ diff --git a/images/SpringBoot/image-20200318090312626.png b/images/SpringBoot/image-20200318090312626.png new file mode 100644 index 0000000..bda10d8 Binary files /dev/null and b/images/SpringBoot/image-20200318090312626.png differ diff --git a/images/SpringBoot/image-20200318090935285.png b/images/SpringBoot/image-20200318090935285.png new file mode 100644 index 0000000..ba73b95 Binary files /dev/null and b/images/SpringBoot/image-20200318090935285.png differ diff --git a/images/SpringBoot/image-20200318091558233.png b/images/SpringBoot/image-20200318091558233.png new file mode 100644 index 0000000..6ede8f8 Binary files /dev/null and b/images/SpringBoot/image-20200318091558233.png differ diff --git a/images/SpringBoot/image-20200318092027020.png b/images/SpringBoot/image-20200318092027020.png new file mode 100644 index 0000000..6af43a8 Binary files /dev/null and b/images/SpringBoot/image-20200318092027020.png differ