蕩漾

爱欲之人犹如执炬逆行,必有灼手之患

0%

SpringBoot启动过程

SpringBoot启动结构图

springboot启动分为构造器部分和run部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//初始化资源加载器
this.resourceLoader = resourceLoader;
//断言资源加载器不为null,否则报错
Assert.notNull(primarySources, "PrimarySources must not be null");
//初始化资源
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//推断当前的web应用类型(非WEB / SERVLET / REACTIVE)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//初始化bootstrap启动器,将所有pom中的start加载进来
this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
//初始化spring.factories文件的所有组件信息,并放入缓存中
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//初始化应用监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断主程序类
this.mainApplicationClass = deduceMainApplicationClass();
}

运行SpringApplication

创建上下文—启动监听器—创建IOC容器—准备环境—刷新IOC容器

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
52
53
54
55
56
57
public ConfigurableApplicationContext run(String... args) {
//记录应用启动时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//创建引导上下文,获得所有的bootstraps逐个执行
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//进入自力更生模式(Headless模式)
configureHeadlessProperty();
//获取到所有的listener
SpringApplicationRunListeners listeners = getRunListeners(args);
//遍历所有的listener调用starting方法,通知当前项目正在启动
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//保存命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//配置忽略信息
configureIgnoreBeanInfo(environment);
//打印bannner
Banner printedBanner = printBanner(environment);
//创建IOC容器
context = createApplicationContext();
//设置start
context.setApplicationStartup(this.applicationStartup);
//准备环境信息
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新IOC容器,创建容器中的所有组件
refreshContext(context);
//刷新后需要完成的工作
afterRefresh(context, applicationArguments);
//记录停止时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//向观察者发布上下文已启动
listeners.started(context);
//调用所有runners
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;
}

springboot如何实现自动装配

归功于springboot强大的注解功能,也是springboot的一大特点

举例:在springboot主运行程序中,下面是常见的springboot启动类

1
2
3
4
5
6
@SpringBootApplication
public class SpringbootSrcApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootSrcApplication.class, args);
}
}

spring会在启动的时候new自己—>new SpringApplication(primarySources).run(args)这时候就会初始化,并加载扫描到所有的组件进入缓存

真正发挥作用的是@Import(),它导什么,容器实例化什么

先来从@SpringBootApplication这个注解说起

主要的注解为@SpringBootConfiguration+@ComponentScan+@EnableAutoConfiguration

前两个没什么好说的,@SpringBootConfiguration就是封装了@Configuration注解,表示他是一个配置类

@ComponentScan:扫描注解,表示哪些会被spring扫描到

那么重要的就是@EnableAutoConfiguration,见名知意拥有自动配置的能力,点进去看下

主要注解为@AutoConfigurationPackage+@Import,而@AutoConfigurationPackage其中也是@Import,所以起到主要作用的就是@Import这个注解了

@Import:给容器中导入一个组件,前提是必须作用于spring的组件下,可以自动调用组件的无参构造器创建对象

1
2
3
4
5
6
7
8
9
10
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}

由上图可知import导入了AutoConfigurationImportSelector.class这个组件,那么程序中是什么时候导入的?

spring容器启动会刷新IOC容器(refreshContext(context))执行invokeBeanFactoryPostProcessors方法,最终调用到getImports()这个方法,这个方法会将标有@Import的组件进行实例化

1
2
3
4
5
6
7
8
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
//核心process
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}

上述方法会执行AutoConfigurationImportSelector类的process方法

调用核心方法SpringFactoriesLoader.loadFactoryNames导入一系列组件

拓展部分,怎么导入的?
接上段程序,调用关键语句

1
2
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());

其中getSpringFactoriesLoaderFactoryClass()返回的正是@EnableAutoConfiguration这个注解

1
2
3
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}

点进去loadFactoryNames

1
2
3
4
5
6
7
8
9
10
11
12
13
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//根据上面代码获得EnableAutoConfiguration的完整类名(全类名,包括包名)
//org.springframework.boot.autoconfigure.EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
//loadSpringFactories(classLoaderToUse)就是刚开始初始化的18个结果集
//后面的get方法就是Map的方法,根据这个全类名可以获取到自动配置包下的所有组件(类)
//也就是上面截图中的131个组件
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

怎么实例化的
spring在刷新IOC的最后一步会调用finishBeanFactoryInitialization(beanFactory)方法,来实例化剩余的非lazy的单例,在调用过程中beanFactory.preInstantiateSingletons(),该方法会遍历所有的bean

按需配置

注解有如下几项:

@ConditionalOnBean:当容器里有指定的bean的条件下

@ConditionalOnMissingBean:当容器里不存在指定bean的条件下

@ConditionalOnClass:当类路径下有指定类的条件下

@ConditionalOnMissingClass:当类路径下不存在指定类的条件下

@ConditionalOnProperty:指定的属性是否有指定的值,比如

@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true

-EOF-