美文网首页
springboot源码分析之SpringApplication

springboot源码分析之SpringApplication

作者: 会上树的程序猿 | 来源:发表于2019-09-26 08:22 被阅读0次

    想必大家对springbootApplication很熟悉,它是我们springboot的项目的一个入口,来看一段大家都熟知的代码:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class AppApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(AppApplication.class, args);
    }
    
    }
    

    上面这是我创建的一个springboot项目,这段代码就是我们项目的入口,看似一段很简单的代码,其真正的作用在于注解SpringBootApplication身上,既然我们知道了入口,那么我们来看看:

    • 注解SpringBootApplication在此处首先表明是一个springboot应用,通过该注解可以开启自动装配的功能.
    • SpringApplication.run(...)是用来启动该应用.

    所以接下来我们来看看springboot项目的启动的过程,关于注解SpringBootApplication我们专门来说.

    SpringApplgication

    SpringApplgication位于org.springframework.boot.SpringApplication包下,是spring应用的启动类,来看一下官方是如何说的:

    Class that can be used to bootstrap and launch a Spring application from a Java main method. By default class will perform the following steps to bootstrap your application
    

    在实际的开发中,我们应该用到的是它的static方法,来看代码:

    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);
    }
    public static void main(String[] args) throws Exception {
        SpringApplication.run(new Class<?>[0], args);
    }
    
    • 简单的三个静态方法,我们可以从中发现,第二个和第三个静态方法最后还是调用第一个静态方法
    • 首先是new SpringApplication对象.
    • 接着是调用SpringApplication#run(Class<?> primarySource, String... args)方法,真正的运行我们的spring应用

    既然知道了上面代码的执行顺序,我们进行一步一步的学习,首先我们来看构造SpringApplication的过程,代码如下:

    //用来保存javaConfig类的数组
    private Set<Class<?>> primarySources;
    //我们启动类的类型
    private Class<?> mainApplicationClass;
    //资源加载器
    private ResourceLoader resourceLoader;
    //针对于web类型的应用
    private WebApplicationType webApplicationType;
    //用来保存ApplicationContextInitializer类型的集合
    private List<ApplicationContextInitializer<?>> initializers;
    //用来保存ApplicationListener的集合
    private List<ApplicationListener<?>> listeners;
    
    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 = WebApplicationType.deduceFromClasspath();
        //初始化ApplicationContextInitializer过程
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //初始化ApplicationListener过程
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    简单的来看下我们上面提到的这些属性:

    • primarySources:是用来保存javaConfig类的,也就是我们本篇开头示例中的AppApplication类.
    • mainApplicationClass是通过调用 #deduceMainApplicationClass() 方法,来判断我们调用的是哪个main方法.
    • resourceLoader也就是我们的资源加载器,我们在之前的spring文章中的spring容器实现之资源加载我们详细的讲解了加载资源的过程,感兴趣的可以去看看.
    • webApplicationType其主要的作用是通过调用WebApplicationType#deduceFromClasspath()方法来判断我们的web类型.
    • initializers 是ApplicationContextInitializer集合,详解后面来说.
    • listeners是用来ApplicationListener的集合,详解见后面.
    deduceMainApplicationClass()
    private Class<?> deduceMainApplicationClass() {
        try {
            //获取当前StackTraceElement类型的数组
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            //遍历处理
            for (StackTraceElement stackTraceElement : stackTrace) {
                //判断哪个执行了main方法
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }
    

    该方法是我们属性mainApplicationClass的作用,位于SpringApplication.java中.

    getSpringFactoriesInstances()

    该方法的是啥哈,主要是针对于上面的属性initializers和listeners来实现的,不同的是一个获取ApplicationContextInitializer的对象集合,一个是ApplicationListener的对象集合,我们来看实现过程:

    SpringApplication.java
    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 classLoader = getClassLoader();
        // Use names and ensure unique to protect against duplicates
        //加载指定的类型的类名集合,从META-INF/spring.factories下加载
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //创建实例对象
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        //给创建的实例对象排序
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    

    几行代码,却做了很多事,简单的来看一下:

    • 首先是调用#getClassLoader()来获取类加载器.
    • 然后是调用SpringFactoriesLoader#loadFactoryNames(...)方法去加载指定了类型的类名集合,在META-INF/spring.factories路径下去加载.
    • 接着是通过#createSpringFactoriesInstances(...)来进行实例的创建过程
    • 最后对刚创建的实例们进行排序

    这就是上面代码的为我们所做的事,其中在第二步中,可能我们对META-INF/spring.factories路径下的东东不太明白,其实就是以key-value的形式存在,关于具体的加载过程,后续我们来说,我们来看看创建实例的过程:

    // SpringApplication.java
    
    /**
     * 创建对象的集合
     *
     * @param type 父类
     * @param parameterTypes 构造方法的参数类型
     * @param classLoader 类加载器
     * @param args 参数
     * @param names 类名的集合
     * @param <T> 泛型
     * @return 对象的数组
     */
    private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
            ClassLoader classLoader, Object[] args, Set<String> names) {
        //通过names集合的大小构建一个新的同样大小的集合
        List<T> instances = new ArrayList<>(names.size());
        //遍历处理
        for (String name : names) {
            try {
                //利用反射机制,获取name对应的具体类
                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;
    }
    

    上述创建实例的过程 其实也并不复杂,还是利用反射的机制来完成实例的创建过程,看完了第一个核心过程,我们来看看第二个过程就是调用方法#run(...),方法明确表示就是用来启动我们的spring的运用的,接下来我们来看看具体的实现过程:

    run方法
    #SpringApplication.java
    public ConfigurableApplicationContext run(String... args) {
        //1.创建StopWatch实例
        //调用它的start方法启动StopWatch来计时run方法的执行cd
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //2.配置Headless属性
        configureHeadlessProperty();
        //通过getRunListeners来获取所有的SpringApplicationRunListeners实例,同时开启监听操作
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            //3.通过参数args来创建ApplicationArguments对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //4.预加载所有的配置信息,包括environment
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    
            configureIgnoreBeanInfo(environment);
            //5.打印Banner图样
            Banner printedBanner = printBanner(environment);
            //6.创建上下文
            context = createApplicationContext();
            //7.创建异常报告器
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            //8.初始化上下文(通过调用别的方法)前的准备
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //9.执行初始化操作
            refreshContext(context);
            //10.后置处理的操作,默认是空实现
            afterRefresh(context, applicationArguments);
            //11.停止执行的运行时间
            stopWatch.stop();
            //12.springboot的启动日志打印过程
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            //13.通知相应的监听器spring容器启动完成
            listeners.started(context);
            //14.调用 ApplicationRunner或CommandLineRunner的运行方法
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            //若在此过程中发生异常,直接抛IllegalStateException
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
    
        try {
            //15.再次通知相应的监听器spring容器处于运行的状态
            listeners.running(context);
        }
        catch (Throwable ex) {
            //如果在此过程中发生异常,直接抛IllegalStateException
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }
    

    额,代码有点长,我们简单的来看一下每一步的过程:

    • 在1.处,首先是创建StopWatch实例同时调用start方法启动它,其目的是用来监控run方法运行的时长.
    • 在2.处,通过调用#configureHeadlessProperty()方法来配置Headless属性
    private void configureHeadlessProperty() {
        System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
                System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
    }
    

    好像该属性和AWT有关,感觉对我们来说作用不大,哈哈哈...,不是很清楚.

    • 在2.1.处,通过调用#getRunListeners()方法来获取SpringApplicationRunListeners集合,同时开启监听操作,代码如下:
    //SpringApplication.java
    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        return new SpringApplicationRunListeners(logger,
                getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }
    

    我们可以通过Dbug发现我们此时的listeners数组里的变量,如图所示:

    方法.png

    listeners中仅有一个变量实例就是我们图中的SpringApplicationRunListener类型.关于它后面来说,这里知道即可.

    • 在3.处通过我们传入的参数args来构建ApplicationArguments对象,作为下一步的必要参数.
    • 在4.处通过调用#prepareEnvironment(...)来预加载所有的配置信息,包括environment.
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // Create and configure the environment
        //1.通过调用#getOrCreateEnvironment()来创建ConfigurableEnvironment实例
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        //2.进行相关的配置
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        //3.设置environment的属性源
        ConfigurationPropertySources.attach(environment);
        //4.通知SpringApplicationRunListener当前环境已就绪
        listeners.environmentPrepared(environment);
        //5.将environment绑定到SpringApplication上
        bindToSpringApplication(environment);
        //6.非自定义的环境,通过条件去转换
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
        //7.将environment添加到PropertySource中
        ConfigurationPropertySources.attach(environment);
        return environment;
    }
    

    好像代码不是很多,逻辑挺多的,简单的来看一下:

    • 在4.1.处,主要是构建ConfigurableEnvironment对象的过程,来看看如何构建的
    //SpringApplication.java
    private ConfigurableEnvironment environment;
    //针对于web类型的应用
    private WebApplicationType webApplicationType;
    private ConfigurableEnvironment getOrCreateEnvironment() {
        //如果有,直接返回即可.
        if (this.environment != null) {
            return this.environment;
        }
        //不存在的话,通过webApplicationType来创建
        switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }
    

    可以看到的是:

    • 如果当前存在environment的话,直接用它
    • 在不存在的情况下,通过webApplicationType来创建不同类型的ConfigurableEnvironment对象
    • 在4.2.处,通过#configureEnvironment(...)方法进行环境的配置过程.
    //是否添加共享的ConversionService 
      private boolean addConversionService = true;
    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        //设置environment的ConversionService属性
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService.getSharedInstance();
            environment.setConversionService((ConfigurableConversionService) conversionService);
        }
        //1.1.添加environment的PropertySource属性源
        configurePropertySources(environment, args);
        //1.2.配置environment的activeProfiles属性
        configureProfiles(environment, args);
    }
    

    该方法其主要的作用是设置environment的一些属性其中包括:

    • 在4.1.1.处,通过方法#configurePropertySources(...)来设置environment的 PropertySource属性
    //SpringApplication.java
    /**jvm参数*/
    private boolean addCommandLineProperties = true;
    
    protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
        //获取Mu对象tablePropertySources
        MutablePropertySources sources = environment.getPropertySources();
        //设置默认的PropertySources
        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));
            }
        }
    }
    

    从代码中可以看到的是作为PropertySources参数的分别有jvm参数以及默认的propertySources.

    • 在4.1.2处通过方法#configureProfiles(...)来设置切换属性
    private Set<String> additionalProfiles = new HashSet<>();
    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()));
        //设置ActiveProfiles
        environment.setActiveProfiles(StringUtils.toStringArray(profiles));
    }
    

    代码简单,这里涉及到了springboot是如何进行Profiles的切换过程,后续来说.

    • 在4.3处,主要是添加属性源的过程.
    //ConfigurationPropertySource.java
    private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
    public static void attach(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
        PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
        if (attached != null && attached.getSource() != sources) {
            sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
            attached = null;
        }
        if (attached == null) {
            sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
                    new SpringConfigurationPropertySources(sources)));
        }
    }
    

    从代码中我们可以看到的通过获取到的attached来区分,存在的话从sources中移除,不存在的话,加入默认的即可,这里不多说了,接着看:

    • 在4.4处通过方法#environmentPrepared(...)来通知SpringApplicationRunListener,其主要的目的是告诉它当前环境已经准备就绪.

    • 在4.5.处,通过方法#bindToSpringApplication(...)将当前environment绑定在当前SpringApplication上.

    • 在4.6.处,如果非自定义 environment ,则根据条件转换.默认情况下isCustomEnvironment 为 false ,所以会执行这块逻辑。但是,一般情况下,返回的还是 environment 自身,所以可以无视这块逻辑先.

    • 在4.7.处.跟4.3处一样,这里就不多扯了.

    • 在5处,通过调用#printBanner(...)来打印springboot启动的Banner图样,相信这个大家都知道,就不多扯了...

    • 在6.处,通过方法#createApplicationContext()创建spring容器.后面来讲

    • 在7.处,通过 #getSpringFactoriesInstances(Class<T> type) 方法,进行获取SpringBootExceptionReporter 类型的对象数组,SpringBootExceptionReporter ,记录启动过程中的异常信息.

    通过dbug我们发现,在此处的exceptionReporters数组中有一个变量,如图:

    微信截图_20190829214623.png
    • 在8处,通过调用#prepareContext(...)方法来初始化上下文环境,这里主要是通过调用别动初始化方法来完成的,详解见后面.
    • 在9处,调用#refreshContext(ConfigurableApplicationContext context)方法,启动(刷新) Spring 容器,详解见后面.
    • 在10处,调用#afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) 方法执,行 Spring 容器的初始化的后置处理,默认实现为空,代码如下:
    protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
    }
    
    • 在11处,通过调用stopWatch#stop()来停止统计run方法的执行时长.
    • 在12处,打印 Spring Boot 启动时的时长日志,如下图所示:
    微信截图_20190829221253.png
    • 在13处,调用SpringApplicationRunListeners#started(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的集合,Spring 容器启动完成,关于该方法的详解后面来说.
    • 在14.处,首先是调用# ApplicationRunner或CommandLineRunner的运行方法,若在此过程中发生异常,直接抛IllegalStateException
    • 在15处,通过SpringApplicationRunListeners#running(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器运行中,如果在此过程中发生异常,直接抛IllegalStateException

    接下来我们分别来看看上述我们遗留的一些方法的详解,首先我们来看看6处遗留的创建spring容器的过程来看是如何实现的.

    createApplicationContext()方法
    //SpringApplication.java
    private Class<? extends ConfigurableApplicationContext> applicationContextClass;
    /**
     * The class name of application context that will be used by default for web
     * environments.
     */
    public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
    /**
     * The class name of application context that will be used by default for reactive web
     * environments.
     */
    public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
    
    /**
     * The class name of application context that will be used by default for non-web
     * environments.
     */
    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
            + "annotation.AnnotationConfigApplicationContext";
    protected ConfigurableApplicationContext createApplicationContext() {
        //通过webApplicationType类型类来完成ApplicationContext的创建过程
        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);
            }
        }
        //这里表示contextClass不为null,则直接通过反射的方法去创建
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    

    从上述代码可以看出,创建spring容器的过程并不复杂,还是根据webApplicationType来判断进行创建不同的ApplicationContext对象,接着我们来看在8处遗留的通过方法#prepareContext(....)来初始化spring容器

    prepareContext()
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        //设置context的environment的环境
        context.setEnvironment(environment);
        //1.设置一些跟当前ApplicationContext有关的属性
        postProcessApplicationContext(context);
        //2.初始化ApplicationContextInitializer
        applyInitializers(context);
        //3.通知SpringApplicationRunListener的数组,Spring容器准备完成
        listeners.contextPrepared(context);
        //4.打印启动日志
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        //5.获取单例ConfigurableListableBeanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //将applicationArguments注册到beanFactory中
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // Load the sources
        //6.加载资源文件主要是(beanDefinitions)
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        //7.通知 SpringApplicationRunListener的数组,Spring容器加载完成
        listeners.contextLoaded(context);
    }
    

    我们可以看到的是该方法主要的作用是针对applicationContext对象的属性做了一些初始化的操作.我们简单的来看下过程:

    • 首先是对environment的设置
    • 在1处,通过调用#postProcessApplicationContext(ConfigurableApplicationContext context)方法,设置一些跟当前ApplicationContext有关的属性,代码如下:
    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
        if (this.beanNameGenerator != null) {
            context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                    this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
            }
            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
            }
        }
        if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
        }
    }
    
    • 在2处,通过调用 #applyInitializers(ConfigurableApplicationContext context) 方法,初始化 ApplicationContextInitializer,代码如下:
    /**
     * Apply any {@link ApplicationContextInitializer}s to the context before it is
     * refreshed.
     * @param context the configured ApplicationContext (not refreshed yet)
     * @see ConfigurableApplicationContext#refresh()
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected void applyInitializers(ConfigurableApplicationContext context) {
        //遍历处理ApplicationContextInitializer数组
        for (ApplicationContextInitializer initializer : getInitializers()) {
            //非空的泛型校验
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                    ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            //初始化context
            initializer.initialize(context);
        }
    }
    
    • 在3处,通过SpringApplicationRunListeners#contextPrepared(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器准备完成
    • 在4处,打印springboot启动时的日志
    • 在5处,只要是获取单例的ConfigurableListableBeanFactory实例并设置相关属性
    • 在6处,主要是加载资源文件(beanDefinition),来看代码:
    protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        //<1>创建BeanDefinitionLoader实例
        BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            //2.设置loader的BeanNameGenerator属性
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            //设置loader的ResourceLoader属性
            loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
            //设置loader的Environment属性
            loader.setEnvironment(this.environment);
        }
        //<3>执行加载操作
        loader.load();
    }
    

    我们可以看到的是在<1>处,首先是通过方法#getBeanDefinitionRegistry(...)来构建BeanDefinitionRegistry对象,我们来看代码:

    private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
        if (context instanceof BeanDefinitionRegistry) {
            return (BeanDefinitionRegistry) context;
        }
        if (context instanceof AbstractApplicationContext) {
            return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
        }
        throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
    }
    

    代码简单就不多扯了,接着看:

    • 同样是在<1>处,等创建完BeanDefinitionRegistry对象时,作为构建BeanDefinitionLoader对象的参数进行创建过程,其中调用的是#createBeanDefinitionLoader(...)方法,额,大家都懂,由于篇幅太长我们后续来说吧.
    • 在<2>处,通过条件的判断对BeanDefinitionLoader对象分别进行属性BeanNameGenerator 和ResourceLoader以及Environment的设置过程.
    • 在<3>处才是执行加载的过程,感兴趣的可以去看看,到这里我就为止了...
    • 在7处,调用 SpringApplicationRunListeners#contextLoaded(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器加载完成

    接着我们来看9处遗留的关于方法#refreshContext(...)启动spring容器的过程,直接看代码:

    //是否开启对ShutdownHook的注册
    private boolean registerShutdownHook = true;
    private void refreshContext(ConfigurableApplicationContext context) {
        //1.开启spring容器
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                //2.向context中注册ShutdownHook
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }
    

    代码结构清晰简单,其主要的逻辑处理还是通过调用别的方法来完成,简单的来看一下:

    • 在1处,通过调用#refresh(ApplicationContext applicationContext)方法来开启spring容器.代码如下:
    /**
     * Refresh the underlying {@link ApplicationContext}.
     * @param applicationContext the application context to refresh
     */
    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext) applicationContext).refresh();
    }
    
    • 在2.处,调用 ConfigurableApplicationContext#registerShutdownHook() 方法,注册 ShutdownHook ,该类主要用于 Spring 应用的关闭时,销毁相应的 Bean 们

    看完了该方法的作用我们来看看在14.处我们遗留的callRunners(...)的详解.

    callRunners

    该方法主要的作用是通过调用ApplicationRunners或者是CommandLineRunner方法,我们来看看代码:

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        //<1>.用来保存所有的runners
        List<Object> runners = new ArrayList<>();
        //<1.1>将从context中获取到的ApplicationRunner的bean保存到runners中
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        //<1.2>将从context中获取到的CommandLineRunner的bean保存到runners中
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        //<1.3>对runners进行排序
        AnnotationAwareOrderComparator.sort(runners);
        //<1.4>遍历进行执行相应的逻辑
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
    

    方法简单明了,我们简单的总结一下:

    • 在<1>处,创建一个用来保存runners类型bean的数组.
    • 在<1.4>处,遍历runners数组,执行逻辑.我们来看代码:
    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接口,这里就不多说了.

    SpringApplicationRunListeners

    该类位于org.springframework.boot.SpringApplicationRunListeners包下,是对SpringApplicationRunListener的封装,我们来看代码:

    ''''''
    /**
     * A collection of {@link SpringApplicationRunListener}.
     *
     * @author Phillip Webb
     */
    class SpringApplicationRunListeners {
    
    private final Log log;
    
    private final List<SpringApplicationRunListener> listeners;
    
    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        this.listeners = new ArrayList<>(listeners);
    }
    
    public void starting() {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.starting();
        }
    }
    
    public void environmentPrepared(ConfigurableEnvironment environment) {
    
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.environmentPrepared(environment);
        }
    }
    
    public void contextPrepared(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextPrepared(context);
        }
    }
    
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextLoaded(context);
        }
    }
    
    public void started(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.started(context);
        }
    }
    
    public void running(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.running(context);
        }
    }
    
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        for (SpringApplicationRunListener listener : this.listeners) {
            callFailedListener(listener, context, exception);
        }
    }
    
    private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
            Throwable exception) {
        try {
            listener.failed(context, exception);
        }
        catch (Throwable ex) {
            if (exception == null) {
                ReflectionUtils.rethrowRuntimeException(ex);
            }
            if (this.log.isDebugEnabled()) {
                this.log.error("Error handling failed", ex);
            }
            else {
                String message = ex.getMessage();
                message = (message != null) ? message : "no error message";
                this.log.warn("Error handling failed (" + message + ")");
            }
        }
    }
    

    我们来看一下该接口方法的定义:

    ''''
    public interface SpringApplicationRunListener {
    
    /**
     * Called immediately when the run method has first started. Can be used for very
     * early initialization.
     */
    void starting();
    
    /**
     * Called once the environment has been prepared, but before the
     * {@link ApplicationContext} has been created.
     * @param environment the environment
     */
    void environmentPrepared(ConfigurableEnvironment environment);
    
    /**
     * Called once the {@link ApplicationContext} has been created and prepared, but
     * before sources have been loaded.
     * @param context the application context
     */
    void contextPrepared(ConfigurableApplicationContext context);
    
    /**
     * Called once the application context has been loaded but before it has been
     * refreshed.
     * @param context the application context
     */
    void contextLoaded(ConfigurableApplicationContext context);
    
    /**
     * The context has been refreshed and the application has started but
     * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
     * ApplicationRunners} have not been called.
     * @param context the application context.
     * @since 2.0.0
     */
    void started(ConfigurableApplicationContext context);
    
    /**
     * Called immediately before the run method finishes, when the application context has
     * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
     * {@link ApplicationRunner ApplicationRunners} have been called.
     * @param context the application context.
     * @since 2.0.0
     */
    void running(ConfigurableApplicationContext context);
    
    /**
     * Called when a failure occurs when running the application.
     * @param context the application context or {@code null} if a failure occurred before
     * the context was created
     * @param exception the failure
     * @since 2.0.0
     */
    void failed(ConfigurableApplicationContext context, Throwable exception);
    

    }

    该接口里面的方法都有详细的解释,可以自己去看看,我们发现该接口唯一的实现类是EventPublishingRunListener

    EventPublishingRunListener类
    public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    
    /**
     * 当前的spring应用
     */
    private final SpringApplication application;
    /***
     * 保存所有的参数的集合
     */
    private final String[] args;
    /**
     * 事件广播器
     */
    private final SimpleApplicationEventMulticaster initialMulticaster;
    
    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
    
    @Override
    public void starting() {
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }
    
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        this.initialMulticaster
                .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
    }
    
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        this.initialMulticaster
                .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
    }
    
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (ApplicationListener<?> listener : this.application.getListeners()) {
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
    }
    
    @Override
    public void started(ConfigurableApplicationContext context) {
        context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
    }
    
    @Override
    public void running(ConfigurableApplicationContext context) {
        context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
    }
    
    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
        if (context != null && context.isActive()) {
            // Listeners have been registered to the application context so we should
            // use it at this point if we can
            context.publishEvent(event);
        }
        else {
            // An inactive context may not have a multicaster so we use our multicaster to
            // call all of the context's listeners instead
            if (context instanceof AbstractApplicationContext) {
                for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                        .getApplicationListeners()) {
                    this.initialMulticaster.addApplicationListener(listener);
                }
            }
            this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
            this.initialMulticaster.multicastEvent(event);
        }
    }
    
    private static class LoggingErrorHandler implements ErrorHandler {
    
        private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);
    
        @Override
        public void handleError(Throwable throwable) {
            logger.warn("Error calling ApplicationEventListener", throwable);
        }
    
    }
    

    代码简单,可以自己看看,这里我们可以将EventPublishingRunListener认为是springApplication的一个事件转换器,关于它的详解我们后面来看....

    相关文章

      网友评论

          本文标题:springboot源码分析之SpringApplication

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