SpringApplication.run方法分析

背景知识

无论是分析SpringApplication.run方法还是分析SpringBoot自动配置原理或者其它方面,有一个配置文件始终绕不开,那就是META-INF目录下的spring.factories文件,SpringBoot的自动配置原理就是基于该文件。在该文件中配置了大量的Key-Value,如果需要扩展SpringBoot,可以参考这种写法。在这里插入图片描述

源码分析

通常我们启动一个SpringBoot应用,在启动类上添加一个@SpringBootApplication注解,然后写一个main方法,在该方法内部执行SpringApplication.run,传入启动类的class以及main方法的参数。

@SpringBootApplication
public class Application {

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

首先我们从SpringApplication的run方法开始分析,在该方法中调用重载的run方法。

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class<?>[] { primarySource }, args);
}

在重载的run方法中,创建了一个SpringApplication实例,并调用重载run方法。

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
}

在SpringApplication构造函数中调用了重载的构造函数。

public SpringApplication(Class<?>... primarySources) {
	this(null, primarySources);
}

在重载的构造函数中,完成了当前环境的推断以及ApplicationContextInitializer实现类实例化以及ApplicationListener实现类的实例化等。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 推断当前运行环境
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	// ApplicationContextInitializer实现类实例化
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// ApplicationListener实现类实例化
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 推断main方法所在类
	this.mainApplicationClass = deduceMainApplicationClass();
}

也许大家比较好奇SpringBoot是如何推断当前运行环境,这里我们就先分析这个推断运行环境逻辑。

其实推断逻辑非常简单,就是判断某些类存不存在类路径下。

static WebApplicationType deduceFromClasspath() {
	if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
			&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
		return WebApplicationType.REACTIVE;
	}
	for (String className : SERVLET_INDICATOR_CLASSES) {
		if (!ClassUtils.isPresent(className, null)) {
			return WebApplicationType.NONE;
		}
	}
	return WebApplicationType.SERVLET;
}

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };

private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";

private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";


明白SpringBoot是如何推断当前运行环境后,接下来我们就分析SpringBoot的核心方法-run方法,也是我们本次分析的重点方法。

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();// 创建一个秒表
	stopWatch.start(); // 启动秒表
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty(); 
	SpringApplicationRunListeners listeners = getRunListeners(args); // 获取classpath:META-INF/spring.factories文件中key为:org.springframework.boot.SpringApplicationRunListener的value 并进行实例化
	listeners.starting(); // 调用所有SpringApplicationRunListener实现类的starting方法
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 预刷新环境
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment); // 打印Banner
		context = createApplicationContext(); // 创建应用上下文
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 预刷新应用上下文
		refreshContext(context); // 刷新应用上下文
		afterRefresh(context, applicationArguments); // 后置刷新
		stopWatch.stop(); // 停止秒表
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context); // 调用所有SpringApplicationRunListener实现类的started方法
		callRunners(context, applicationArguments); // 调用所有ApplicationRunner和CommandLineRunner的run方法
	} catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		listeners.running(context);// 调用所有SpringApplicationRunListener实现类的running方法
	} catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}// 返回应用上下文
	return context;
}
getRunListeners

首先来分析getRunListeners方法,在该方法中,通过getSpringFactoriesInstances方法来获取classpath下META-INF/spring.factories文件中key为org.springframework.boot.SpringApplicationRunListener的value,然后并进行实例化,将结果作为参数传入到SpringApplicationRunListeners的构造函数中,最后返回该实例。

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

在SpringBoot工程中该值只有一个,即EventPublishingRunListener。在SpringBoot自动配置模块-SpringBootAutoConfigure工程中并未定义。

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

接下来就是调用SpringApplicationRunListeners的starting方法,在该方法中循环调用所有SpringApplicationRunListener 实现类的starting方法,通过前面分析我们可以得知默认情况下只有一个EventPublishingRunListener。

// SpringApplicationRunListeners#starting
void starting() {
	for (SpringApplicationRunListener listener : this.listeners) {
		listener.starting();
	}
}

在EventPublishingRunListener的starting方法中,调用了Spring 应用事件广播器的multicastEvent方法来发布ApplicationStartingEvent事件。注意这个SimpleApplicationEventMulticaster是Spring的应用上下文事件广播器,并且Spring应用上下文是会去创建一个事件广播器,但并不是在这里,这还属于SpringBoot范围,并且Spring标准事件中并没有ApplicationStartingEvent事件,这个事件属于SpringBoot扩展出来的应用事件。

private final SimpleApplicationEventMulticaster initialMulticaster;
// EventPublishingRunListener#starting
public void starting() {
	this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
prepareEnvironment

创建DefaultApplicationArguments实例这里就不再分析了,无非就是对参数的封装。接下来就是prepareEnvironment方法。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
	// 首先根据当前环境来创建对应的Environment实例,例如当前是Servlet环境或者Reactive或者普通环境
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	// 配置刚创建出来的环境,主要是设置类型转换器,将命令行中的配置保存到Environment中
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	// 将前面配置进Environment中属性全部复制一份来再进行保存
	ConfigurationPropertySources.attach(environment);
	// 发布ApplicationEnvironmentPreparedEvent事件
	listeners.environmentPrepared(environment);
	// 绑定Environment到SpringApplication中
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		// 添加一个环境转换器
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}
	// 将前面配置进Environment中属性全部复制一份来再进行保存
	ConfigurationPropertySources.attach(environment);
	return environment;
}

configureIgnoreBeanInfo方法就是配置要忽略的BeanInfo信息,以及printBanner方法用来打印Banner,这里就不展开分析了。

createApplicationContext

createApplicationContext方法决定了SpringBoot使用何种应用上下文,如果是Servlet环境就使用AnnotationConfigServletWebServerApplication,如果是Reactive就使用AnnotationConfigReactiveWebServerApplicationContext,否则使用AnnotationConfigApplicationContext。最后使用反射来进行实例化。

    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";

	
	public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";


	public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {
	Class<?> contextClass = this.applicationContextClass;
	if (contextClass == null) {
		try {
			switch (this.webApplicationType) {// 根据前面推断结果来决定使用何种应用上下文。
			case SERVLET:
				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
				break;
			case REACTIVE:
				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
				break;
			default:
				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
			}
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
		}
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
prepareContext

创建完应用上下文之后,接下来便是预刷新应用上下文了-prepareContext,

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	context.setEnvironment(environment); // 将前面并配置好的Environement设置到应用上下文
	postProcessApplicationContext(context); // 后置处理应用上下文
	applyInitializers(context); // 调用所有ApplicationContextInitializer实现类的initialize方法
	listeners.contextPrepared(context); // 发布ApplicationContextInitializedEvent事件
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null); // 打印启动详情
		logStartupProfileInfo(context); // 打印当前环境(Profile)
	}
	// 将前面创建的DefaultApplicationArguments作为单例Bean注册到BeanFactory中
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {// 将PrintBanner作为单例Bean注册到BeanFactory中
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
		// 将是否允许BeanDefinition覆盖设置为false,Spring默认是允许的!!!
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.lazyInitialization) {// 注册一个BeanFactoryPostProcessor
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// 加载启动源
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	// 为main方法所在类创建BeanDefinition,并注册进Spring应用上下文
	load(context, sources.toArray(new Object[0]));
	listeners.contextLoaded(context);// 发布ApplicationPreparedEvent事件
}
refreshContext

在refreshContext方法中首先调用应用上下文的refresh方法来刷新应用上下文(调用应用上下文的refresh方法),接下来调用registerShutdownHook方法注册一个钩子。

private void refreshContext(ConfigurableApplicationContext context) {
	refresh((ApplicationContext) context);
	if (this.registerShutdownHook) {
		try {
			context.registerShutdownHook();
		}
		catch (AccessControlException ex) {
			// Not allowed in some environments.
		}
	}
}
afterRefresh

afterRefresh方法目前只是一个空实现,预留扩展方法。

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
callRunners

接下来就是调用秒表的stop方法结束计时(stopWatch.stop()),然后打印启动时间(秒数)。发布ApplicationStartedEvent事件以及AvailabilityChangeEvent事件(listeners.started(context))。这里就不再展开分析。

在callRunners方法中,通过应用上下文来获取所有ApplicationRunner以及CommandLineRunner接口实现类,接下来逐个调用其run方法。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
	List<Object> runners = new ArrayList<>();
	runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
	runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
	AnnotationAwareOrderComparator.sort(runners);
	for (Object runner : new LinkedHashSet<>(runners)) {
		if (runner instanceof ApplicationRunner) {
			callRunner((ApplicationRunner) runner, args);
		}
		if (runner instanceof CommandLineRunner) {
			callRunner((CommandLineRunner) runner, args);
		}
	}
}

最后,发布ApplicationReadyEvent以及AvailabilityChangeEvent事件(listeners.running(context))。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'KMSEncryptUtil': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [com.ideal.upms.common.utils.KMSEncryptUtil] from ClassLoader [sun.misc.Launcher$AppClassLoader@42a57993] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:265) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1253) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1168) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) at com.ideal.order.web.CloudorderWebAPP.main(CloudorderWebAPP.java:27)
06-09

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值