Spring Boot启动过程(一)

  之前在排查一个线上问题时,不得不仔细跑了很多遍 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());
        }
    }
</span><span style="color: rgba(0, 0, 255, 1)">protected</span><span style="color: rgba(0, 0, 0, 1)"> Log getApplicationLog() {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.mainApplicationClass == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> logger;
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> LogFactory.getLog(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.mainApplicationClass);
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> logStarting(Log log) {
    Assert.notNull(log, </span>"Log must not be null"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (log.isInfoEnabled()) {
        log.info(getStartupMessage());
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (log.isDebugEnabled()) {
        log.debug(getRunningMessage());
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> logStartupProfileInfo(ConfigurableApplicationContext context) {
    Log log </span>=<span style="color: rgba(0, 0, 0, 1)"> getApplicationLog();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (log.isInfoEnabled()) {
        String[] activeProfiles </span>=<span style="color: rgba(0, 0, 0, 1)"> context.getEnvironment().getActiveProfiles();
        </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (ObjectUtils.isEmpty(activeProfiles)) {
            String[] defaultProfiles </span>=<span style="color: rgba(0, 0, 0, 1)"> context.getEnvironment().getDefaultProfiles();
            log.info(</span>"No active profile set, falling back to default profiles: "
                    +<span style="color: rgba(0, 0, 0, 1)"> StringUtils.arrayToCommaDelimitedString(defaultProfiles));
        }
        </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            log.info(</span>"The following profiles are active: "
                    +<span style="color: rgba(0, 0, 0, 1)"> StringUtils.arrayToCommaDelimitedString(activeProfiles));
        }
    }
}</span></pre>

   接着 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;
    }
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> load(Object source) {
    Assert.notNull(source, </span>"Source must not be null"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (source <span style="color: rgba(0, 0, 255, 1)">instanceof</span> Class&lt;?&gt;<span style="color: rgba(0, 0, 0, 1)">) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span> load((Class&lt;?&gt;<span style="color: rgba(0, 0, 0, 1)">) source);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (source <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Resource) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> load((Resource) source);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (source <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Package) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> load((Package) source);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (source <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> CharSequence) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> load((CharSequence) source);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> IllegalArgumentException("Invalid source type " +<span style="color: rgba(0, 0, 0, 1)"> source.getClass());
}</span></pre>

  类型不同加载过程不同,其中 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 </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    String beanName </span>= (name != <span style="color: rgba(0, 0, 255, 1)">null</span> ? name : <span style="color: rgba(0, 0, 255, 1)">this</span>.beanNameGenerator.generateBeanName(abd, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.registry));
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (qualifiers != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
        </span><span style="color: rgba(0, 0, 255, 1)">for</span> (Class&lt;? <span style="color: rgba(0, 0, 255, 1)">extends</span> Annotation&gt;<span style="color: rgba(0, 0, 0, 1)"> qualifier : qualifiers) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (Primary.<span style="color: rgba(0, 0, 255, 1)">class</span> ==<span style="color: rgba(0, 0, 0, 1)"> qualifier) {
                abd.setPrimary(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (Lazy.<span style="color: rgba(0, 0, 255, 1)">class</span> ==<span style="color: rgba(0, 0, 0, 1)"> qualifier) {
                abd.setLazyInit(</span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
            }
            </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                abd.addQualifier(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> AutowireCandidateQualifier(qualifier));
            }
        }
    }

    BeanDefinitionHolder definitionHolder </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BeanDefinitionHolder(abd, beanName);
    definitionHolder </span>= AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.registry);
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.registry);
}</span></pre>

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

    protected AbstractBeanDefinition(ConstructorArgumentValues cargs, MutablePropertyValues pvs) {setConstructorArgumentValues(cargs);
        setPropertyValues(pvs);
    }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> StandardAnnotationMetadata(Class&lt;?&gt; introspectedClass, <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> nestedAnnotationsAsMap) {
    </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">(introspectedClass);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.annotations =<span style="color: rgba(0, 0, 0, 1)"> introspectedClass.getAnnotations();
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.nestedAnnotationsAsMap =<span style="color: rgba(0, 0, 0, 1)"> nestedAnnotationsAsMap;
}</span></pre>

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

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

==========================================================

咱最近用的 github:https://github.com/saaavsaaa

微信公众号: