天天看点

SpringBoot源码解析系列(1) 启动源码分析

序章 : 这是我拔了将近4个月SpringBoot源码 , 本人本二  刚开始自学的时候有幸接触到第一本书籍是java编程思想 故对java编程产生了浓厚的兴趣 所以想着人人为我 我为人人 故出此专题  虽然出此专题 但我想说的是还是建议大家多看看Spring相关源码 对于代码感觉 , 对于设计模式以及抽象事务理解即为提高 !! 希望大家能也去扒扒源码

1 . SpringBoot概述

Build Anything with Spring Boot:Spring Boot is the starting point for building all Spring-based applications. Spring Boot is designed to get you up and running as quickly as possible, with minimal upfront configuration of Spring.(引用官网)

  • 它使用 “习惯优于配置” (基本上根据用户习惯配置了很多默认配置 同时还支持修改)的理念让你的项目快速运行起来。
  • 它并不是什么新的框架,而是默认配置了很多框架的使用方式,就像 Maven 整合了所有的 jar 包一样,Spring Boot 整合了所有框架 (通过xxx-starter包关联起来)

Spring源码中常用的单词总结 : (本人英语不是很好 开始读spring相关源码时极其难受 后来我会针对一些高频英文单词做一下笔记 我先把这个英文单词贴出来 供大家参考  后来基本上达到的水平是给我一个类名  结合着接口 我大概能猜出来这个类是干什么用的 以及有哪些重要方法)

handler处理程序
adapter适配器
resolver解析器(解决器)
registy注册器
mapping映射
Definition定义
Expression表达式
Converter转换器
Support支持
Profile个人资料(配置文件什么的)
Scope作用域
Alias别名
Publisher发行
Configurable可配置的
Required需要
Instantiation实例化
refresh刷新
Boot开机
Multicaster广播
Standard标准 (Spring中基本上如果你不是默认的话 都是走的是这个)
validate验证
Arguments争论
bootstrap引导程序
PostProcessor 后置处理器
Generic通用
Metadata元数据
skip跳过
priority优先
internal内部
Evaluator评估
Parser解析器
candidate候选
excluded限制
ignore忽略
validate证实
Customizer定制器
wrapper包装
           

介于目前好多博客都有启动方式 也非常简单 我这里就不细说如何启动 我们直接进入正题 看看 SpringBoot是如何实现了那么多流弊功能

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

这是一个简单到不能再简单地入口类  主要是通过 @SpringBootApplication()这个注解实现了自动装载 这个自动装载实现过程我会单独开一个话题讲解

接下来 走到了创建了一个SpringApplication 构造器函数如下 : 

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	//资源装载
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//主加载类 就是本文的InsAcaConsoleMain
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//判断 到底是 WebApplicationType.SERVLET 还是什么 如果不进行设置的话 默认是Serverlet !!!! 词条后面关联创建不同容器以及环境 甚至跟ServerletContext都有关 很关键
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 从MATE.INF下拿到所有key为 ApplicationContextInitializer !!!!并赋值
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		// 从MATE.INF下拿到所有key为 ApplicationListener !!!!并赋值
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
           

SpringBoot能通过SpringFactoriesLoader.loadFactoryNames(type, classLoader)); type为接口类型 拿到所有依赖的jar包下/MATA-INF下的spring.factories里面的配置 里面是key - value 一般key为接口名称 这样就实现了动态加载一些类 底层调用的是classLoader.getResources jdk源码关于这部分写的挺有意思 感兴趣可以扒一下 在此模块我就不先讲jdk了

//启动监控表
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//模拟系统spring为没有键盘鼠标等状态
configureHeadlessProperty();
//返回SpringApplicationRunListener 从spring.factories拿到返回SpringApplicationRunListener 
//此类是跟SpringBoot事件有关 来会根据加载情况发布不同的事件 对于监听该事件的会进行跑监听事件 
//且SpringBoot只有一个实现类EventPublishingRunListener
//如果想魔改此类 新增事件发布 就也需要配置新的SpringApplicationRunListener 发布新的事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
	// 把传入的args进行解析 生成key - value 后面会将这个类注册成bean
	ApplicationArguments applicationArguments = new DefaultApplicationArguments(
			args);
    //详细说
	ConfigurableEnvironment environment = prepareEnvironment(listeners,
			applicationArguments);
	configureIgnoreBeanInfo(environment);
	//打印banner 就是那个SpringBoot图标 后面也会将这个对象注册成bean
	Banner printedBanner = printBanner(environment);
	// 会创建 AnnotationConfigApplicationContext容器
	context = createApplicationContext();
	//获得异常输出exceptionReporters (Spring.factories里面拿的 可以自定义)
	exceptionReporters = getSpringFactoriesInstances(
			SpringBootExceptionReporter.class,
			new Class[] { ConfigurableApplicationContext.class }, context);
	// 准备环境
	prepareContext(context, environment, listeners, applicationArguments,
			printedBanner);
	// 这个类很重要 涉及到Spring核心refresh功能 需要 单独开专题讲解
	refreshContext(context);
	// null方法 如果想改框架 想实现什么功能 覆盖掉这个方法
	afterRefresh(context, applicationArguments);
	stopWatch.stop();
	if (this.logStartupInfo) {
		new StartupInfoLogger(this.mainApplicationClass)
				.logStarted(getApplicationLog(), stopWatch);
	}
	//发布ApplicationStartedEvent事件
	listeners.started(context);
	// 从容器里面拿到ApplicationRunner跟CommandLineRunner实现类 跑对应的run方法
	callRunners(context, applicationArguments);
}
catch (Throwable ex) {
	handleRunFailure(context, ex, exceptionReporters, listeners);
	throw new IllegalStateException(ex);
}

try {
    //发布ApplicationReadyEvent事件
	listeners.running(context);
}
catch (Throwable ex) {
	handleRunFailure(context, ex, exceptionReporters, null);
	throw new IllegalStateException(ex);
}
return context;
           

上面代码逻辑就是SpringApplication#run方法的简单注释讲解        下面我会针对重要方法进行深度讲解

第一个重要方法 SpringApplication # prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		//创建StandardEnvironment环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//重要
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//发布ApplicationEnvironmentPreparedEvent事件
		listeners.environmentPrepared(environment);
		//将当前环境绑定到当前SpringApplication
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		//往环境里面添加key为configurationProperties的PropertySources
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
           

第二个重要方法  configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		if (this.addConversionService) {
		// 把Spring默认的Converter添加进环境 他是将一个类型转换成另外的类型 比如date转换成localdatetime
		// 同时也支持可扩展
			ConversionService conversionService = ApplicationConversionService
					.getSharedInstance();
			environment.setConversionService(
					(ConfigurableConversionService) conversionService);
		}
		// 添加比如java默认参数 或者环境变量啥的 
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}
           

其实到这里 SpringBoot基本启动类就已经结束了 但Spring的魅力远远不止如此   接下来我会对SpringBoot以及Spring中一些常见的实现进行详细讲解 !!!!!!!!!!!!!!!!!!!!!!!!!!

下一节SpringBoot源码解析系列(2) SpringBoot如何扫描并将对象注册Beandefinition : https://blog.csdn.net/weixin_44669461/article/details/115910478?spm=1001.2014.3001.5501

继续阅读