SpringBoot 核心源码解读

Posted 踩踩踩从踩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot 核心源码解读相关的知识,希望对你有一定的参考价值。

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 CommandLineRunnerbean  在同一个应用程序上下文中,可以使用@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 获取了所有 ApplicationRunner CommandLineRunner 类 型的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);
	

以上是关于SpringBoot 核心源码解读的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码解读---推断构造方法解析

Spring源码解读---推断构造方法解析

Spring源码解读---推断构造方法解析

Spring源码解读---推断构造方法解析

springboot自动配置源码解读以及jdbc和redis配置原理小点

SpringBoot源码解读系列二——依赖配置