之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE。

  首先,普通的入口,这没什么好说的,我就随便贴贴代码了:

SpringApplication.run(Application.class, args);
-->

  public static ConfigurableApplicationContext run(Object source, String... args) {
    return run(new Object[] { source }, args);
  }

  public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return new SpringApplication(sources).run(args);
  }

  也就是一个静态方法,调用了构造函数创建实例,构造的参数是Object数组,这里new这个数组的时候传入了一个元素就是启动类的类对象实例(一般就是“new Object[] { Application.class” }),构造函数里调用了一个initialize方法。

  SpringApplication的initialize方法,首先在Object数组有值的情况下将数组放入一个final的类实例私有Object的Set集合中;然后用deduceWebEnvironment方法判断当前应用环境是否是web环境,判断逻辑是看Classpath是否同时存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext,缺一就认为不是。然后,调用setInitializers方法,设置类实例的私有List<ApplicationContextInitializer<?>>类型变量initializers:

    public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
this.initializers.addAll(initializers);
}

  设置的时候会先new,也就是说这方法每次都是整体更换,不会追加。这个方法的参数都是各个模块中配置在META-INF/spring.factories中的key为org.springframework.context.ApplicationContextInitializer的值,这些类都是接口ApplicationContextInitializer<C extends ConfigurableApplicationContext>的泛型实现。

    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方法去取上面说的被配置的ApplicationContextInitializer的名字放进Set<String>中,并用反射创建这些名字的实例。

  

  setInitializers方法之后又是setInitializers,参数同上都是getSpringFactoriesInstances方法获取,只不过这次参数Class<T> type泛型类型是org.springframework.context.ApplicationListener。

  

  initialize方法的最后一个步是设置实例的Class<?>类型私有属性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;
}

  实例化SpringApplication后调用了它的run实例方法(注意不是上面的静态方法)。一进run方法首先启动了StopWatch,这个StopWatch的功能在类的注释写可,大概意思是这是个简单的秒表,用于在开发过程中方便程序员调试性能等,非线程安全,不建议用于生产。configureHeadlessProperty设置使用Headless,对于只有远程登录使用的服务器来说这样性能要好一些。接着是加载用于这个run方法启动过程的监听器,依然是getSpringFactoriesInstances方法,这次的类型是org.springframework.boot.SpringApplicationRunListener:

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

  

    SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<SpringApplicationRunListener>(listeners);
}

  先是加载所有可用监听,然后初始化SpringApplicationRunListeners对象,最后循环启动所有SpringApplicationRunListener监听。启动监听的方法:

    @Override
public void started() {
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}

  ApplicationStartedEvent实例化传了两个参数,先看第一个参数this.application是怎么来的,实例的SpringApplication的run方法中,用于获取SpringApplicationRunListener,也就是前面说的getSpringFactoriesInstances被调用时:

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

  getSpringFactoriesInstances方法的参数包含SpringApplication.class和this,这两个参数被传入createSpringFactoriesInstances方法:

  

  可以看到,是通过反射创建实例的时候,将SpringApplication中的this传进来EventPublishingRunListener构造的,然后EventPublishingRunListener构造:

    public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}

  最后在构造ApplicationStartedEvent时传给它的基类EventObject的protected不可序列化属性source。实例化ApplicationStartedEvent后instance.getClass()并包装为ResolvableType类型以保存类型信息,并将它和event作为参数传入SimpleApplicationEventMulticaster的multicastEvent方法。multicastEvent首先获取ApplicationListener,使用getApplicationListeners方法,这个方法中抛开对listener做了一些缓存类工作外,主要就是将事件和对应的监听器做了下是否支持的验证,返回通过了retrieveApplicationListeners中通过了supportsEvent验证的监听器集合,这里就体现出了ResolvableType的作用,它保存了类型的信息同时对泛型类型也支持。

  得到了这些匹配的监听器后,判断当前Executor是否被设置过,如果为null则同步循环执行所有:invokeListener(listener, event);如果不为null则:

                executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});

  监听器执行的时候也会先判断是否是该由自己处理的事件,例如:

    @Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}

  监听启动后,只准备一些启动参数,和环境变量prepareEnvironment方法先是读取了应用的启动参数和profile配置,然后用listeners.environmentPrepared(environment)传给监听器:

    public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}

  接着判断如果environment是org.springframework.web.context.ConfigurableWebEnvironment的实例,但webEnvironment不是true,也就是说存在org.springframework.web.context.ConfigurableWebEnvironmen但不存在javax.servlet.Servlet的情况,会多执行一步environment = convertToStandardEnvironment(environment)转换。

  之后的printBanner就不细说了,如果你在resource下自定义了一个banner.txt文件,启动时会输出内容,否则输出:

  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.4.3.RELEASE)

  接着创建ConfigurableApplicationContext实例,方法也很简单,如果是web环境就BeanUtils.instantiate一个org.springframework.boot.context.embedded. AnnotationConfigEmbeddedWebApplicationContext的实例并强转为ConfigurableApplicationContext,否则用org.springframework.context.annotation. AnnotationConfigApplicationContext的实例强转。

  创建FailureAnalyzers实例,记录了ConfigurableApplicationContext实例中需要关注的部分,如果启动出错了可以据此分析,可以配置,具体的逻辑依然是老方法spring.factories:

  不同的Analyzer关注不同的部分,自己可以扩展配置,最后prepareFailureAnalyzers方法给所有Analyzer实例setBeanFactory(context.getBeanFactory()),一旦启动过程进入catch,被注册的Analyzer实例的analyze方法就会被触发执行,分析结果会被loggedExceptions.add(exception)加入到抛出的异常中:

    private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) {
for (FailureAnalyzer analyzer : analyzers) {
FailureAnalysis analysis = analyzer.analyze(failure);
if (analysis != null) {
return analysis;
}
}
return null;
} 例如:NoSuchBeanDefinitionFailureAnalyzer
@Override
protected FailureAnalysis analyze(Throwable rootFailure,
NoSuchBeanDefinitionException cause, String description) {
if (cause.getNumberOfBeansFound() != 0) {
return null;
}
List<AutoConfigurationResult> autoConfigurationResults = getAutoConfigurationResults(
cause);
StringBuilder message = new StringBuilder();
message.append(String.format("%s required %s that could not be found.%n",
description == null ? "A component" : description,
getBeanDescription(cause)));
if (!autoConfigurationResults.isEmpty()) {
for (AutoConfigurationResult provider : autoConfigurationResults) {
message.append(String.format("\t- %s%n", provider));
}
}
String action = String.format("Consider %s %s in your configuration.",
(!autoConfigurationResults.isEmpty()
? "revisiting the conditions above or defining" : "defining"),
getBeanDescription(cause));
return new FailureAnalysis(message.toString(), action, cause);
}

  prepareContext方法中postProcessApplicationContext会在this.beanNameGenerator存在的情况下加载自定义命名策略,然后在this.resourceLoader存在的情况下为context设置resourceLoader和classLoader。applyInitializers方法调用之前加载的Initializer的实例并执行其initialize方法,例如加载环境变量信息、注册EmbeddedServletContainerInitializedEvent的监听、注册CachingMetadataReaderFactoryPostProcessor等。listeners.contextPrepared(context)由于EventPublishingRunListener的contextPrepared是空的,先不说了。logStartupInfo部分初始化了logger,然后根据配置情况打印了启动或运行以及profile是否配置的日志:

    protected void logStartupInfo(boolean isRoot) {
if (isRoot) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarting(getApplicationLog());
}
} protected Log getApplicationLog() {
if (this.mainApplicationClass == null) {
return logger;
}
return LogFactory.getLog(this.mainApplicationClass);
} public void logStarting(Log log) {
Assert.notNull(log, "Log must not be null");
if (log.isInfoEnabled()) {
log.info(getStartupMessage());
}
if (log.isDebugEnabled()) {
log.debug(getRunningMessage());
}
} protected void logStartupProfileInfo(ConfigurableApplicationContext context) {
Log log = getApplicationLog();
if (log.isInfoEnabled()) {
String[] activeProfiles = context.getEnvironment().getActiveProfiles();
if (ObjectUtils.isEmpty(activeProfiles)) {
String[] defaultProfiles = context.getEnvironment().getDefaultProfiles();
log.info("No active profile set, falling back to default profiles: "
+ StringUtils.arrayToCommaDelimitedString(defaultProfiles));
}
else {
log.info("The following profiles are active: "
+ StringUtils.arrayToCommaDelimitedString(activeProfiles));
}
}
}

  接着prepareContext中注册启动参数(applicationArguments)到bean工厂,包括logger、commandLineArgs等。然后加载bean定义的来源并根据其中配置加载bean,这里的sources就是初始化启动类时传进来的那个sources:

    BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
this.sources = sources;
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
this.xmlReader = new XmlBeanDefinitionReader(registry);
if (isGroovyPresent()) {
this.groovyReader = new GroovyBeanDefinitionReader(registry);
}
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

  注意下面的sources是待加载的,和上面这段不是同一个:

    public int load() {
int count = 0;
for (Object source : this.sources) {
count += load(source);
}
return count;
} private int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
return load((Package) source);
}
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

  类型不同加载过程不同,其中Class<?>加载过程大概是通过BeanDefinitionLoader调用AnnotatedBeanDefinitionReader的registerBean方法:

    public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
} ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
} BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

  可以看到有生成方法名,设置默认注入的实例、延迟以及过滤等等,注入的过程包括初始化一些信息,如构造、内部类、注解等:

    protected AbstractBeanDefinition(ConstructorArgumentValues cargs, MutablePropertyValues pvs) {
setConstructorArgumentValues(cargs);
setPropertyValues(pvs);
} public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
super(introspectedClass);
this.annotations = introspectedClass.getAnnotations();
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
}

  其他三种比如有的有输入流什么的就不细总结了,这部分介绍Spring IOC的相关文章应该不少。

  prepareContext方法最后listeners.contextLoaded(context),加载监听器到context并广播ApplicationPreparedEvent事件。

转:Spring Boot启动过程的更多相关文章

  1. Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动

    之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...

  2. Spring Boot启动过程(七):Connector初始化

    Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...

  3. Spring Boot启动过程及回调接口汇总

    Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...

  4. Spring Boot启动过程(三)

    我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...

  5. Spring Boot 启动过程

    一切从SpringApplication.run()开始,最终返回一个ConfigurableApplicationContext 构造了一个SpringApplication对象,然后调用它的run ...

  6. Spring Boot启动过程(一)

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...

  7. Spring Boot启动过程(二)

    书接上篇 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractAppl ...

  8. Spring Boot 启动过程及 自定义 Listener等组件

    一.启动过程 二.自定义组件 package com.example.jdbc.listener; import org.springframework.context.ApplicationCont ...

  9. Spring boot 启动过程解析 logback

    使用 Spring Boot 默认的日志框架 Logback. 所有这些 POM 依赖的好处在于为开发 Spring 应用提供了一个良好的基础.Spring Boot 所选择的第三方库是经过考虑的,是 ...

随机推荐

  1. mysql字符集那些事

    1..查看mysql当前使用的字符集. 登录mysql 在mysql 里输入 show variables like 'character_set%' mysql> show variables ...

  2. js获取文件里面的所有文件名

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. volatile相关内容

    volatile是jvm提供的轻量级的同步机制 保证可见性(一个线程的修改对其它线程是可见的) 不保证原子性 禁止指令重排序 什么是指令重排? 计算机在执行程序时,为了提高性能,编译器和处理器会对指令 ...

  4. java面试题-Java集合相关

    1. ArrayList 和 Vector 的区别 ArrayList和Vector底层实现原理都是一样得,都是使用数组方式存储数据 Vector是线程安全的,但是性能比ArrayList要低. Ar ...

  5. 【Linux】Debian 下安装 Apache,MySQL,PHP

    首先,对你的源进行更新: $ sudo apt-get update 第一步--安装 Apache Apache 是一个开源软件,它目前运行在全球超过 50% 的服务器上,是 LAMP(Linux,A ...

  6. Test Complete 的自动化测试 --- 三角形

    Test Complete 的自动化测试 --- 三角形 PS:工具:Test Complete , OS:win 10 这里做三角形的测试与上一篇博客做计算器的测试大致一样,都是对.exe的执行文件 ...

  7. python 学习路程(一)

    好早之前就一直想学python,可是一直没有系统的学习过,给自己立个flag,从今天开始一步步掌握python的用法: python是一种脚本形式的语言,据说是面向废程序员学习开发使用的,我觉得很适合 ...

  8. Missing radix parameter 错误的解决办法

    下载了Mint-Ui的example,使用npm run dev时发现如下报错: ERROR in ./packages/loadmore/src/loadmore.vue ✘ http://esli ...

  9. 记录用户登陆信息,你用PHP是如何来实现的

    对于初入门的PHP新手来说,或许有一定的难度.建议大家先看看PHP中session的基础含义,需要的朋友可以选择参考. 下面我们就通过具体的代码示例,为大家详细的介绍PHP中session实现记录用户 ...

  10. 微信小程序使用 ECharts 实现数据可视化

    微信小程序使用 ECharts 显示图表 首先创建微信小程序 这里就不再赘述 下载 GitHub 上的 ecomfe/echarts-for-weixin 下载后解压,打开文件夹,里面的 ec-can ...