Springboot启动原理

springboot的启动类入口

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

 @SpringBootApplication注解

@Target(ElementType.TYPE) // 注解的适用范围,其中TYPE用于描述类、接口(包括包注解类型)或enum声明
@Retention(RetentionPolicy.RUNTIME) // 注解的生命周期,保留到class文件中(三个生命周期)
@Documented // 表明这个注解应该被javadoc记录
@Inherited // 子类可以继承该注解
@SpringBootConfiguration // 继承了Configuration,表示当前是注解类
@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助
@ComponentScan(excludeFilters = { // 扫描路径设置
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
} 

其中比较重要的有三个注解,分别是:

  1)@SpringBootConfiguration // 继承了Configuration,表示当前是注解类,会被扫描并加载到IOC容器

  2)@EnableAutoConfiguration // 开启springboot的注解功能,springboot的四大神器之一,其借助@import的帮助

  3)@ComponentScan(excludeFilters = { // 扫描路径设置}),

//自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,将这些bean定义加载到IoC容器中。

   我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。

  注:所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages

@EnableAutoConfiguration

@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage【重点注解】
@Import(AutoConfigurationImportSelector.class)【重点注解】
public @interface EnableAutoConfiguration {
...
}

其中有两个重点注解,下面分别介绍:

@AutoConfigurationPackage注解

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Import(AutoConfigurationPackages.Registrar.class)

public @interface AutoConfigurationPackage {



}

通过@Import(AutoConfigurationPackages.Registrar.class)

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
 
        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata,
                BeanDefinitionRegistry registry) {
            register(registry, new PackageImport(metadata).getPackageName());
        }
 
        ……
 
    }

注册当前启动类的根package

注册org.springframework.boot.autoconfigure.AutoConfigurationPackagesBeanDefinition

@Import(AutoConfigurationImportSelector.class)注解

可以从图中看出  AutoConfigurationImportSelector 实现了 DeferredImportSelector 从 ImportSelector继承的方法:selectImports

该方法在springboot启动流程——bean实例化前被执行,返回要实例化的类信息列表;

如果获取到类信息,spring可以通过类加载器将类加载到jvm中。

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }

其中this.getCandidateConfigurations(annotationMetadata, attributes)方法调用了SpringFactoriesLoader#loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader),实际上是找到所有jar包以及项目中的META-INF/spring.factories文件,加载依赖包及项目中的bean进入IOC容器中。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
        ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

@EnableAutoConfiguration自动装配其实就变成了:

从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。

启动源码

入口:

SpringApplication.run(AppTenderApplicationExt.class);

总流程 

public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
//创建引导上下文(Context环境)
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    ConfigurableApplicationContext context = null;
//设置headless无头模式
    this.configureHeadlessProperty();
//获取所有运行监听器(为了方便所有Listener进行事件感知)
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
//遍历所有监听器,并且starting
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
//保存命令行中所有的参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境信息
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//配置需要忽略的环境信息
        this.configureIgnoreBeanInfo(environment);
//打印banner
        Banner printedBanner = this.printBanner(environment);
//创建IOC容器
        context = this.createApplicationContext();
//记录当前事件
        context.setApplicationStartup(this.applicationStartup);
//准备IOC容器信息(准备ApplicationContext)
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新IOC容器
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
//监听容器启动花费了多长时间
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
        }

//listener通知项目启动
        listeners.started(context, timeTakenToStartup);
//调用所有的runners
        this.callRunners(context, applicationArguments);
    } catch (Throwable var12) {
        this.handleRunFailure(context, var12, listeners);
        throw new IllegalStateException(var12);
    }

    try {
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        listeners.ready(context, timeTakenToReady);
        return context;
    } catch (Throwable var11) {
        this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var11);
    }
}

分步

1、 createBootstrapContext

private DefaultBootstrapContext createBootstrapContext() {
//首先创建DefaultBootstrapContext对象,保存信息。
        DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
//接着获取所有的initializer并执行initialize(bootstrapContext)),来完成对引导启动器的环境设置让当前应用进入headless模式
        this.bootstrapRegistryInitializers.forEach((initializer) -> {
            initializer.initialize(bootstrapContext);
        });
        return bootstrapContext;
    }

 2、configureHeadlessProperty

headless模式的意思就是明确Springboot要在无鼠键支持的环境中运行,一般程序也都跑在Linux之类的服务器上,无鼠键支持,这里默认值是true

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

3、getRunListeners

获取所有运行监听器(为了方便所有Listener进行事件感知)

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

4、listeners.starting

遍历所有监听器,并且starting。
doWithListeners中的foreach方法,相当于通知所有感兴趣(需要这个信息)系统正在启动的“人”,项目正在starting

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
        this.doWithListeners("spring.boot.application.starting", (listener) -> {
            listener.starting(bootstrapContext);
        }, (step) -> {
            if (mainApplicationClass != null) {
                step.tag("mainApplicationClass", mainApplicationClass.getName());
            }

        });
    }

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
        StartupStep step = this.applicationStartup.start(stepName);
        this.listeners.forEach(listenerAction);
        if (stepAction != null) {
            stepAction.accept(step);
        }

        step.end();
    }

5、prepareEnvironment

准备环境信息

在配置环境信息时:

ConfigurationPropertySources.attach(environment);

中有MutablePropertySources sources=environment.getPropertySources();能够获取@PropertySource,读取到外部的配置文件,也就是说此方法加载全系统中所有的配置信息!

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
//返回或创建基础环境信息对象
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
//通过命令行参数和环境信息配置环境
        this.configureEnvironment(environment, applicationArguments.getSourceArgs());
//绑定信息,也就是保存工作
        ConfigurationPropertySources.attach(environment);
 //listeners就是之前获取到的所有的RunListener,通知所有监听器当前环境准备完成
        listeners.environmentPrepared(bootstrapContext, environment);
        DefaultPropertiesPropertySource.moveToEnd(environment);
        Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
//绑定环境信息
        this.bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());
            environment = environmentConverter.convertEnvironmentIfNecessary(environment, this.deduceEnvironmentClass());
        }

        ConfigurationPropertySources.attach(environment);
        return environment;
    }

public static void attach(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
//能够获取@PropertySource,读取到外部的配置文件,也就是说此方法加载全系统中所有的配置信息!
        MutablePropertySources sources = ((ConfigurableEnvironment)environment).getPropertySources();
        PropertySource<?> attached = getAttached(sources);
        if (attached == null || !isUsingSources((PropertySource)attached, sources)) {
            attached = new ConfigurationPropertySourcesPropertySource("configurationProperties", new SpringConfigurationPropertySources(sources));
        }

        sources.remove("configurationProperties");
        sources.addFirst((PropertySource)attached);
    }

6、createApplicationContext

创建IOC容器

根据当前项目类型(Servlet、reactive、default)创建

protected ConfigurableApplicationContext createApplicationContext() {
        return this.applicationContextFactory.create(this.webApplicationType);
    }

7、prepareContext

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//保存基本环境信息
        context.setEnvironment(environment);
//IOC容器的后置处理流程,像注册一些组件,读取配置文件的资源,资源的加载器
        this.postProcessApplicationContext(context);
//应用初始化器,遍历所有的ApplicationContextInitializer,调用initialize(context);来对IOC容器进行初始化(扩展工作)
也就说之前保存的ApplicationContextInitializer在此时调用了。
        this.applyInitializers(context);
//通知所有的监听器IOC容器的上下文配置完毕
        listeners.contextPrepared(context);
        bootstrapContext.close(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

//拿到Bean工厂,注册单实例
(项目中的参数会作为组件注册进去)
banner也是IOC容器中的一个组件
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
//所有监听器调用contextLoaded,通知IOC容器已经加载完毕
        listeners.contextLoaded(context);
    }

8、refreshContext

private void refreshContext(ConfigurableApplicationContext context) {
        if (this.registerShutdownHook) {
            shutdownHook.registerApplicationContext(context);
        }

        this.refresh(context);
    }

9、callRunners

获取容器中的Runner分别是
ApplicationRunner
CommandLineRunner
合并所有Runner并且按照@Order(优先级)进行排序
遍历所有的Runner调用run方法
异常处理
调用Listener的failed方法

private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        Iterator var4 = (new LinkedHashSet(runners)).iterator();

        while(var4.hasNext()) {
            Object runner = var4.next();
            if (runner instanceof ApplicationRunner) {
                this.callRunner((ApplicationRunner)runner, args);
            }

            if (runner instanceof CommandLineRunner) {
                this.callRunner((CommandLineRunner)runner, args);
            }
        }

    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值