springboot启动原理自动配置原理

104

@[toc]

1. SpringBoot自动加载原理

@SpringBootApplication
public class Application {

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

}

首先看@SpringBootApplication注解

@Target({ElementType.TYPE}) // 标记在类上
@Retention(RetentionPolicy.RUNTIME) // 作用于运行时
@Documented // 生成javadoc文档
@Inherited // 允许被继承
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
  // ...
}

其中包含了@SpringBootConfiguration和@EnableAutoConfiguration重要注解

@SpringBootConfiguration

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

该注解实际上起的还是@Configuration的作用,指定配置类

@EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

EnableAutoConfiguration中有两个注解,分别是

  • @AutoConfigurationPackage
  • @Import({AutoConfigurationImportSelector.class})

@AutoConfigurationPackage

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

这个注解的关键是@Import({Registrar.class}),@Import的作用是让Spring加载Registrar.class作为bean,那么看看Registrar中有什么

Registrar.class
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            register(registry, new PackageImport(metadata).getPackageName());
        }

        @Override
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImport(metadata));
        }

    }

@Import加载的类如果实现了ImportBeanDefinitionRegistrar接口,那么就会调用该接口的方法并注册成bean,再看一下该方法做了什么

register(registry, new PackageImport(metadata).getPackageName());

在这段代码上打断点然后debug项目,可以看到getPackageName()最终返回的是整个项目启动类所在的包,即该类的作用就是扫描包下面的组件注册成bean

@Import({AutoConfigurationImportSelector.class})

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered{
      // ...
    }

@Import导入的类如果实现了DeferredImportSelector或ImportSelector接口,则会执行selectImports方法,看一下selectImports方法的内容

@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        // 跟到这一段里面去
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

这里重点看AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);这段代码,跟到方法里面去

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 这里是重点
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

在跟到getCandidateConfigurations方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

此处有SpringFactoriesLoader.loadFactoryNames,这个方法比较关键了,跟进去

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgPO2OOS-1646748601337)(/Users/zhangxing/Library/Application Support/typora-user-images/image-20220228230648913.png)]

可以看到自动配置实际上会加载jar包中的spring.factories文件

所以自动配置实际上分2种情况

  1. 启动类同包下以及子包下的所有组件都会进行扫描并按需加载(如@ConditionXXXX),通过@ComponentScan可以修改扫描位置
  2. 读取resource目录下的META-INF/spring.factories文件中配置的类型

2.SpringApplication.run()

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

这一句代码包含两步骤

  1. 创建SpringApplication的实例
  2. 使用实例调用run方法

创建SpringApplication实例

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 推断web项目使用的mvc还是webflux,通过判断dispatcher的全限定名属于reactive包还是servlet包
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // bootstrap层的初始化,这里我们知道配置问价加载顺序是bootstrap>application,所以此处先初始化bootstrap
        this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
    // 初始化ApplicationContextInitializer,从spring.factories中加载
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 初始化ApplicationListener,从spring.factories中加载
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 通过栈 判断调用main方法的class
        this.mainApplicationClass = deduceMainApplicationClass();
    }

在这个构造方法中,由于容器还没有进行refresh方法(bean的初始化方法),所以可以通过实现ApplicationContextInitializer类来修改配置等上下文信息

run方法

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        ConfigurableApplicationContext context = null;
        configureHeadlessProperty();
  // 获取SpringApplicationRunListener类型的启动监听器并实例化,从spring.factories中获取
        SpringApplicationRunListeners listeners = getRunListeners(args);
  // 发送启动事件
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
      // 准备环境,包括了环境变量、java环境、spring环境、配置文件配置
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            configureIgnoreBeanInfo(environment);
      // 打印banner
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
      // 准备上下文
            prepareContext(bootstrapContext, 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, listeners);
            throw new IllegalStateException(ex);
        }

        try {
      // 发送run事件
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
  1. 获取并启动监听器

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

    这里实际上找的是SpringApplicationRunListener类型的监听器,用于监听应用的启用

    监听器实际上最终使用的是spring的事件机制实现

  2. 构造应用上下文环境
    包括系统环境变量、java环境、spring环境、spring配置文件

  3. 初始化应用上下文
    通过SpringApplication实例化的时候对webType进行的推断,当webType为MVC类型的时候,创建的上下文实际上是AnnotationConfigServletWebServerApplicationContext
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OQiSdlJh-1646748601338)(/Users/zhangxing/Library/Application Support/typora-user-images/image-20220301213421653.png)]
    AnnotationConfigServletWebServerApplicationContext实现了BeanFactory,具有IOC容器能力,其次这个类也是GenericApplicationContext的子类,GenericApplicationContext具备上下文能力,他使用一个BeanFactory字段保存了IOC容器,所以说,AnnotationConfigServletWebServerApplicationContext是上下文,并且持有了容器,对容器能力可以进行扩展

  4. 刷新应用上下文前的准备阶段

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
     // 设置环境参数
        context.setEnvironment(environment);
     // 上下文创建好的后置处理
        postProcessApplicationContext(context);
     // springapplication初始化时找到的initializers做调用
        applyInitializers(context);
        listeners.contextPrepared(context);
        bootstrapContext.close(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // 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);
    }
    

    上下文对象创建出来之后,给上下文设置环境参数,以及调用之前找到的initializer做调用,跟入load方法

    protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
     // 创建bean定义的加载器,跟进去看如何创建的
        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);
        }
     // bean定义加载器执行加载
        loader.load();
    }
    
    ```java
    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);
     // xml读取器
        this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null);
        this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null);
     // 包扫描读取器
        this.scanner = new ClassPathBeanDefinitionScanner(registry);
     // 排除bean过滤器
        this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
    }
    

    说明会通过注解方式、包扫描方式和xml方式读取哪些地方配置了需要交给spring管理的bean,最终执行.load()方法,将这三种读取方式分别执行,拿到所有的bean定义,这里只是读取到这个项目一共有哪些bean定义,然后给保存起来,并不是说这些bean定义一定都要载入到上下文中去,至于为什么在瞎main的刷新上下文中讲解

    BeanDefinition其实就是bean的基本信息,比如这个bean是什么class类型的,bean是否是懒加载,这个bean是否是primary修饰的,这个bean是单例还是property(每次都创新对象)模式等等信息…

  5. 刷新应用上下文

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    
            // Prepare this context for refreshing.
         // 刷新前的准备,主要校验了环境参数是否完整,比如某些必填配置没有写
            prepareRefresh();
    
            // Tell the subclass to refresh the internal bean factory.
         // 告诉子类刷新内部的beanFactory,其实里面没做啥事,主要就是刷新BeanFactory,获取BeanFactory,返回BeanFactory
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            // 准备beanFactory,添加spel表达式的解析器、设置后置处理器等设置一堆东西
            prepareBeanFactory(beanFactory);
    
            try {
                // Allows post-processing of the bean factory in context subclasses.
           // 添加bean的后置处理器,bean的后置处理器执行时机是beanDefinition创建之后,bean实例化之前,这样就拥有修改beanDefinition信息的能力
                postProcessBeanFactory(beanFactory);
    
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                // Invoke factory processors registered as beans in the context.
           // 这里是springboot自动加载的入口,通过主类转换成的BeanDefinition,拿到basePackage,然后找到basePackage下的所有@Component,然后将beanDefinition加入到容器中
                invokeBeanFactoryPostProcessors(beanFactory);
    
                // Register bean processors that intercept bean creation.
    
                registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
    
                // 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();
                contextRefresh.end();
            }
        }
    }
    
  6. 刷新应用上下文后的扩展接口
    方便扩展