SpringBoot启动类SpringApplication源码

2020-05-25  本文已影响0人  每天进步一丢儿丢儿
创建一个SpringBoot应用启动类的两个关键点

下面针对以上两点对SpringApplication源码进行解读,首先来看SpringApplication的静态run方法

/**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified source using default settings.
     * @param primarySource the primary source to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }

    /**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified sources using default settings and user supplied arguments.
     * @param primarySources the primary sources to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

可以看出第一个静态run方法是对第二个静态run方法的调用,来看看第二个静态run方法,这个方法通过SpringApplication对象的构造方法创建了一个SpringApplication的对象实例,并调用了SpringApplication的实例方法run(args[])。那么SpringApplication构造做了那些工作呢?

/**
     * Create a new {@link SpringApplication} instance. The application context will load
     * beans from the specified primary sources (see {@link SpringApplication class-level}
     * documentation for details. The instance can be customized before calling
     * {@link #run(String...)}.
     * @param primarySources the primary bean sources
     * @see #run(Class, String[])
     * @see #SpringApplication(ResourceLoader, Class...)
     * @see #setSources(Set)
     */
    public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }

    /**
     * Create a new {@link SpringApplication} instance. The application context will load
     * beans from the specified primary sources (see {@link SpringApplication class-level}
     * documentation for details. The instance can be customized before calling
     * {@link #run(String...)}.
     * @param resourceLoader the resource loader to use
     * @param primarySources the primary bean sources
     * @see #run(Class, String[])
     * @see #setSources(Set)
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    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();
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

通过上面的代码可以看出SpringApplication主要做了一下几件事情:

下面重点关注ApplicationContextInitializer、ApplicationListener接口实现类的实例化过程。即对SpringApplicationget的SpringFactoriesInstances方法的调用。

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 = getClassLoader();
        // Use names and ensure unique to protect against duplicates
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

实例化的过程利用了java的反射机制,具体实现参考下面的代码。

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
            ClassLoader classLoader, Object[] args, Set<String> names) {
        List<T> instances = new ArrayList<>(names.size());
        for (String name : names) {
            try {
                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;
    }

这段代码就是java反射的具体使用,没有特别需要说明的地方,下面主要分析一下getSpringFactoriesInstances()方法中的SpringFactoriesLoader.loadFactoryNames(type, classLoader)这句代码。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            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 factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

上面这段源码中Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION))这句代码是一个从spring.factories中加载资源,并将指定类型的资源从Map中取出,这段代码的目的就是取出spring.factories中配置的ApplicationListener接口的实现了类和ApplicationContextInitializer接口实现类的集合,并将其实例化。

构造之后的run()方法解析

public ConfigurableApplicationContext run(String... args) {
                StopWatch stopWatch = new StopWatch();
                stopWatch.start();
                ConfigurableApplicationContext context = null;
                // 创建一个SpringBoot异常报告集合
                Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
                configureHeadlessProperty();
                SpringApplicationRunListeners listeners = getRunListeners(args);
                // 1. 发布一个应用启动事件
                listeners.starting();
                try {
                    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                    // 2. 根据当前ClassPath的类型创建一个应用启动环境,并发布一个环境准备事件 listeners.environmentPrepared(environment);
                    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                    // 配置启动环境的Bean信息忽略参数
                    configureIgnoreBeanInfo(environment);
                    // 配置应用启动环境的Banner,Banner打印机制是在环境准备完成之后进行的
                    Banner printedBanner = printBanner(environment);
                    // 3. 根据当前ClassPath环境创建ApplicationContext
                    context = createApplicationContext();
                    // 实例化异常报告类
                    exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                            new Class[] { ConfigurableApplicationContext.class }, context);
                    // 4. 准备ApplicationContext,同时发布上下文准备事件和上下文载入事件,这一步同时准备和载入上下文
                    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                    // 刷新应用上下文
                    refreshContext(context);
                    afterRefresh(context, applicationArguments);
                    stopWatch.stop();
                    if (this.logStartupInfo) {
                        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                   // 5. 发布应用启动成功事件              }
                    listeners.started(context);
                    // 调用所有实现了ApplicationRunner接口和CommandRunner接口的实例
                    callRunners(context, applicationArguments);*        }
                catch (Throwable ex) {
                    // 6. 如果整个过程包错,则会发布启动异常的错误报告,并发布启动失败事件
                    handleRunFailure(context, ex, exceptionReporters, listeners);
                    throw new IllegalStateException(ex);
                }
 
                try {
                    // 7.发布应用程序运行中事件
                    listeners.running(context);
                }
                catch (Throwable ex) {
                    handleRunFailure(context, ex, exceptionReporters, null);
                    throw new IllegalStateException(ex);
                }
                return context;
            }

run方法做了一下几件事情

SpringBoot应用上下文的实例化过程,代码如下:

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);
    }

这段代码通过反射的方式实例化了相应的上下文,本例中我们使用的是webServlet环境所以实例化的是{@link org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext}容器。到此SpringBoot的启动流程差不多就完成了。

上一篇下一篇

猜你喜欢

热点阅读