Spring boot 启动源码

1、main启动:

    @SpringBootApplication + SpringApplication.run(App.class,args)

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

spring boot分析@SpringBootApplication注解@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan

1.1、@SpringBootConfiguration定义当前为配置类

1.2、@ComponentScan指定扫描哪些范围

1.3、@EnableAutoConfiguration:@AutoConfigurationPackage + @Import(AutoConfigurationImportSelector.class)

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

    1.3.1、@AutoConfigurationPackage:@Import(AutoConfigurationPackages.Registrar.class)

            new PackageImports(metadata).getPackageNames()获取主程序包名,所以spring boot默认扫描主程序所在包下所有配置注入到Bean

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    //导入组件
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

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

    1.3.2、 @Import(AutoConfigurationImportSelector.class)

            1)、selectImports获取导入组件

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

            2)、getAutoConfigurationEntry获取所有需要组件

protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

            3)、getCandidateConfigurations获取所有要导入的候选组件

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = new ArrayList<>(
            SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
    ImportCandidates.load(AutoConfiguration.class, 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;
}

其中spring boot 2.7.3后已经不建议SpringFactoriesLoader.loadFactoryNames

            4)、SPI机制META-INF/spring/%s.imports中的组件

public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
    Assert.notNull(annotation, "'annotation' must not be null");
    ClassLoader classLoaderToUse = decideClassloader(classLoader);
    String location = String.format(LOCATION, annotation.getName());
    Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
    List<String> importCandidates = new ArrayList<>();
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        importCandidates.addAll(readCandidateConfigurations(url));
    }
    return new ImportCandidates(importCandidates);
}

        5)、EnableAutoConfiguration从META-INF/spring.factories移到org.springframework.boot.autoconfigure.AutoConfiguration.imports

分析 SpringApplication类,提供静态方法调用构造方法实现方法调用

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);
}
启动方法SpringApplication.run(App.class,args)可以写成new SpringApplication(App.class).run(args)
2、初始化
启动环境变量准备、资源构造器初始化
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 = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
            getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    //实例化META-INF/spring.factories 相应初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //实例化监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

2.1、setInitializers,实例化所有初始器:

    事件流程:

# 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.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

    2.1.1、扩展:增加自定义初始化器

    1)、新增自定义初始化类,实现ApplicationContextInitializer类,重写initialize方法

public class MyInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("自定义初始化执行器");
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        Map proMap = new HashMap<>();
        proMap.put("key","myinitializer");
        proMap.put("value","sk");
        environment.getPropertySources().addLast(new MapPropertySource("MyInitailizer",proMap));
        System.out.println("初始化结束,添加参数!");
    }
}

    2)、SPI配置resources/META-INF/spring.factories增加Initializers定义

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
com.sk.cloud.spring.init.initialner.MyInitializer

    3)、调试如下

    4)、调用链路:SpringApplication#run()->prepareContext(..)-->applyInitializers(context)-initialize(context)-->回调自定义初始化器

2.2、setListeners,实例化所有监听器

    监听机制:

    事件流程:

# 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.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

        2.2.1、扩展:增加自定义监视器(开始和结束监听器)

            1)、创建自定义监听器,实现

public class AppStartingListener implements ApplicationListener<ApplicationStartingEvent> {

    @Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        System.out.println("容器启动前");
    }
}


public class AppEndListener implements ApplicationListener<ApplicationStartedEvent> {

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        System.out.println("容器启动后");
    }
}

            2)、SPI配置resources/META-INF/spring.factories增加Listeners定义

# Application Listeners
org.springframework.context.ApplicationListener=\
com.sk.cloud.spring.init.listenter.AppEndListener,\
com.sk.cloud.spring.init.listenter.AppStartingListener

        3)、调试如下

        4)、监听器回调

                

                    1)、ApplicationStartingEvent事件回调:SpringApplication#run() --> listeners.starting(..)  -->回调

                    2)、ApplicationStartedEvent事件回调:SpringApplication#run() --> listeners.started(..) --> 回调

   2.3、prepareEnvironment:准备Environment环境对象

  url:Springboot配置文件加载原理及流程【源码分析】_springboot加载配置文件源码-CSDN博客

    2.4、上下文刷新

        1)、确定context类型

context = createApplicationContext();

public enum WebApplicationType {
    /**
     * The application should not run as a web application and should not start an
     * embedded web server.
     * 不启动内嵌的WebServer,不是运行web application
     */
    NONE,

    /**
     * The application should run as a servlet-based web application and should start an
     * embedded servlet web server.
     * 启动内嵌的基于servlet的web server
     */
    SERVLET,

    /**
     * The application should run as a reactive web application and should start an
     * embedded reactive web server.
     * 启动内嵌的reactive web server,这个application是一个reactive web application
     */
    REACTIVE;
}

        2)、prepareContext :预加载

        Bean注册到单例池 --> SPI机制初始化ApplicationContextInitializer列表 --> 广播ApplicationContextInitializedEvent事件 --> 打印启动容器(Starting App ...)--> load(启动类转化为BeanDefinition注册到spring容器的BeanDefinitionMap中) --> listeners.contextLoaded(context)遍历了SpringApplication对象所有的监听器(创建SpringApplication的时候,从META-INF/spring.factories中加载到的ApplicationListener),判断实现ApplicationContextAware接口添加到spring容器的监听列表。最后广播ApplicationPreparedEvent事件添加到spring容器中

    2.5、refreshContext

         refreshContext(..)-->AbstractApplicationContext#refresh

public void refresh() throws BeansException, IllegalStateException {    
               //上下文刷新前的准备工作:设置启动日期、context当前状态、初始化属性和环境
        prepareRefresh();
        //获取bean工厂类
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        //配置beanFactory一些特性,例如上下类加载器和后处理器
        prepareBeanFactory(beanFactory);
        try {        
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // 执行beanFactory后置处理器,可以自定义BeanFactoryPostProcessor修改Bean属性值
            //ConfigurationClassParser#doProcessConfigurationClass 解析所有beanDefinition(配置类、bean类)
            invokeBeanFactoryPostProcessors(beanFactory);

            //注册bean的PostProcessor,用于后续bean的创建和拦截:自定义BeanPostProcessor实现postProcessBeforeInitialization和postProcessAfterInitialization拦击Bean处理。例如AOP
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

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

            // 初始化广播器,用于发布事件
            initApplicationEventMulticaster();

            // 初始话context上下文特殊bean,例如tomcat容器
            onRefresh();

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

            // 实例化bean
            finishBeanFactoryInitialization(beanFactory);

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

            以ServletWebServerFactoryAutoConfiguration配置类为例,解释一下全局配置文件中的属性如何生效,比如:server.port=8081,是如何生效的(当然不配置也会有默认值,这个默认值来自于org.apache.catalina.startup.Tomcat)

在ServletWebServerFactoryAutoConfiguration类上,有一个@EnableConfigurationProperties注解:开启配置属性

server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。

Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。

    2.1.2、启动内置tomcat

        refreshContext(..)-->AbstractApplicationContext#refresh -->onRefresh() -->ServletWebServerApplicationContext#onRefresh-->createWebServer()-->getWebServerFactory()-->factory.getWebServer(..) -->getTomcatWebServer()-->initialize

    1)、getWebServerFactory()

    默认springboot使用tomacat容器

    自动装配获取:spring-boot-autoconfigure的SPI文件中(这里注意spring boot2.7官方不推荐使用spring.factories了,EnableAutoConfiguration采用 META-INF/spring/%s.imports)

    通过Import导入EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow

@AutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

    分析EmbeddedTomcat,条件装配存在Tomcat类装配TomcatServletWebServerFactory

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(

    pom文件只要依赖spring-boot-start-tomacat会将Tomcat类引入到项目中,满足EmbeddedTomcat条件装配条件,向容器注入TomcatServletWebServerFactory,springboot容器启动getWebServerFactory会获取TomcatServletWebServerFactory创建Tomcat web容器

    2)、Tomacat实现类:new Tocmat并完成了一些初始化配置 --> getTomcatWebServer(创建webserver实例)并启动tomcat容器(this.tomcat.start())

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    for (LifecycleListener listener : this.serverLifecycleListeners) {
        tomcat.getServer().addLifecycleListener(listener);
    }
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}
    2.6、 afterRefresh
    
2.7、Banner打印类

    1)、默认SpringBootBanner

    printBanner(env)->bannerPrinter.print->SpringBootBanner#printBanner

class SpringBootBanner implements Banner {
    private static final String[] BANNER = { "", "  .   ____          _            __ _ _",
            " /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
            " \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )", "  '  |____| .__|_| |_|_| |_\\__, | / / / /",
            " =========|_|==============|___/=/_/_/_/" };


    private static final String SPRING_BOOT = " :: Spring Boot :: ";
    private static final int STRAP_LINE_SIZE = 42;
    @Override
    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
        for (String line : BANNER) {
            printStream.println(line);
        }
        String version = SpringBootVersion.getVersion();
        version = (version != null) ? " (v" + version + ")" : "";
        StringBuilder padding = new StringBuilder();
        while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
            padding.append(" ");
        }
        printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
                AnsiStyle.FAINT, version));
        printStream.println();
    }
}

     2)、自定义bannner打印

    在Resources目录下新增banner.txt文件,自定义打印banner效果

    2.8、Runners运行器

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);
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
    try {
        (runner).run(args);
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
    }
}

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
    try {
        (runner).run(args.getSourceArgs());
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
    }
}

调用ApplicationRunner和CommandLineRunner类执行run方法(order相同ApplicationRunner优先级更高),一般需要在初始化完毕的时候执行,例如加载数据库的配置信息

    1)、自定义ApplicationRunner

@Component
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("my application runner: init dbserver application");
    }
}
3、Spring boot中的扩展机制
    1)、自定义初始化器,在refreh之前
    2)、事件监听器
    3)、Runner
    4)、自定义BeanFactoryPostProcessor用于修改beanFactory的属性值
    5)、bean的扩展 beanPostPrecessor拦截bean
    6)、aop扩展机制:前置 后置 环绕通知
    7)、Aware
  • 15
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值