美文网首页
springboot启动分析

springboot启动分析

作者: cyjd | 来源:发表于2019-06-19 16:51 被阅读0次

那么启动过程分为两步分析

  • 创建SpringApplication实例过程
  • 实例run()方法执行过程

入口在springboot启动类

@SpringBootApplication
public class CloudApplication {

    public static void main(String[] args) {
        //调用SpringApplication类静态run方法
        SpringApplication.run(CloudApplication.class, args);
    }
}

跟踪代码

public static ConfigurableApplicationContext run(Class<?> primarySource,
            String... args) {
               //调用当前类另一个静态方法
        return run(new Class<?>[] { primarySource }, args);
    }

跟踪到另一个静态方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {
               //先创建实例,调用实例run()方法
        return new SpringApplication(primarySources).run(args);
    }

一.创建实例

创建实例主要做了四件事:

  • 推断应用类型
  • 类加载并实例化所有的ApplicationContextInitializer
  • 类加载并实例化所有的ApplicationListener
  • 推断springboot启动类是哪个
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
                //资源加载器
        this.resourceLoader = resourceLoader;
                //断言资源加载器非空
        Assert.notNull(primarySources, "PrimarySources must not be null");
                //设置源primarySources
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
                //推断应用类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
                //设置初始化器
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
                //设置监听器
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
                //推断启动类
        this.mainApplicationClass = deduceMainApplicationClass();
    }

1.1推断应用类型

其实就是根据,类路径下是否存在以下关键类判断返回哪个枚举值。后面的过程会根据应用类型创建对应类型的容器 。WebApplicationType是一个枚举类,有三个枚举值

  • NONE:按照非web应用运行并且不创建内置的web服务器
  • SERVLET:按照一个基于servlet的web应用运行,并且创建内置的web容器
  • REACTIVE:按照一个反应式的web应用运行,并肩创建一个反应式的web容器
static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }

ClassUtils.isPresent 方法,是判断类路径下是否存在类,如果存在就类加载并返回true,否则返回false。总结一下类型推断的过程其实就是:看类路径下是否存在应用对应的关键类,根据是否存在返回对应的枚举值。后面根据枚举值来创建对应类型的应用。

1.2类加载并实例化所有的ApplicationContextInitializer

调用getSpringFactoriesInstances(ApplicationContextInitializer.class)方法 ,作用是返回所有ApplicationContextInitializer类型的扩展类实例,主要分两步进行:

  • 先获取所有要加载类的全限定名
  • 根据全限定名实例化对象
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
//获取类全限定名
        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,这个方法是springboot加载扩展类实现扩展机制的关键,主要分两步:

  • 先获取当前类加载器对用的所有组件类型的所有类
  • 根据组件类型从第一步返回的map的获取对应组件类型的所有类
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

最终调用方法如下

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//从缓存中获取,ApplicationClassLoader对应的扩展类是否加载过了
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
//如果加载过了直接返回
            return result;
        }

        try {
//遍历所有Jar包 如果存在META-INF/spring.factories文件,就将文件加载为资源
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));           
//创建一个复合结构map<String,List<String>> key扩展类型全限定名,如:org.springframework.context.ApplicationContextInitializer,value为所有扩展实现类的全限定名集合
            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()) {
                    String factoryClassName = ((String) entry.getKey()).trim();
//遍历该扩展类型对应的属性值
                    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryClassName, factoryName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
  • 先从缓存中获取对应类加载器的全部要加载的类
  • 若果获取不到,就去读取jar包中的META-INF/spring.factories文件
  • 对每个文件遍历,将结果放到result中一个LinkedMultiValueMap,LinkedMultiValueMap的key为加载类型的全限定名,value为要加载类的集合
  • 将result放入到cache中,key为类加载器 ,value为result
  • 类加载器对应的全部类型组件的全部类的类名集合


    image.png

每次调用SpringFactoriesLoader.loadFactoryNames时先从缓存cache成员变量中获取,也就是说在应用启动过程中除第一次调用此方法都是从缓存返回的。

1.3设置监听器

原理与设置初始化器相同

1.4推断启动类

private Class<?> deduceMainApplicationClass() {
        try {
//得到方法调用栈
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
//遍历调用栈
            for (StackTraceElement stackTraceElement : stackTrace) {
//如果当前遍历到的方法名为main,那么当前方法所在类为启动类
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
        
        }
        return null;
    }

二.run方法执行过程

public ConfigurableApplicationContext run(String... args) {
//创建计时器,用于记录一段程序的执行时间
        StopWatch stopWatch = new StopWatch();
//开始计时
        stopWatch.start();
//spring上下文对象
        ConfigurableApplicationContext context = null;
//异常报告器
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置系统属性
        configureHeadlessProperty();
//使用扩展方式,获取SpringApplication对象run方法专用的监听器 (其实不是监听器,而是发布事件用的)
        SpringApplicationRunListeners listeners = getRunListeners(args);
//发布开始事件
        listeners.starting();
        try {
//获取参数,args为启动类main方法的入参
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
//准备spring应用运行时环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            configureIgnoreBeanInfo(environment);
//打印banner
            Banner printedBanner = printBanner(environment);
//创建spring应用上下文对象
            context = createApplicationContext();
//获取异常报告器
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
//准备spring应用上下文
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
//刷新spring应用上下文
            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;
    }

2.1获取SpringApplicationRunListeners

跟前边获取ApplicationContextInitializer,ApplicationListener的过程是一样的 ,通过SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法获取类名,然后实例化 。
主要说下这个类的作用:在springboot应用启动过程中发布事件

SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();

跟踪下starting()方法

public void starting() {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.starting();
        }
    }

此处其实只有一个SpringApplicationRunListener,实现类是EventPublishingRunListener,继续跟EventPublishingRunListener源码

public void starting() {
        this.initialMulticaster.multicastEvent(
                new ApplicationStartingEvent(this.application, this.args));
    }

initialMulticaster是spring事件派发器 ,在EventPublishingRunListener构造方法调用时被创建

public EventPublishingRunListener(SpringApplication application, String[] args) {
//获取到SpringApplication 
        this.application = application;
        this.args = args;
//创建事件派发器
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
//从SpringApplication获取所有注册在上面的监听器,将这些监听器注册到事件派发器上
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

前边,SpringApplication创建实例的时候通过扩展方式 从META-INF/spring.factories中获取的那些监听器就在这里使用。
EventPublishingRunListener其实是委托事件派发器去派发给监听器事件。

2.2准备spring应用运行时环境

private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        //创建应用环境对象
        ConfigurableEnvironment environment = getOrCreateEnvironment();
                 //配置应用环境对象
        configureEnvironment(environment, applicationArguments.getSourceArgs());
                //发布环境准备就绪事件
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

跟踪创建环境对象代码 ,根据之前推断的应用类型,创建的是StandardServletEnvironment类型的环境对象

private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }

跟踪配置环境对象代码

protected void configureEnvironment(ConfigurableEnvironment environment,
            String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService
                    .getSharedInstance();
            environment.setConversionService(
                    (ConfigurableConversionService) conversionService);
        }
                //配置property
        configurePropertySources(environment, args);
                //配置profiles
        configureProfiles(environment, args);
    }

spring环境对象分两部分 profiles和property
profiles是一组BeanDefination的逻辑区分 ,当一个profiles处于active状态,处于同一profiles的BeanDefination才会被注册到容器中
property包含: 属性文件(properties files),JVM系统属性,系统环境变量,JNDI,servlet上下文参数,临时属性对象等。

2.3创建spring应用上下文对象

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_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);
    }

根据之前的应用类型推断 ,创建一个AnnotationConfigServletWebServerApplicationContext类型的上下文对象

2.4

private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
//给上下文对象关联环境对象
        context.setEnvironment(environment);
//注册bean生成器和资源加载器
        postProcessApplicationContext(context);
//回调前面从扩展中获取的初始化器的初始化方法
        applyInitializers(context);
//发布容器上下文准备完毕事件
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // 添加两个特殊单例bean
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        //源只有当前移动类一个
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
//发布上下文加载完成事件
        listeners.contextLoaded(context);
    }

2.5刷新容器

调用父类的refresh()方法 ,也就是一般spring容器非boot框架启动时都要执行的那个refresh方法。太复杂了,就不展开了 。

最后

其实启动过程没什么新奇的东西,事件监听啊bean定义,bean注册等等,主要还是spring容器启动那一套 ,增加了一些springboot用到的特殊组件,另外一个需要注意的地方就是扩展方式从 META-INF/spring.factories中获取扩展类,springboot自动配置等很多功能都是这种实现方式

相关文章

网友评论

      本文标题:springboot启动分析

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