Springboot初始化流程解析

入口

@SpringBootApplication
public class DemoApplication {

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

}

以上是一個最簡單的Springboot程序(2.0.3版本)示例,也是我們最通用的寫法,但其中其實封裝這一系列復(fù)雜的功能操作,讓我們開始逐步進(jìn)行分析。

首先這里最重要的必然是注解@SpringBootApplication

@SpringBootApplication注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};


    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

}

@SpringBootApplication注解由幾個注解復(fù)合組成,其中最主要的就是@SpringBootConfiguration、@EnableAutoConfiguration@ComponentScan這三個。

@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

其中的@ComponentScan是spring的原生注解,@SpringBootConfiguration雖然是springboot中的注解,但其實質(zhì)就是包裝后的@Configuration,仍然是spring中的注解,用于代替xml的方式管理配置bean

@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    Class<?>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
    String[] excludeName() default {};

}

@EnableAutoConfiguration的定義如上,這里最重要的注解是@Import@AutoConfigurationPackage注解的實現(xiàn)也是基于@Import),借助@Import的幫助,將所有符合自動配置條件的bean定義加載到IoC容器中。關(guān)于@EnableAutoConfiguration注解后續(xù)涉及到時會再詳細(xì)說明。這里我們先回到啟動類的run方法從頭分析初始化流程。

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

可以看到'run'方法最終調(diào)用的是new SpringApplication(primarySources).run(args),這里首先創(chuàng)建了SpringApplication對象,然后調(diào)用其run方法

public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }
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對象進(jìn)行初始化,這里要專門提一下的是webApplicationTypegetSpringFactoriesInstances。

webApplicationType

它用來標(biāo)識我們的應(yīng)用是什么類型的應(yīng)用,來看一下deduceWebApplicationType()方法的實現(xiàn)

    private WebApplicationType deduceWebApplicationType() {
        if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
                && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : WEB_ENVIRONMENT_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }

其返回值是WebApplicationType類型的枚舉類,其值有NONE、SERVLET、REACTIVE三種,分別對應(yīng)非WEB應(yīng)用,基于servlet的WEB應(yīng)用和基于reactive的WEB應(yīng)用。

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 = Thread.currentThread().getContextClassLoader();
        // 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;
    }

這里的核心是SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法,來看一下

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

重點關(guān)注一下loadSpringFactories(classLoader)做了什么

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    List<String> factoryClassNames = Arrays.asList(
                            StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                    result.addAll((String) entry.getKey(), factoryClassNames);
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

這里的FACTORIES_RESOURCE_LOCATION定義為META-INF/spring.factories,因此該方法會掃描所有包下的該文件,將其解析成map對象并緩存到cache中以避免重復(fù)加載,springboot包下該文件的部分片段如下


# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

從這里可以看出,setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class))setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));分別對應(yīng)設(shè)置的是上述這些類。

解析完成后調(diào)用createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)處理解析結(jié)果,生成對應(yīng)的實例,源碼如下

    @SuppressWarnings("unchecked")
    private <T> List<T> createSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
            Set<String> names) {
        List<T> instances = new ArrayList<>(names.size());
        for (String name : names) {
            try {
                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);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException(
                        "Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }

這里的核心是通過ClassUtils.forName(name, classLoader)方法,以反射的方式生成類實例instanceClass。由此可以看出SpringFactoriesLoader.loadFactoryNames(type, classLoader)的作用就是將META-INF/spring.factories中配置的內(nèi)容進(jìn)行實例化的工廠方法類,具備很強(qiáng)的擴(kuò)展性,與SPI機(jī)制有異曲同工
的效果。

看完SpringApplication的初始化,接著跳回run方法繼續(xù)分析

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);//獲取并啟動監(jiān)聽器
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);//構(gòu)造容器環(huán)境
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();//創(chuàng)建容器
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);//準(zhǔn)備容器
            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;
    }

這里挑其中比較重要的幾個方法進(jìn)行分析

  1. 創(chuàng)建ConfigurableEnvironment對象
    private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // Create and configure the environment
        ConfigurableEnvironment environment = getOrCreateEnvironment();//初始化environment
        configureEnvironment(environment, applicationArguments.getSourceArgs());//加載默認(rèn)配置
        listeners.environmentPrepared(environment);//通知環(huán)境監(jiān)聽器,加載項目中的配置文件
        bindToSpringApplication(environment);
        if (this.webApplicationType == WebApplicationType.NONE) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertToStandardEnvironmentIfNecessary(environment);
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

通過getOrCreateEnvironment()方法創(chuàng)建容器環(huán)境

    private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        if (this.webApplicationType == WebApplicationType.SERVLET) {
            return new StandardServletEnvironment();
        }
        return new StandardEnvironment();
    }

可以看到environment存在則不會重復(fù)創(chuàng)建,當(dāng)應(yīng)用類型為servlet時創(chuàng)建的是StandardServletEnvironment對象,否則創(chuàng)建StandardEnvironment對象。

接著來看configureEnvironment(environment, applicationArguments.getSourceArgs())

    protected void configureEnvironment(ConfigurableEnvironment environment,
            String[] args) {
        configurePropertySources(environment, args);//加載啟動命令行配置屬性
        configureProfiles(environment, args);//設(shè)置active屬性
    }

configurePropertySources(environment, args)加載啟動命令行的配置屬性,來看一下實現(xiàn)

    protected void configurePropertySources(ConfigurableEnvironment environment,
            String[] args) {
        MutablePropertySources sources = environment.getPropertySources();
        if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
            sources.addLast(
                    new MapPropertySource("defaultProperties", this.defaultProperties));
        }
        //加載命令行配置
        if (this.addCommandLineProperties && args.length > 0) {
            String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
            if (sources.contains(name)) {
                PropertySource<?> source = sources.get(name);
                CompositePropertySource composite = new CompositePropertySource(name);
                composite.addPropertySource(new SimpleCommandLinePropertySource(
                        "springApplicationCommandLineArgs", args));
                composite.addPropertySource(source);
                sources.replace(name, composite);
            }
            else {
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
        }
    }

這里的MutablePropertySources對象用于存儲配置集合,其內(nèi)部維護(hù)了一個CopyOnWriteArrayList類型的list對象,當(dāng)默認(rèn)配置存在時,會向該list的尾部插入一個new MapPropertySource("defaultProperties", this.defaultProperties)對象。

接著來看configureProfiles(environment, args)

    protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
        environment.getActiveProfiles(); // ensure they are initialized
        // But these ones should go first (last wins in a property key clash)
        Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
        profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
        environment.setActiveProfiles(StringUtils.toStringArray(profiles));
    }

這里主要做的事情就是獲取environment.getActiveProfiles()的參數(shù)設(shè)置到environment中,即spring.profiles.active對應(yīng)的環(huán)境變量。

最后來看一下listeners.environmentPrepared(environment)

    public void environmentPrepared(ConfigurableEnvironment environment) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.environmentPrepared(environment);
        }
    }

這里的listeners就是之前通過META-INF/spring.factories注冊的所有l(wèi)isteners,后面我們先以其中最重要的ConfigFileApplicationListener做為例子進(jìn)行分析,接著來看listener.environmentPrepared(environment)

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

可以看到這里創(chuàng)建了一個ApplicationEnvironmentPreparedEvent類型的事件,并且調(diào)用了multicastEvent方法,通過該方法最終會調(diào)用到listener的onApplicationEvent方法,觸發(fā)事件監(jiān)聽器的執(zhí)行。

接下來具體看一下ConfigFileApplicationListeneronApplicationEvent方法做了什么

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

可以看到當(dāng)監(jiān)聽到ApplicationEnvironmentPreparedEvent類型的事件時,調(diào)用onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event)方法

    private void onApplicationEnvironmentPreparedEvent(
            ApplicationEnvironmentPreparedEvent event) {
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        postProcessors.add(this);
        AnnotationAwareOrderComparator.sort(postProcessors);
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessEnvironment(event.getEnvironment(),
                    event.getSpringApplication());
        }
    }

可以看到這里通過loadPostProcessors()方法加載了META-INF/spring.factories中的所有EnvironmentPostProcessor類到list中,同時把ConfigFileApplicationListener自己也添加進(jìn)去了。接著遍歷list中所有對象,并執(zhí)行postProcessEnvironment方法,于是接著來看該方法

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        addPropertySources(environment, application.getResourceLoader());
    }
    protected void addPropertySources(ConfigurableEnvironment environment,
            ResourceLoader resourceLoader) {
        RandomValuePropertySource.addToEnvironment(environment);
        new Loader(environment, resourceLoader).load();
    }

這里的核心是new Loader(environment, resourceLoader).load(),這里的Loader是一個內(nèi)部類,用于處理配置文件的加載,首先看一下其構(gòu)造方法

        Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
            this.environment = environment;
            this.resourceLoader = (resourceLoader != null ? resourceLoader
                    : new DefaultResourceLoader());
            this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
                    PropertySourceLoader.class, getClass().getClassLoader());
        }

可以看到這里的resourceLoader又是通過SpringFactoriesLoader進(jìn)行加載,那么來看看META-INF/spring.factories中定義了哪些resourceLoader

org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

從名字就可以看出來,PropertiesPropertySourceLoaderYamlPropertySourceLoader分別用于處理.properties和.yml類型的配置文件。

接著來看看load()方法做了什么

        public void load() {
            this.profiles = new LinkedList<>();
            this.processedProfiles = new LinkedList<>();
            this.activatedProfiles = false;
            this.loaded = new LinkedHashMap<>();
            initializeProfiles();//初始化
            while (!this.profiles.isEmpty()) {//定位解析資源文件
                Profile profile = this.profiles.poll();
                if (profile != null && !profile.isDefaultProfile()) {
                    addProfileToEnvironment(profile.getName());
                }
                load(profile, this::getPositiveProfileFilter,
                        addToLoaded(MutablePropertySources::addLast, false));
                this.processedProfiles.add(profile);
            }
            load(null, this::getNegativeProfileFilter,
                    addToLoaded(MutablePropertySources::addFirst, true));//對加載過的配置文件進(jìn)行排序
            addLoadedPropertySources();
        }

initializeProfiles()進(jìn)行了profiles的初始化,默認(rèn)會添加nulldefaultprofiles中,null對應(yīng)配置文件application.properties和application.yml,default對應(yīng)配置文件application-default.yml和application-default.properties,這里的null會被優(yōu)先處理,由于后處理的會覆蓋先處理的,因此其優(yōu)先級最低。

接著來看load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false))方法

        private void load(Profile profile, DocumentFilterFactory filterFactory,
                DocumentConsumer consumer) {
            getSearchLocations().forEach((location) -> {
                boolean isFolder = location.endsWith("/");
                Set<String> names = (isFolder ? getSearchNames() : NO_SEARCH_NAMES);
                names.forEach(
                        (name) -> load(location, name, profile, filterFactory, consumer));
            });
        }

這里重點是通過getSearchLocations()獲取配置文件的路徑,默認(rèn)會獲得4個路徑

  • file:./config/
  • file:./
  • classpath:/config/
  • classpath:/

接著會遍歷這些路徑,拼接配置文件名稱,選擇合適的yml或者properties解析器進(jìn)行解析,最后將結(jié)果添加到environmentpropertySources中。

  1. 通過createApplicationContext()創(chuàng)建run方法的返回值對象context
    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);
    }

可以看到這里也是根據(jù)webApplicationType的取值,分別創(chuàng)建不同的返回類型。

  1. 通過prepareContext(context, environment, listeners, applicationArguments,printedBanner)方法將listeners、environment、applicationArguments、printedBanner等重要組件與上下文對象context進(jìn)行關(guān)聯(lián)
    private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);//設(shè)置容器環(huán)境,包括各種變量
        postProcessApplicationContext(context);//執(zhí)行容器后置處理,為自定義預(yù)留擴(kuò)展
        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);//發(fā)布容器已加載事件。
    }

這里的sources裝的就是我們的啟動類,然后通過load(context, sources.toArray(new Object[0]))方法進(jìn)行加載

    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();
    }

來看一下loader是如何被加載的

    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());
    }
    private int load(Class<?> source) {
        if (isGroovyPresent()
                && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
            // Any GroovyLoaders added in beans{} DSL can contribute beans here
            GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
                    GroovyBeanDefinitionSource.class);
            load(loader);
        }
        if (isComponent(source)) {
            this.annotatedReader.register(source);
            return 1;
        }
        return 0;
    }

經(jīng)過一系列調(diào)用之后最終由load(Class<?> source)方法執(zhí)行,這里比較有趣的是當(dāng)Groovy存在時居然是優(yōu)先調(diào)用Groovy的方式進(jìn)行加載,否則才走this.annotatedReader.register(source)方法將啟動類注冊到beanDefinitionMap中。

  1. 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();
    }
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

這個refresh()方法相當(dāng)重要,尤其是invokeBeanFactoryPostProcessors(beanFactory),這是實現(xiàn)spring-boot-starter-*(mybatis、redis等)自動化配置的關(guān)鍵部分,后續(xù)再詳細(xì)講解。

總結(jié)

至此Springboot的啟動流程已經(jīng)大體分析完了,也了解了配置文件和啟動類分別是是如何被加載的,但仍有兩個問題待解,一是Springboot的核心思想約定大于配置是如何做到的,二是Springboot的各種spring-boot-starter-*是如何發(fā)揮作用的,這兩個問題留待后續(xù)文章繼續(xù)分析。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 寫在前面: 由于該系統(tǒng)是底層系統(tǒng),以微服務(wù)形式對外暴露dubbo服務(wù),所以本流程中SpringBoot不基于...
    jianshu_tr閱讀 38,016評論 5 88
  • springboot 概述 SpringBoot能夠快速開發(fā),簡化部署,適用于微服務(wù) 參考嘟嘟大神SpringBo...
    一紙硯白閱讀 5,750評論 2 20
  • 寫在前面: 由于該系統(tǒng)是底層系統(tǒng),以微服務(wù)形式對外暴露dubbo服務(wù),所以本流程中SpringBoot不基于...
    sherlock_6981閱讀 768評論 0 10
  • 一個簡單的SB程序如下,點擊main方法左邊的原諒色的三角形就能把程序啟動起來,雖然什么功能都沒有,但是啟動做了很...
    _六道木閱讀 959評論 0 0
  • 最近隨著新一輪的購房政策出臺,身邊各種聲音不絕于耳,心里感覺七上八下。但也不知道到底在慌什么,或許是周遭的一切讓人...
    紅豆女士閱讀 294評論 0 0

友情鏈接更多精彩內(nèi)容