美文网首页
Springboot初始化流程解析

Springboot初始化流程解析

作者: 海涛_meteor | 来源:发表于2019-09-21 13:44 被阅读0次

    入口

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

    以上是一个最简单的Springboot程序(2.0.3版本)示例,也是我们最通用的写法,但其中其实封装这一系列复杂的功能操作,让我们开始逐步进行分析。

    首先这里最重要的必然是注解@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注解由几个注解复合组成,其中最主要的就是@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan这三个。

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

    其中的@ComponentScan是spring的原生注解,@SpringBootConfiguration虽然是springboot中的注解,但其实质就是包装后的@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注解的实现也是基于@Import),借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器中。关于@EnableAutoConfiguration注解后续涉及到时会再详细说明。这里我们先回到启动类的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'方法最终调用的是new SpringApplication(primarySources).run(args),这里首先创建了SpringApplication对象,然后调用其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对象进行初始化,这里要专门提一下的是webApplicationTypegetSpringFactoriesInstances

    webApplicationType

    它用来标识我们的应用是什么类型的应用,来看一下deduceWebApplicationType()方法的实现

        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类型的枚举类,其值有NONESERVLETREACTIVE三种,分别对应非WEB应用,基于servlet的WEB应用和基于reactive的WEB应用。

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

    重点关注一下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中以避免重复加载,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));分别对应设置的是上述这些类。

    解析完成后调用createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)处理解析结果,生成对应的实例,源码如下

        @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中配置的内容进行实例化的工厂方法类,具备很强的扩展性,与SPI机制有异曲同工
    的效果。

    看完SpringApplication的初始化,接着跳回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);//获取并启动监听器
            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. 创建ConfigurableEnvironment对象
        private ConfigurableEnvironment prepareEnvironment(
                SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
            // Create and configure the environment
            ConfigurableEnvironment environment = getOrCreateEnvironment();//初始化environment
            configureEnvironment(environment, applicationArguments.getSourceArgs());//加载默认配置
            listeners.environmentPrepared(environment);//通知环境监听器,加载项目中的配置文件
            bindToSpringApplication(environment);
            if (this.webApplicationType == WebApplicationType.NONE) {
                environment = new EnvironmentConverter(getClassLoader())
                        .convertToStandardEnvironmentIfNecessary(environment);
            }
            ConfigurationPropertySources.attach(environment);
            return environment;
        }
    

    通过getOrCreateEnvironment()方法创建容器环境

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

    可以看到environment存在则不会重复创建,当应用类型为servlet时创建的是StandardServletEnvironment对象,否则创建StandardEnvironment对象。

    接着来看configureEnvironment(environment, applicationArguments.getSourceArgs())

        protected void configureEnvironment(ConfigurableEnvironment environment,
                String[] args) {
            configurePropertySources(environment, args);//加载启动命令行配置属性
            configureProfiles(environment, args);//设置active属性
        }
    

    configurePropertySources(environment, args)加载启动命令行的配置属性,来看一下实现

        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对象用于存储配置集合,其内部维护了一个CopyOnWriteArrayList类型的list对象,当默认配置存在时,会向该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()的参数设置到environment中,即spring.profiles.active对应的环境变量。

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

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

    这里的listeners就是之前通过META-INF/spring.factories注册的所有listeners,后面我们先以其中最重要的ConfigFileApplicationListener做为例子进行分析,接着来看listener.environmentPrepared(environment)

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

    可以看到这里创建了一个ApplicationEnvironmentPreparedEvent类型的事件,并且调用了multicastEvent方法,通过该方法最终会调用到listener的onApplicationEvent方法,触发事件监听器的执行。

    接下来具体看一下ConfigFileApplicationListeneronApplicationEvent方法做了什么

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

    可以看到当监听到ApplicationEnvironmentPreparedEvent类型的事件时,调用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自己也添加进去了。接着遍历list中所有对象,并执行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是一个内部类,用于处理配置文件的加载,首先看一下其构造方法

            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进行加载,那么来看看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));//对加载过的配置文件进行排序
                addLoadedPropertySources();
            }
    

    initializeProfiles()进行了profiles的初始化,默认会添加nulldefaultprofiles中,null对应配置文件application.properties和application.yml,default对应配置文件application-default.yml和application-default.properties,这里的null会被优先处理,由于后处理的会覆盖先处理的,因此其优先级最低。

    接着来看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()获取配置文件的路径,默认会获得4个路径

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

    接着会遍历这些路径,拼接配置文件名称,选择合适的yml或者properties解析器进行解析,最后将结果添加到environmentpropertySources中。

    1. 通过createApplicationContext()创建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);
        }
    

    可以看到这里也是根据webApplicationType的取值,分别创建不同的返回类型。

    1. 通过prepareContext(context, environment, listeners, applicationArguments,printedBanner)方法将listenersenvironmentapplicationArgumentsprintedBanner等重要组件与上下文对象context进行关联
        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);//发布容器已加载事件。
        }
    

    这里的sources装的就是我们的启动类,然后通过load(context, sources.toArray(new Object[0]))方法进行加载

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

    经过一系列调用之后最终由load(Class<?> source)方法执行,这里比较有趣的是当Groovy存在时居然是优先调用Groovy的方式进行加载,否则才走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()方法相当重要,尤其是invokeBeanFactoryPostProcessors(beanFactory),这是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键部分,后续再详细讲解。

    总结

    至此Springboot的启动流程已经大体分析完了,也了解了配置文件和启动类分别是是如何被加载的,但仍有两个问题待解,一是Springboot的核心思想约定大于配置是如何做到的,二是Springboot的各种spring-boot-starter-*是如何发挥作用的,这两个问题留待后续文章继续分析。

    相关文章

      网友评论

          本文标题:Springboot初始化流程解析

          本文链接:https://www.haomeiwen.com/subject/ucqeuctx.html