SpringBoot 核心源码解读

2022年10月20日08:17:31

Spring boot基本使用及 stater机制原理_踩踩踩从踩的博客-CSDN博客

前言

前面文章对于springboot得基本使用以及stater机制 以及autoconfig 做了一个解读,如何手写一个starter的包,对于 自动装配 解读,有了个大概的思维;这篇文章继续讲解SpringBoot的核心源码 ,然后深入的解析整个Springboot怎么快速的构建项目 并管理我们的依赖jar包等等。

源码解读

Application启动类

@SpringBootApplication
public class DeviceManagerApplication extends SpringBootServletInitializer {

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

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
		return builder.sources(DeviceManagerApplication.class);
	}

}
就注解  以及  run方法
认识@SpringBootApplication注解
//没有指定包名,默认是加这个注解的类所在包

@SpringBootApplication @Configuration @EnableAutoConfiguration @ComponentScan三个注 解的复合
所有的 spring.factoies  所有的EnableAutoConfiguration  自动配置类。
组成了原始的基本信息  基本注解。

入口方法SpringApplication.run

对于springboot 只需要运行run方法就可以将我们的程序给运行起来。

/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified source using default settings.
	 * @param primarySource the primary source to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}
它的返回值:ConfigurableApplicationContext
这个ConfigurableApplicationContext属于总的一个   总的接口,也就是说可以创建的类可以是任意一个applicationcontext.
对于SpringApplication 方法上的注释

该类,该类可用于从Java main引导和启动Spring应用程序方法。默认情况下,类将执行以下步骤来引导申请:

下面的步骤  核心逻辑 就是  创建 applicationcontext  实例、

并且 注册 CommandLinePropertySource

激活 CommandLineRunner  这个接口  用于指示当bean包含在  一个{@link SpringApplication}。可以定义多个{@link CommandLineRunner}bean  在同一个应用程序上下文中,可以使用{@link ordered} 接口或{@link Order@Order}注释。  也就是说可以  查看当前springapplication创建了那些bean.

SpringApplication构造方法解读
通过  构造的方式 springApplication 可以指定初始化器  以及 监听器等等。 都是可以使用的。
setInitializers
primarySources
ApplicationListener
等等 都是可以构造的。
/**一:注释上的重要信息: 1、primary sources:application context从primarySource加载beans 
	2、创建的SpringApplication实例在调用它的run(String...)方法前,可对其进行定制化设置 可以进行哪些设置?看类中提供的public方法。
	* Create a new {@link SpringApplication} instance. The application context will load * beans from the specified primary sources (see {@link SpringApplication class- level} 
	* documentation for details. The instance can be customized before calling * {@link #run(String...)}. 
	* @param resourceLoader the resource loader to use 
	* @param primarySources the primary bean sources 
	* @see #run(Class, String[]) 
	* @see #setSources(Set) 
	*/@SuppressWarnings({ "unchecked", "rawtypes" }) 
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 
		//二、构造方法中的逻辑: 
		this.resourceLoader = resourceLoader; 
		// 1、一定要指定primarySources 
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 
		// 2、deduce(推断)web类型(servlet、reactive、NoWeb) 
		this.webApplicationType = WebApplicationType.deduceFromClasspath(); 
		// 3、从META-INF/spring.factories中获取
		ApplicationContextInitializer setInitializers((Collection) 
		getSpringFactoriesInstances( ApplicationContextInitializer.class)); 
		// 4、从META-INF/spring.factories中获取
		ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 
		// 5、推断执行的main方法的定义类 this.mainApplicationClass = deduceMainApplicationClass(); 
	}

webApplicationType  探测分配 是什么类型的 项目 ,其实是很简单的。 主要是判断 是否存在  父类

创建获取实例, 将监听器都放进去就可以了。

对于ApplicationContextInitializer

在初始化的时候会去调用

 以及ApplicationListener  是作为监听器存在的。

SpringApplication.run()实例方法解读

相当于ApplicationContext.refresh一样重要
/** 一:注释上的重要信息: 运行Spring application,创建并刷新一个 ApplicationContext 
  * Run the Spring application, creating and refreshing a new 
  * {@link ApplicationContext}. 
  * @param args the application arguments (usually passed from a Java main method) 
  * @return a running {@link ApplicationContext} 
  */
  public ConfigurableApplicationContext run(String... args) { 
	// 二、方法中的逻辑:
   	// 1 StopWatch开启计时 
	StopWatch stopWatch = new StopWatch(); 
	stopWatch.start(); 
	ConfigurableApplicationContext context = null; 
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<> (); 
	// 这是设置系统属性java.awt.headless,请百度了解 java.awt.headless的用途。
	configureHeadlessProperty(); 
	// 2、获取到 META-INF/spring.factories中配置的SpringApplicationRunListener 
	// 疑问:又出一个Listener,这个SpringApplicationRunListener是监听什么的?
	// 通过它的接口定义、注释了解它的用途 
	SpringApplicationRunListeners listeners = getRunListeners(args); 
	// 这里就调用它的starting() 
	listeners.starting();
	try {
		// 命令行参数包装为了ApplicationArguments 
		ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); 
		// 3、准备好了Environment,此刻Environment中都有哪些配置参数了?
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); 
		configureIgnoreBeanInfo(environment); 
		// 4、打印springboot LOGo图标 
		Banner printedBanner = printBanner(environment); 
		// 5、创建ApplicationContext context = createApplicationContext();
		// 6、获取到 META-INF/spring.factories中配置的SpringBootExceptionReporter 
		exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { 
		     ConfigurableApplicationContext.class }, context); 
	     // 7、准备ApplicationContext 
		 prepareContext(context, environment, listeners, applicationArguments, printedBanner); 
		 // 8、刷新ApplicationContext 
		 refreshContext(context); 
		 afterRefresh(context, applicationArguments); 
		 stopWatch.stop(); 
		 if (this.logStartupInfo) { 
		     new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); 
		}// 
		9、发布started事件 listeners.started(context);
		// 10、执行所有的Runners 
		callRunners(context, applicationArguments); 
	}catch (Throwable ex) { 
	   handleRunFailure(context, ex, exceptionReporters, listeners); 
	   throw new IllegalStateException(ex); 
   }
    try {
	// 11、发布running中事件 
	listeners.running(context); 
     }catch (Throwable ex) { 
	   handleRunFailure(context, ex, exceptionReporters, null); 
	   throw new IllegalStateException(ex); 
	 }
	// 12、返回ok的ConfigurableApplicationContext return context; 
	}

configureHeadlessProperty 方法 该方法只做了一件事:设置了一个名为java.awt.headless的系统属性

	private void configureHeadlessProperty() {
		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
				System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
	}

SpringApplicationRunListeners  返回的所有 SpringApplicationRunListener  监听器  ,  这里面就是 监听 它失败 等等状态。

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
				this.applicationStartup);
	}

 准备参数 DefaultApplicationArguments  本身需要处理

其中比较重要的prepareEnvironment   Environment中都有哪些配置参数

根据应用类型,创建对应的Environment对象,会装载环境变量、系统参数、具体应用类型配置参
配置环境:加入命令行参数PropertySource,配置启用的profiles

 触发RunListener环境准备完成回调

将environment绑定到SpringApplication

根据类型加载 不同的参数。

 ConfigurationPropertySources

包括各种 的配置启用的profiles    也可以指定 profiles   所以可以取出来做处理。

 环境上准备 就有的这个。 参数配置

ConfigFileApplicationListener   这里是所有的配置   包括配置的名称 和 路径。  名字 以及加载
主要在 starter包 中

这里面就是  加载的配置参数。

实现的ApplicationListener方法

 会将事件给发布出来。

 完整的加载过程。

 createApplicationContext

 

如果没有编程式指定contextClass,则根据之前推断的webApplicationType来选择对
应默认的 ApplicationContext实现类

在新版本中是添加

默认的三个常量类名 是采用的 工厂方法去创建的,但是道理是一样的。

准备过程

prepareContext
1、设置环境对象
2、设置ApplicationContext的beanNameGenerator、resourceLoader、
3、应用初始化器对ApplicationContext进行初始化处理(Initializers在构造SpringApplication时就从spring.factories中加载到了)
4、发布ApplicationContext准备妥当事件
5、打印startup日志信息
6 、添加例 bean 到 beanFactory中
对应的资源进行加载创建 解读resource   懒加载load

 refreshcontext   刷新

这里最终调用 的applicationcontext. refresh方法。

auto configurationbean定义加载

这个加载是整个 创建基础。

1. META-INF/spring.factories中指定的auto configuration Bean定义在哪里完成的加载?
而对于META-INF/spring.factories中加载其他的:
// 3、从META-INF/spring.factories中获取ApplicationContextInitializer 
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));

这个方法 是可以加载 spring.factories

但是对于 EnableAutoconfiguration  的加载 是在那里去加载的,暂时不知道。

那就从这个getSpringFactoriesInstances...)方法调用中找到一个合适的点,
看到,它是调用的SpringFactoriesLoader.loadFactoryNames(type, classLoader));  去加载 的 auto configuration 的  spring.factories 中
从调用栈看到是在进行ConfigurationClassPostProcessor处理阶段
其实最后就是AutoConfigurationImportSelector干的这个事
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}
AutoConfigurationImportSelector实现了 DeferredImportSelector
DeferredImportSelector是延迟导入选择器。所谓延迟:在所有指定的、包扫描到的
@Configuration类中的bean定义注册后,才会来处理延迟导入的@Configuration
所谓延迟:在所有指定的、包扫描到的@Configuration类中的bean定义注册后,才会来处理延迟导入 的

让我们自己编程配置的bean提前注册,这样自动配置时的条件判断能发现到我们配置的,
就能做到不再配置 自动配置的bean等。比如:如果我们自己配置了数据源bean,则使用我们配置
的数据源,不在自动配置数据源。数 据源自动配置的代码中可以看到
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })

callRunners(context, applicationArguments)

在代码中我们看到,从applicationContext获取了所有 ApplicationRunnerCommandLineRunner类 型的bean,并执行了它们的run方法。
Tomcat是如何跑起来
从创建ApplicationContext处开始,对于servlet的AnnotationConfigServletWebServerApplicationContext类
public class AnnotationConfigServletWebServerApplicationContext
extends ServletWebServerApplicationContext implements AnnotationConfigRegistry {
}
ServletWebServerApplicationContext
这个context会通过从自身查找一个ServletWebServerFactory单例bean来创建、初始化、运行一个
WebServer

onRefresh()方法是AbstractApplicationContext中定义的供子类实现的空模板方法,在refresh()方法中被调用。
TomcatServletWebServerFactory中的实现:
@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		connector.setThrowOnFailure(true);
		tomcat.getService().addConnector(connector);
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatWebServer(tomcat);
	}
  • 作者:踩踩踩从踩
  • 原文链接:https://blog.csdn.net/qq_33373609/article/details/122936853
    更新时间:2022年10月20日08:17:31 ,共 9541 字。