手记

spring boot 2.0 源码分析(一)

在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例:

1.先从创建示例中的main函数开始读起:

package com.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** *  spring boot 2.0 源码分析 *  author lizongshen *  date 2018/06/02 */ @SpringBootApplicationpublic class DemoApplication {    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

2.在这里我们可以看到,spring boot是通过SpringApplication.run这个函数来进行启动的,其中args可以传递启动时需要的个性化参数。跳转到源码中继续一探究竟:

    /**     * Static helper that can be used to run a {@link SpringApplication} from the     * specified source using default settings.     * @param primarySource the primary source to load     * @param args the application arguments (usually passed from a Java main method)     * @return the running {@link ApplicationContext}     */
    public static ConfigurableApplicationContext run(Class<?> primarySource,
            String... args) {        return run(new Class<?>[] { primarySource }, args);
    }

3.在这里我们看到,其把primarySource这个参数包装成数组,跳转到了另外一个同样的方法中。

小发现:SpringApplication.run()函数是允许同时启动多个Application的。

接着往下读

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

4.在这段代码中,我们可以看到spring boot把我们所使用的静态方法,创建出了一个SpringApplication的实例,并启动了实例中的run方法。

小知识:根据这个发现,我们也可以在main函数中,自己创建SpringApplication的实例,然后调用实例方法run。

我们来看一下SpringApplication的构造函数期间都干了些什么事:

    /**     * Create a new {@link SpringApplication} instance. The application context will load     * beans from the specified primary sources (see {@link SpringApplication class-level}     * documentation for details. The instance can be customized before calling     * {@link #run(String...)}.     * @param primarySources the primary bean sources     * @see #run(Class, String[])     * @see #SpringApplication(ResourceLoader, Class...)     * @see #setSources(Set)     */
    public SpringApplication(Class<?>... primarySources) {        this(null, primarySources);
    }    /**     * Create a new {@link SpringApplication} instance. The application context will load     * beans from the specified primary sources (see {@link SpringApplication class-level}     * documentation for details. The instance can be customized before calling     * {@link #run(String...)}.     * @param resourceLoader the resource loader to use     * @param primarySources the primary bean sources     * @see #run(Class, String[])     * @see #setSources(Set)     */
    @SuppressWarnings({ "unchecked", "rawtypes" })    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 = deduceWebApplicationType();        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));        this.mainApplicationClass = deduceMainApplicationClass();
    }

在这里主要是初始化了SpringApplication的私有属性,在构造的过程中,调用了另外一个构造函数,并传递了ResourceLoader的参数。

小知识:通过ResourceLoader这个参数,我们看到如果自定义SpringApplication在初始化过程中,是可以通过ResourceLoader来引入自定义资源的。

接着往下看,来看看run函数的真面目

    /**     * Run the Spring application, creating and refreshing a new     * {@link ApplicationContext}.     * @param args the application arguments (usually passed from a Java main method)     * @return a running {@link ApplicationContext}     */
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            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);            callRunners(context, applicationArguments);
        }        catch (Throwable ex) {            handleRunFailure(context, ex, exceptionReporters, listeners);            throw new IllegalStateException(ex);
        }        try {
            listeners.running(context);
        }        catch (Throwable ex) {            handleRunFailure(context, ex, exceptionReporters, null);            throw new IllegalStateException(ex);
        }        return context;
    }

这段代码很长,我们先来一点一点地分析。
1.通过configureHeadlessProperty();这行代码,配置属性:

    private void configureHeadlessProperty() {
        System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
                SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
    }

2.通过SpringApplicationRunListeners listeners = getRunListeners(args);这行代码获取了监听器:

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

3.通过listeners.starting();这行代码启动监听器:

    public void starting() {
        Iterator var1 = this.listeners.iterator();        while(var1.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
            listener.starting();
        }

    }

4.通过context = this.createApplicationContext();创建了容器

    /**      * Strategy method used to create the {@link ApplicationContext}. By default this     * method will respect any explicitly set application context or application context     * class before falling back to a suitable default.     * @return the application context (not yet refreshed)     * @see #setApplicationContextClass(Class)     */
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;        if (contextClass == null) {            try {                switch (this.webApplicationType) {                case SERVLET:
                    contextClass = Class.forName(DEFAULT_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);
    }

5.通过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 = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");        load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

6.通过this.refreshContext(context);刷新容器:

    private void refreshContext(ConfigurableApplicationContext context) {        this.refresh(context);        if(this.registerShutdownHook) {            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
                ;
            }
        }

    }

在刷新容器完成之后,调用afterRefresh函数,跟踪过去发现是空的,可能是预留了一个扩展点。

通过阅读发现spring boot 2.0的源码和1.x的源码对比起来,细节方面还是变化很大的。当然,这些还只是其中的冰山一角,后面我会继续与大家分享在源码阅读中的一些心得体会,

作者:Lizongshen  
出处:http://www.cnblogs.com/lizongshen/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。


0人推荐
随时随地看视频
慕课网APP