SpringBoot启动流程源码分析
先写个HelloApplication.java :
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
run方法最终在SpringApplication中调用
return new SpringApplication(primarySources).run(args);
可以看到包括两部分:
-
实例化一个SpringApplication对象
-
调用其run方法
SpringApplication对象的实例化过程
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();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
- this.webApplicationType = WebApplicationType.deduceFromClasspath();
- setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
- setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class));
- this.mainApplicationClass = deduceMainApplicationClass();
可以看到主要就是这几个方法调用,逐个分析:
deduceFromClasspath
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
}
catch (IllegalAccessError err) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
className + "]: " + err.getMessage(), err);
}
catch (Throwable ex) {
// Typically ClassNotFoundException or NoClassDefFoundError...
return false;
}
}
- isPresent会尝试根据字符串来加载类,从而判断当前的classpath中有没有对应的类
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;
}
- deduceFromClasspath的作用就是根据一些流程来判断有没有类似"org.springframework.web.reactive.DispatcherHandler"的这些类存在,从而判断类型。
NONE
The application should not run as a web application and should not start an embedded web server.REACTIVE
The application should run as a reactive web application and should start an embedded reactive web server.SERVLET
The application should run as a servlet-based web application and should start an embedded servlet web server.
setInitializers
调用源码,按照层次换行
setInitializers(
(Collection)getSpringFactoriesInstances(
ApplicationContextInitializer.class
)
)
其中setInitializers
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
没什么好说的
关键在getSpringFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
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;
}
//createSpringFactoriesInstances的关键部分,一个工厂方法,没啥好说的
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names)
{
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
单步调试发现names有这么多
- "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
- "org.springframework.boot.context.ContextIdApplicationContextInitializer"
- "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
- "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
- "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
- "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"
所以这一步会注册6个initializers,后续可以看到对他们的调用
setListeners
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//老老实实的setter
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>();
this.listeners.addAll(listeners);
}
剩下的和上一步一模一样,只是names变成了
0 = "org.springframework.boot.ClearCachesApplicationListener"
1 = "org.springframework.boot.builder.ParentContextCloserApplicationListener"
2 = "org.springframework.boot.context.FileEncodingApplicationListener"
3 = "org.springframework.boot.context.config.AnsiOutputApplicationListener"
4 = "org.springframework.boot.context.config.ConfigFileApplicationListener"
5 = "org.springframework.boot.context.config.DelegatingApplicationListener"
6 = "org.springframework.boot.context.logging.ClasspathLoggingApplicationListener"
7 = "org.springframework.boot.context.logging.LoggingApplicationListener"
8 = "org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"
9 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"
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;
}
获取当前调用栈,看看哪个方法名字是main
然而main方法也可以被用作实例方法,各种重载啥的,当然一般生物不会那么做
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);
//关键点1:listener start
listeners.starting();
try {
//应用参数,一般是个CommandLineArgs
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//小关键点1
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//根据上一步的配置搞些事情,貌似影响不大的样子
configureIgnoreBeanInfo(environment);
//打印图标
Banner printedBanner = printBanner(environment);
//关键点2
context = createApplicationContext();
//用于后面的catch语句中处理异常,不用管它
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//关键点3
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//关键点4
refreshContext(context);
//关键点5
afterRefresh(context, applicationArguments);
//计时器不用管
stopWatch.stop();
//日志啥的
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//关键点6
listeners.started(context);
//关键点7
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//关键点8:listener
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//返回值没人接收,不用管它
return context;
}
所以要分析8个主要矛盾和一个次要矛盾
先拿次要矛盾练练手
prepareEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
environment的初始化,配置文件加载,包括大名鼎鼎的profile
listeners.starting()
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
发布一个ApplicationStartingEvent事件
createApplicationContext()
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);
}
根据不同的webApplication类型初始化同的容器,使用BeanUtils来实例化,其内部是用反射来搞的
prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment); //1
postProcessApplicationContext(context);//2
applyInitializers(context);//3
//发消息
listeners.contextPrepared(context);
//日志里打出被激活的profile,不用管
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//4
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);//5
//打出banner等信息,不用管
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {//6 允许同名覆盖吗?
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();//7
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));//8
//发消息
listeners.contextLoaded(context);
}
context.setEnvironment(environment);
@Overridepublic void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment); this.reader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}
public void setEnvironment(Environment environment) {
this.conditionEvaluator = new ConditionEvaluator(this.registry, environment, null);
}
可以看到条件注解的解析工具是在这里注册的,根据环境配置文件来搞的
postProcessApplicationContext
/**
* Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
* apply additional processing as required.
* @param context the application context
*/
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
getBeanFactory的结果是一个DefaultListableBeanFactory
setConversionService,ConversionService是用来做类型转换的
有个converse接口
applyInitializers
/**
* Apply any {@link ApplicationContextInitializer}s to the context before it is
* refreshed.
* @param context the configured ApplicationContext (not refreshed yet)
* @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
之前分析过setInitializers方法,这里先获取到Initializers,逐个执行其初始化方法
一般在初始化里面会进行
-
listener的注册
-
注册添加一些beans啥的
-
添加postProcess
beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
一个DefaultListableBeanFactory
registerSingleton
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);//5
注册一个这样的单例,但不知道有啥用啊
setAllowBeanDefinitionOverriding
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
同名覆盖
å�¨è¿�é��æ��å ¥å�¾ç��æ��è¿°所谓的Spring容器,其实就是一个大Map,其管理的bean的id(也就是bean的name)应该都是不一样的,假如项目中有两个bean的name一样了,在项目启动时一般会报类似于下图的同名异常:
但在有些情况下,spring并不会报"同名异常" ,其处理方式是:
如果两个bean的name相同,则后出现的bean会覆盖前面出现的同名bean
1
所带来的问题:
如果启动时,对于同名的bean加载没有异常信息,出现问题后将会很难进行定位。
1
产生原因:
spring中的DefaultListableBeanFactory类有个属性:allowBeanDefinitionOverriding,
默认情况下为true,即允许重名的bean可以被覆盖。
可参考下面源码DeafaultListable的
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); //先获取已经存在的同名bean
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {//存在,不允许覆盖,搞事情
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
//存在,允许覆盖
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition); //拿新的替换掉旧的
}
getAllSources
Set<Object> sources = getAllSources();
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet<>();
if (!CollectionUtils.isEmpty(this.primarySources)) {
allSources.addAll(this.primarySources);
}
if (!CollectionUtils.isEmpty(this.sources)) {
allSources.addAll(this.sources);
}
return Collections.unmodifiableSet(allSources);
}
调试时发现就加载了个主类,HelloApplication的那个
load
load(context, sources.toArray(new Object[0]))
/**
* Load beans into the application context.
* @param context the context to load beans into
* @param sources the sources to load
*/
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
BeanDefinitionLoader用于从源加载Bean的定义信息,并封装成BeanDefinition对象,并注册到ApplicationContext中,加载的源可以是类注解、XML文件、package、classpath、Groovy文件等。
这里加载的source只有一个HelloApplication,也就是主类
最后一步加载BeanDefinition,注意在doRegisterBean中有一个
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
对应前面提到的条件判断
最终的核心部分就是一个
beanDefinition = definitionHolder.getBeanDefinition()
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
BeanDefinition中包含了当前Bean有哪些注解,名字,哪个类等等信息
总结:load这一步就是读取source文件,从中获取各种BeanDefinition,加到beanFactory的map里面完事,Bean的实例化、初始化还在后面
refreshContext(context)
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
最终调用的还是AbstractApplicationContext的refresh,在IOC中继续研究
afterRefresh(context, applicationArguments)
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
没实现啊,可以在这里做一些钩子?
listeners.started(context)
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
他又来发事件了,但这次是通过context来发的,跟上次不同
callRunners(context, applicationArguments)
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);
}
}
}
逐个调用Runner中的run方法,但是我的Runner是空的哇
立刻实现一个:
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class MRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
for (int i = 0; i < 100; i++) {
try {
System.out.println(i);
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
启动后果然开始输出数字了
listeners.running(context)
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
所以listener的作用就是发事件?
对start事件的单步调试
发了三次事件,对第一次仔细研究一下
发现最终接受的只有一个loglisterner,所以控制台的输出使用事件机制实现的吗?
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
else if (event instanceof ContextClosedEvent
&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}