1 自启动使用实例
1.1 配置 spring.factories
在 resources 下自定义 META-INF/spring.factories,里面填写需要自动化配置的类
org.springframework.boot.autoconfigure.EnableAutoConfiguration = com.wujun234.uid.UidAutoConfigure
1.2 实现自定义配置类
UidAutoConfigure
@Configuration
@MapperScan({ "com..." })
@EnableConfigurationProperties(UidProperties.class)
public class UidAutoConfigure {
@Autowired
private UidProperties uidProperties;
@Bean
DefaultUidGenerator defaultUidGenerator() {
return new DefaultUidGenerator(uidProperties);
}
...
}
2 源码解析
以 spring-boot 2.2.1 为例
2.1 SpringApplication 启动流程
一个简单 spring-boot-starter-web 项目的入口方法
@SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
可以在 Spring Boot 容器的启动阶段,扩展:
- 初始化器(Initializer)
- 监听器(Listener)
- 容器刷新后置 Runners(ApplicationRunner 或者 CommandLineRunner 接口的实现类)
- 启动期间在 Console 打印 Banner 的具体实现类
1)@SpringBootApplication
@SpringBootApplication 注解包含了 @SpringBootConfiguration,@EnableAutoConfiguration 以及 @ComponentScan
- @SpringBootConfiguration:相当于 @Configuration
- @EnableAutoConfiguration:自动配置的关键
- 包含了 @Import(AutoConfigurationImportSelector.class),导入了一个重要的类 AutoConfigurationImportSelector。
- @ComponentScan:默认扫描所在类的同级及下级目录所有类
2)SpringApplication.run()
实际上是首先创建了 SpringApplication 的实例,然后调用了 SpringApplication 的 run() 方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
SpringApplication 实例化
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
...
// 推断应用类型(在类路径中查找类),后面会根据类型初始化对应的环境。常用的一般都是servlet环境。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置初始化器。
// 初始化 classpath 下 META-INF/spring.factories 中已配置的 Key为 org.springframework.context.ApplicationContextInitializer 的 value
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器
// 初始化 classpath 下 META-INF/spring.factories 中已配置的 Key为 org.springframework.context.ApplicationListener 的 value
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
...
}
SpringApplication.run() 方法
- 获取并启动监听器
- 构造 ApplicationContext 环境
- 初始化 ApplicationContext
- ApplicationContext 的前置处理
- 刷新 ApplicationContext(IoC 容器的初始化)
- ApplicationContext 的后置处理
public ConfigurableApplicationContext run(String... args) {
// 记录程序运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 声明 ApplicationContext
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
// 设置 java.awt.headless 系统属性为 true - 没有图形化界面
this.configureHeadlessProperty();
// 第一步:获取并启动监听器
// 从 META-INF/spring.factories 中获取监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 发布事件——“容器开始启动”
listeners.starting();
Collection exceptionReporters;
try {
// 第二步:构造 ApplicationContext 环境
// 配置计算机环境,Java 环境,Spring 运行环境,配置Spring 项目 Profiles(SpringBoot 的 application.properties/yml)等等。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
// 处理需要忽略的 Bean
this.configureIgnoreBeanInfo(environment);
// 打印 banner
Banner printedBanner = this.printBanner(environment);
// 第三步:初始化 ApplicationContext
context = this.createApplicationContext();
// 准备异常报告器
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 第四步:ApplicationContext 的前置处理
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 第五步:刷新 ApplicationContext
// IoC 容器的初始化过程。
// 5.1 Resource 定位(包扫描)
// 5.2 BeanDefinition 的载入(加载 basePackage,判断 @Component 注解,存在即是 BeanDefinition)
// 5.3 注册 BeanDefinition(在 IoC 容器中将 BeanDefinition 注入到一个 ConcurrentHashMap 中)
this.refreshContext(context);
// 第六步:ApplicationContext 的后置处理
this.afterRefresh(context, applicationArguments);
// 时间记录停止
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 发布事件——“容器启动完成”
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
2.2 SpringBoot 自动装配原理实现
@EnableAutoConfiguration 注解中包含了 @Import(AutoConfigurationImportSelector.class),导入了一个重要的类 AutoConfigurationImportSelector。
// AutoConfigurationImportSelector 类 selectImports 方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
// 加载元数据。(从 META-INF/spring-autoconfigure-metadata.properties 加载)
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
// 获取所有的自动配置类。(从 META-INF/spring.factories 中获取配置的 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的类)
// 并去掉需要排除的自动装配类。(springboot 的主类上 @SpringBootApplication(exclude = {}) 指定的排除类)
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
2.3 IoC 容器依赖注入
触发依赖注入:
- AbstractApplicationContext 类 refresh() 方法
- -》 AbstractApplicationContext 类 finishBeanFactoryInitialization() 方法
- -》 DefaultListableBeanFactory 类 preInstantiateSingletons() 方法
- -》 AbstractBeanFactory 类 getBean() 方法
- -》 AbstractBeanFactory 类 doGetBean() 方法
1)获取 bean 名字
bean 获取过程:先获取 bean 名字。会把带有 & 前缀的去掉,或者去 aliasMap 中找这个是不是别名,最终确定 bean 的 id 是什么
2)检查 bean 实例
检查缓存中或者实例工厂中是否有对应的实例,Spring 默认是单例的,如果能获取到直接返回,提高效率。
因为在创建单例 bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring 在创建 bean 的时候不会等 bean 创建完成就会将 bean 的 ObjectFactory 提早曝光,也就是将 ObjectFactory 加入到缓存中。
一旦下一个要创建的 bean 需要依赖上个 bean 则直接使用 ObjectFactory。
3)检查 BeanDefinition
对 IoC 容器中的 BeanDefinition 是否存在进行检查,检查是否能在当前的 BeanFactory 中取得需要的 Bean。
如果当前的工厂中取不到,则到双亲 BeanFactory 中去取。如果当前的双亲工厂取不到,那就顺着双亲 BeanFactory 链一直向上查找。
4)创建 Bean
- 根据 Bean 的名字取得 BeanDefinition。
- 获取当前 Bean 的所有依赖 Bean,触发 getBean 的递归调用。直到取到一个没有任何依赖的 Bean 为止。
- 通过 createBean 方法创建 Bean 实例。
- 对创建的Bean进行类型检查,如果没有问题,就返回这个新创建的Bean。
- 这个 Bean 已经是包含了依赖关系的 Bean