springboot源码分析(一)

SpringApplication类通过run方法开始进行分析

configureHeadlessProperty();
java.awt.headless默认为true,用来配置不存在显示器等系统配置的时候进行使用的配置,类似于服务器。

继续往下看看到了对应的classLoader相关的代码

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
}

说到classLoader,顺便讲一下classLoader
1.负责将class加载到JVM中
2.ClassLoader 通过双亲委任的方式进行加载类
解释一下双亲加载机制

一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象。这种机制就叫做双亲委托。
custom <-> AppclassLoader <-> extenstionClassLoader <-> bootstrap classLoader(c++ c)
为什么采用双亲委任机制:
因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。从安全角度考虑,如果不使用这种委托模式,我们自己定义一个Map来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为Map在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的Map,除非你改变JDK中ClassLoader搜索类的默认算法。

Bootstrap ClassLoader: j a v a P a t h / l i b e x t e n s i o n C l a s s L o a d e r : javaPath/lib extension ClassLoader : javaPath/libextensionClassLoader:javaPath/lib/ext
appClass loader:classpath 所有的类

3.显示加载和隐式加载。
显示加载是通过this.getClass | Class.forName | this.getClass.getClassLoader
隐式加载主要是以下几种方式
1)创建类的实例
2)访问某个类/接口的静态变量,或者赋值
3)调用类的静态方法
4)初始化类的子类
5)java虚拟机启动标注启动类的类,比如启动指定 main函数的类

4.源码分析开始

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

在使用springboot的时候,我们是使用 SpringApplication.run(Application.class,args); 开始启动服务,在实例化SpringApplication的时候开始注入相关数据,其中有一项是 this.webApplicationType,打开之后会发现如下代码

    /**
	 * The application should not run as a web application and should not start an
	 * embedded web server.
	 */
	NONE,

	/**
	 * The application should run as a servlet-based web application and should start an
	 * embedded servlet web server.
	 */
	SERVLET,

	/**
	 * The application should run as a reactive web application and should start an
	 * embedded reactive web server.deduceWebEnvironment
	 */
	REACTIVE;

除了我们已知的传统的SERVLET的WEB容器,还采用的新型的REACTIVE响应式编程,就是springboot2.x加入了webFlux,webFlux是一个异步非阻塞式的 Web 框架,它能够充分利用多核 CPU 的硬件资源去处理大量的并发请求。
WebFlux 内部使用的是响应式编程(Reactive Programming),以 Reactor 库为基础, 基于异步和事件驱动,可以让我们在不扩充硬件资源的前提下,提升系统的吞吐量和伸缩性。

我们开始分析springboot启动过程中所有的操作
1.所有监听开始运行

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

看到以上代码就应该清除,springboot所有监听器会在这里进行启动,关于springboot源码监听器有如下几种

nameintroduction
ClearCachesApplicationListener应用上下文加载完成后对缓存做清除工作,响应事件ContextRefreshedEvent
ParentContextCloserApplicationListener监听双亲应用上下文的关闭事件并往自己的孩子应用上下文中传播,相关事件ParentContextAvailableEvent/ContextClosedEvent
ClearCachesApplicationListener应用上下文加载完成后对缓存做清除工作,响应事件ContextRefreshedEvent
FileEncodingApplicationListener如果系统文件编码和环境变量中指定的不同则终止应用启动。具体的方法是比较系统属性file.encoding和环境变量spring.mandatory-file-encoding是否相等(大小写不敏感)
AnsiOutputApplicationListener根据spring.output.ansi.enabled参数配置AnsiOutput
ConfigFileApplicationListenerEnvironmentPostProcessor,从常见的那些约定的位置读取配置文件,比如从以下目录读取application.properties,application.yml等配置文件:classpath: file:.classpath:config file:./config/:也可以配置成从其他指定的位置读取配置文件
DelegatingApplicationListener监听到事件后转发给环境变量context.listener.classes指定的那些事件监听器
ClasspathLoggingApplicationListener一个SmartApplicationListener,对环境就绪事件ApplicationEnvironmentPreparedEvent/应用失败事件ApplicationFailedEvent做出响应,往日志DEBUG级别输出TCCL(thread context class loader)的classpath。
LoggingApplicationListener配置LoggingSystem。使用logging.config环境变量指定的配置或者缺省配置
LiquibaseServiceLocatorApplicationListener使用一个可以和Spring Boot可执行jar包配合工作的版本替换liquibase ServiceLocator
BackgroundPreinitializer尽早触发一些耗时的初始化任务,使用一个后台线程

以上是springboot定义的一些listener。每个listener作用各不相同对应的event对应不同生命周期。

关于lisntener,可以看到一个类 EventPublishingRunListener,相关代码如下

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}

	@Override
	public void started(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
	}

	@Override
	public void running(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
	}

	@Override
	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
		if (context != null && context.isActive()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			this.initialMulticaster.multicastEvent(event);
		}
	}

	private static class LoggingErrorHandler implements ErrorHandler {

		private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);

		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}

对应类 EventPublishingRunListener 中的方法,有不同的执行周期,周期如下表

nameintroduction
ApplicationStartingEvent在Environment和ApplicationContext可用之前 & 在ApplicationListener注册之后发布。
ApplicationEnvironmentPreparedEvent在context中 Environment被使用的时候并在context被创建之前
ApplicationContextInitializedEvent在bean定义加载之前 & ApplicationContextInitializers被调用之后 & ApplicationContext开始准备之后发布
ApplicationPreparedEvent在ApplicationContext完全准备好并且没有刷新之前发布,此时bean定义即将加载,Environment已经准备好被使用。
ApplicationStartedEvent在ApplicationContext刷新之后,调用ApplicationRunner和CommandLineRunner之前发布
ApplicationReadyEvent应用已经准备好,可以接收请求
ApplicationFailedEvent在启动过程中如果发生异常则触发。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Spring Boot源码分析,我可以提供一些基本的指导。 Spring Boot是一个用于简化Spring应用程序的框架。它提供了一种基于约定的配置方式,使得开发人员能够快速启动和部署应用程序。如果你想要深入了解Spring Boot源码,可以按照以下步骤进行: 1. 下载源码:你可以从Spring官方网站或者GitHub上获取Spring Boot源码。确保下载与你使用的版本相对应的源码。 2. 导入项目:使用你喜欢的集成开发环境(IDE)导入源码项目。Spring Boot使用Maven构建工具,所以确保你已经安装了Maven。 3. 了解核心模块:Spring Boot源码结构由许多模块组成。开始时,可以关注核心模块,如spring-bootspring-boot-autoconfigure和spring-boot-starter等。这些模块包含了Spring Boot框架的核心功能。 4. 阅读文档:Spring Boot为每个模块提供了详细的文档,包括类、方法和配置选项的说明。阅读文档将帮助你理解源码中的各个部分。 5. 调试和跟踪代码:使用IDE的调试功能,通过设置断点和跟踪代码的执行路径,来深入了解Spring Boot的工作原理。 6. 参考示例代码:Spring Boot源码中包含了许多示例代码,用于演示各种功能和用法。阅读这些示例代码可以帮助你更好地理解源码的实现方式。 请注意,Spring Boot源码非常庞大且复杂,需要一定的时间和经验来深入研究。建议先从核心模块开始,并结合阅读文档和调试代码的方式来进行学习。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值