Spring Boot启动详解

从启动类开始

@SpringBootApplication
public class SpringdemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringdemoApplication.class, args);
    }
}

核心注解@SpringBootApplication

@Target(ElementType.TYPE)	// 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME)	 // 注解的生命周期,保留到class文件中(三个生命周期)
@Documented		// 表明这个注解应该被javadoc记录
@Inherited		// 表明子类可以继承该注解
@SpringBootConfiguration	// 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration	// 开启Springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = {	//扫描路径设置
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}

三个Annotation:

  • @Configuration(@SpringBootConfiguration点开查看发现里面还是应用了@Configuration)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
...
}
  • @EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
  • @ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
...
}

所以,如果我们使用如下的SpringBoot启动类,整个SpringBoot应用依然可以与之前的启动类功能对等

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class SpringdemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringdemoApplication.class, args);
    }
}

每次都写三个比较繁琐,所以写一个@SpringBootApplication方便

三个注解

@Configuration

  • 是JavaConfig形式的Spring Ioc容器的配置类使用 的那个@Configuration,SpringBoot社区推荐使用基于JavaConfig的配置形式,所以,这里的启动 类标注了@Configuration之后,本身其实也是一个IoC容器的配置类。
  • 举几个简单例子回顾下, XML跟config配置方式的区别:
    表达形式层面
  • 基于XML配置的方式
<?xml version="1.0"encoding="UTF‐8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring‐beans‐3.0.xsd" default‐lazy‐init="true">
    <!‐‐bean定义‐‐>
</beans>
  • 基于JavaConfig的配置方式是这样
@Configuration public class MockConfiguration{
	//bean 定义
}

任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类

  • 注册bean定义层面,基于XML的配置形式是这样
<bean id="mockService" class="..MockServiceImpl">
... 
</bean>
  • 基于JavaConfig的配置形式是这样
@Configuration 
public class MockConfiguration{ 
	@Bean 
	public MockService mockService(){ 
		return new MockServiceImpl(); 
	}
}

任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id

  • 表达依赖注入关系层面,为了表达bean与bean之间的依赖关系,在XML形式中一般如下
<bean id="mockService" class="..MockServiceImpl">
	<propery name ="dependencyService" ref="dependencyService" /> 
</bean>

<bean id="dependencyService" class="DependencyServiceImpl"></bean>
  • 基于JavaConfig的配置形式如下
@Configuration public class MockConfiguration{
	@Bean 
	public MockService mockService(){ 
		return new MockServiceImpl(dependencyService()); 
	}
	
	@Bean 
	public DependencyService dependencyService(){ 
		return new DependencyServiceImpl(); 
	}

}

如果一个bean的定义依赖其他bean,则直接调用对应的额JavaConfig类中依赖bean的创建方法就可以了

@ComponentScan

  • @ComponentScan这个注解在Spring中很重要,它对应XML配置中的元素
  • @ComponentScan 的功能其实就是自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者 bean定义,最终将这些bean定义加载到IoC容器中。
  • 我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
  • 注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages

@EnableAutoConfiguration

  • Spring框架提供的各种名字为@Enable开头的Annotation定义,比如@EnableScheduling、 @EnableCaching、@EnableMBeanExport等
  • @EnableScheduling是通过@Import将Spring调度框架相关的bean定义都加载到IoC容器。
    @EnableMBeanExport是通过@Import将JMX相关的bean定义加载到IoC容器。

而@EnableAutoConfiguration也是借助@Import的帮助,将所有符合自动配置条件的bean定义 加载到IoC容器,仅此而已!
@EnableAutoConfiguration作为一个复合Annotation,其自身定义关键信息如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}

在以上注解之中,包含两个比较重要的注解
@AutoConfigurationPackage:自动配置包
@Import:导入自动配置的组件

@AutoConfigurationPackage
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		register(registry, new PackageImport(metadata).getPackageName());
	}
}

其实就是注册了一个Bean定义
new PackageImport(metadata).getPackageName():返回当前主程序类的同级以及子级的包组件
这也就是为什么我们把XxxApplication放在项目的最高级中

@Import(AutoConfigurationImportSelector.class)

AutoConfigurationImportSelector 继承了 DeferredImportSelector 继承了 ImportSelector
ImportSelector有一个方法为:selectImports
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
该方法被AutoConfigurationImportSelector类重写
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
本类其实是去加载public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;外部文件,这个外部文件里面有很多自动配置的类,如下
在这里插入图片描述

注解总结

以上注解,最关键的要属 @Import(EnableAutoConfigurationImportSelector.class),借助 EnableAutoConfigurationImportSelector,@EnableAutoConfiguration可以帮助SpringBoot应 用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器。

自动配置的关键SpringFactoriesLoader

借助于Spring框架原有的一个工具类:SpringFactoriesLoader的支持,@EnableAutoConfiguration才可以实现自动配置功效

SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其

  • 主要功能就是从指定的配置文件 META-INF/spring.factories加载配置。
public abstract class SpringFactoriesLoader { //...

	public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) { 
		...
	}
	
	public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { 
		...
	} 
}

配合@EnableAutoConfiguration使用的话,它更多是提供一种配置查找的功能支持,即根据 @EnableAutoConfiguration的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration作为查找的Key,获取对应的 一组@Configuration类

所以,@EnableAutoConfiguration自动配置的过程就变成了:从classpath中搜寻所有的METAINF/spring.factories配置文件,并将其中 org.springframework.boot.autoconfigure.EnableautoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类, 然后汇总为一个并加载到IoC容器。

SpringBoot启动原理

SpringApplication的run方法的实现是启动原理探寻的起点,该方法的大致流程如下

1)SpringApplication实例初始化及设置

如果我们使用的是SpringApplication的静态run方法,那么,这个方法里面首先要创建一个 SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例方法。在 SpringApplication实例初始化的时候,它会提前做几件事情:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class<?>[] { primarySource }, args);
}

1.根据classpath里面是否存在某个特征类 (org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
2.使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
3.使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
4.推断并设置main方法的定义类。

在这里SpringBoot将应用程序分为三种类型:

  • Reactive:Spring团队推出的Reactor编程模型的非阻塞异步Web编程框架WebFlux
  • Servlet:基于J2EE Servlet API的编程模型,运行在Servlet容器上
  • None:非Web应用程序
    通过类路径中是否存在WebFlux中的Dispatcherhandler,SpringMVC中的DispatcherServlet、 Servlet、ConfigurableWebApplicationContext来推断Web应用程序类型
    在这里插入图片描述

2)执行run方法的逻辑

SpringApplication实例初始化完成并且完成设置后,就开始执行run方法的逻辑了,方法执行开始,首先遍历执行所有通过SpringFactoriesLoader可以查找到并加载的 SpringApplicationRunListener。调用它们的started()方法,告诉这些 SpringApplicationRunListener,“SpringBoot应用要开始执行咯!”。
在这里插入图片描述

3)创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource 以及Profile)。

  • Environment是SpringFramework中一个很重要的接口,用于存放应用程序的配置信息
  • PropertySource(org.springframework.core.env.PropertySource)是用来将一个对象以键值 对的形式表示,Spring将多种来源的属性键值对转换成PropertySource来表示

这里的做了如下事情:
1.创建Environment对象

  • 在getOrCreateEnvironment方法中,会根据之前推断的webApplicationType(web程序类型)创建不同了实现的Environment对象

2.配置Environment对象

  • 应用程序如果有命令行参数,则在Environment中添加一个与这个命令行参数相关的 PropertySource
  • 根据命令行参数中spring.profiles.active属性配置Environment对象中的activeProfile

3.发布ApplicationEnvironmentPreparedEvent(应用环境已准备)事件
4.将Environment中的spring.main属性绑定到SpringAppilcation对象中

  • 在执行到这一步时,Environment中已经包含了用户设置的profile文件属性

5.转换Environment对象的类型

  • 在上一步中,如果用户使用spring.main.web-application-type属性手动设置了应用程序的 webApplicationType并且用户设置的类型与SpringApplication推断出来的不一致,则 SpringApplication会将环境对象转换成用户设置的webApplicationType相关的类型
    在这里插入图片描述

4) 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法

遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。
在这里插入图片描述
在这里插入图片描述

5) 如果SpringApplication的showBanner属性被设置为true,则打印banner

在这里插入图片描述

6) 创建ApplicationContext

根据用户是否明确设置了applicationContextClass类型以及初始化阶段的推断结果,决定该为当前SpringBoot应用创建什么类型的ApplicationContext(通过 webApplicationType类型判断),最后通过BeanUtils实例化上下文对象,并返回。
在这里插入图片描述

7)SpringApplication在prepareContext方法中对上下文对象进行预配置

主要做了如下事情:
1.执行所有ApplicationContextInitializer的initialize方法

  • 这些ApplicationContextInitializer是在SpringApplication中的构造函数中加载的(通过读取spring.factories加载)
    2.发布ApplicationContextInitializedEvent(上下文已初始化)事件发布3.ApplicationPreparedEvent(上下文已准备)事件
    在这里插入图片描述
    在这里插入图片描述

8)刷新应用上下文对象

在这里插入图片描述
这里是ApplicationContext真正开始初始化容器和创建bean的阶段,其中bean的整个生命周期可以从这一步骤看出来;Spring Framework中的所有ApplicationContext实现都直接或间接继承自 AbstracttApplicationContext,它的refresh方法描述了整个上下文的初始化逻辑
在这里插入图片描述
举例:
以AnnotationConfigServletWebServerApplicationContext(当应用的webApplicationType 为Servlet时使用)这个实现类为例
AbstractApplicationContext的refresh方法

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 1.Prepare this context for refreshing.
		prepareRefresh();
		// 2.Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// 3.Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);
		try {
			// 4.Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);
			// 5.Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);
			// 6.Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);
			// 7.Initialize message source for this context.
			initMessageSource();
			// 8.Initialize event multicaster for this context.
			initApplicationEventMulticaster();
			// 9.Initialize other special beans in specific context subclasses.
			onRefresh();
			// 10.Check for listener beans and register them.
			registerListeners();
			// 11.Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);
			// 12.Last step: publish corresponding event.
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}
			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();
			// Reset 'active' flag.
			cancelRefresh(ex);
			// Propagate exception to caller.
			throw ex;
		}
		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

详解如下:
1.准备更新上下文时的预备工作:

  • 初始化PropertySource
  • 验证Enrivonment中必要的属性

2.获取上下文的内部BeanFactory

  • 内部BeanFactory的实现类是DefaultListableBeanFactory

3.对BeanFactory做些预备工作:

  • 设置BeanFactory的Bean类加载器、Bean表达式解析器、属性编辑器注册表
  • 添加类型为ApplicationContextAwareProcessor、ApplicationListenerDetector的 BeanPostProcessor
  • 让BeanFactory在自动装配时忽略一些接口类型
  • 注册可解析的依赖(自动装配时碰到这些类型直接注入,包括BeanFactory、 ResourceLoader、ApplicationEventPublisher、ApplicationContext)
  • 在BeanFactory中注册一些单例对象,包括environment、systemProperties、 systemEnvironment

4.对BeanFactory进行预处理

  • 添加一个WebApplicationContextServletContextAwareProcessor的BeanPostProcessor
  • 使BeanFactory自动装配时忽略ServletContextAware接口
  • 在BeanFactory中注册request、session两种scope
  • 注册可解析的依赖(自动装配时碰到这些类型可以注解注入,包括ServletRequest、 ServletResponse、HttpSession、WebRequest)

5.执行容器中的BeanFactoryPostProcessor执行到这时容器已经注册了三个 BeanFactoryPostProcessor,分别为

SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor ApplicationContexttInitializer初始化时注册 ConfigurationWarningsApplicationContextInitializer#ConfigurationWarningsPostProcessor ApplicationContexttInitializer初始化时注册 ConfigFileApplicationListener$PropertySourceOrderingPostProcessor ApplicationPreparedEvent事件发布时由ConfigFileApplicationListener注册

  • 在BeanFactory中找到已注册的BeanFactoryPostProcessor,执行其中类型为 BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
  • 循环从BeanFactory中获取BeanDefinitionRegistryPostProcessor(从BeanDefinition注册表 中获取,和上一步的来源不一样);有一个 ConfigurationClassPostProcessor(ApplicationContext的构造函数中注册的) ConfigurationClassPostProcessor会执行SpringBoot的自动装配功能,将spring.factories中 类型为EnableAutoConfiguration的类读取成BeanDefinition并过滤掉不满足条件的,然后注册 到BeanFactory中
  • 执行他们的postProcessBeanFactory方法对BeanFactory进行后处理
    6.注册BeanPostProcessor,Bean生命周期的钩子,允许用户对实例化后的Bean进行操作
  • 从BeanFactory中获取所有BeanPostProcessor
  • 在BeanFactory中注册一个类型为BeanPostProcessorChecker的BeanPostProcessor
  • 将所有BeanPostProcessor按照实现了PriorityOrdered、Ordered、没有实现排序接口的 顺序注册所有BeanPostProcessor到BeanFactory
  • 在BeanFactory中注册一个类型为ApplicationListenerDetector的BeanPostProcessor

7.初始化MessageSource(国际化相关)

8.初始化容器事件广播器(用来发布事件)

  • 构造了一个SimpleApplicationEventMulticaster当成默认的事件广播器

9.初始化一些特殊的Bean

  • 初始化ThemeSource(跟国际化相关的接口)
  • 创建WebServer

10.将所有监听器注册到前两步创建的事件广播器中
11.结束BeanFactory的初始化工作(这一步主要用来将所有的单例BeanDefinition实例化)
12.afterRefresh(上下文刷新完毕) 初始LifecycleProcessor(生命周期处理器),向BeanFactory注册一个 DefaultLifecycleProcessor 调用LifecycleProcessor的onrefresh方法(找到所有已注册的SmartLifecycle,根据 isRunning和isAutoStartup的条件判断,执行SmartLifecycle的start方法) 在ServletWebServerApplicationContetxt中,启动了WebServer并发布了 ServletWebServerInitializedEvent事件

9) 发布应用程序已启动(ApplicationStartedEvent)事件

10) 在BeanFactory中获取所有ApplicationRunner和CommandLineRunner并调用他们的run方法

在这里插入图片描述

11) 异常处理,如果run方法的处理过程中发生异常,则对exitCode进行相应处理

在这里插入图片描述

  • shutdownHook简单介绍
    – 作用:JVM退出时执行的业务逻辑
    – 添加:Runtime.getRuntime().addShutdownHook()
    – 移除:Runtime.getRuntime().removeShutdownHook(this.shutdownHook)
  • 什么时候会调用Shutdown Hook
    – 程序正常停止 :Reach the end of program、System.exit
    – 程序异常退出:NPE、OutOfMemory
    – 受到外界影响停止:Ctrl+C、用户注销或者关机

SpringBoot流程图

  • AutoConfigurationImportSelector借助spring框架原有的一个工具类SpringFactoriesLoader的支持, @EnableAutoConfiguration可以智能地完成自动配置。
    在这里插入图片描述
  • SpringApplication执行流程
    在这里插入图片描述
    在这里插入图片描述
  • Spring Boot自动化配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • Spring Boot启动流程
    在这里插入图片描述
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值