一、Spring Boot启动源码解析

简要说明:本次源码解析基于spring boot 1.5.9版本,该源码解析纯粹是为了记录自己所理解的东西,如果有误,请指正。

Spring boot 启动类:

@SpringBootApplication
public class Application {

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

@SpringBootApplication该注解源码如下:

主要是引用了其他三个注解@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan暂不作分析。

SpringApplication.run(Application.class, args) run方法的执行

/**
 * Static helper that can be used to run a {@link SpringApplication} from the
 * specified sources using default settings and user supplied arguments.
 * @param sources the sources to load
 * @param args the application arguments (usually passed from a Java main method)
 * @return the running {@link ApplicationContext}
 */
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
	return new SpringApplication(sources).run(args);
}

先构造一个SpringApplication对象

/**
 * Create a new {@link SpringApplication} instance. The application context will load
 * beans from the specified sources (see {@link SpringApplication class-level}
 * documentation for details. The instance can be customized before calling
 * {@link #run(String...)}.
 * @param sources the bean sources
 * @see #run(Object, String[])
 * @see #SpringApplication(ResourceLoader, Object...)
 */
public SpringApplication(Object... sources) {
	initialize(sources);
}

然后执行initialize(sources)方法,此处的sources即执行main方法的类实例对象Application.class,initialize(source)源码如下:

@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
	if (sources != null && sources.length > 0) {
		this.sources.addAll(Arrays.asList(sources));
	}
	this.webEnvironment = deduceWebEnvironment();
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

由以上源码可知主要执行4个方法:

deduceWebEnvironment()

判断当前环境是否是web环境,太简单不做详述。

初始化ApplicationContextInitializer的子类

setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));

第二个方法setinitializers()之前先执行getSpringFactoriesInstances(ApplicationContextInitializer.class)

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

在方法SpringFactoriesLoader.loadFactoryNames(type,classLoader)中

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		try {
			Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			List<String> result = new ArrayList<String>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
				String factoryClassNames = properties.getProperty(factoryClassName);
				result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
			}
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
					"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	} 

即通过在jar中的 META-INF/spring.factories中找到需要初始化的ApplicationContextInitializer的实现类

spring-boot-1.5.9.RELEASE.jar包中的META-INF/spring.factories中ApplicationContextInitializer如下:

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer

将需要初始化的bean名字存放到Set<String> names,接下来就是实例化这些类

List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);

初始化ApplicationListener的子类

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

方法如3.初始化ApplicationContextInitializer一样的执行过程

deduceMainApplicationClass找到main方法的类实例

this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
	try {
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			if ("main".equals(stackTraceElement.getMethodName())) {
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

通过new一个运行时异常,找到栈,在栈信息中遍历得到main方法的主类

至此SpringApplication初始化完成。

开始执行SpringApplication的run方法

《一、Spring Boot启动源码解析》

第321行代码SpringApplicationRunListeners listeners = getRunlisteners(args)

《一、Spring Boot启动源码解析》

和new SpringApplication时使用方法一致,在META-INF/spring.factories中得到SpringApplicationRunListener的实现类并且实例化这些对象

第292行执行SpringApplicationRunListener子类的接口中的starting方法

第296行执行prepareEnvironment(listeners,applicationArguments)方法

《一、Spring Boot启动源码解析》

上图第323行getOrCreateEnvironment()方法new StandardServletEnvironment()类

上图第324行configureEnvironment(environment, applicationArguments.getSourceArgs())

protected void configureEnvironment(ConfigurableEnvironment environment,
      String[] args) {
   configurePropertySources(environment, args);
   configureProfiles(environment, args);
}

configurePropertySources得到new StandardServletEnvironment()构造方法时配置的4个:

servletContextInitParams
servletConfigInitParams
systemProperties
systemEnvironment

configureProfiles(environment, args)得到spring.profiles.active的值

然后执行第325行的springApplicationRunListeners中所有listerns的environmentPrepared()方法

接下来执行run方法的第298行printBanner方法,该方法打印banner信息,即启动时默认的Spring异形文字信息,可以有Image和Textg两种形式默认使用console。通过在properties中配置”banner.location”指定banner.txt的位置来修改文本信息。默认使用 SpringBootBanner类中的信息。如果是图片使用”banner.image.location”指定图片位置,图片只支持gif、jpg、png三种扩展名格式。

接下来执行第329行createApplicationContext()方法,创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext的实例,即spring的上下文环境。

第301行执行prepareContext(context, environment, listeners, applicationArguments, printedBanner)方法。上下文环境准备工作。

private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   applyInitializers(context);
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }

   // Add boot specific singleton beans
   context.getBeanFactory().registerSingleton("springApplicationArguments",
         applicationArguments);
   if (printedBanner != null) {
      context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
   }

   // Load the sources
   Set<Object> sources = getSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[sources.size()]));
   listeners.contextLoaded(context);
}

applyInitializers(context)中开始执行ApplicationContextInitializer接口的initialize方法,还有SpringApplicationListener接口中的contextPrepared和contextLoaded方法。

run方法第303行的refreshContext()方法中执行上下文的refresh()方法,即Spring初始化过程中父类AbstractApplicationContext的refresh(),该方法不做详述,太复杂,但与spring一致。

run方法第304行afterRefres()方法启用ApplicationRunner和CommandLineRunner接口中的run方法

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/scemsjyd/article/details/79615673
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞