Spring Boot -- 启动源码分析

 2019-11-02 21:06  阅读(795)
文章分类:Spring boot

1 启动代码

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

通过以Java Application 的方式就能运行Spring Boot项目(web项目需要引入对应的starter,和普通的Java项目)

2 SpringApplication 源码分析

进入SpringApplication 的源码,分析Spring Boot的启动过程

2.1 SpringApplication 类的几个重要属性

/** * 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";

    public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
                + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

说明: DEFAULT_CONTEXT_CLASS 属性值 AnnotationConfigApplicationContext
DEFAULT_WEB_CONTEXT_CLASS 属性AnnotationConfigServletWebServerApplicationContext 从这两个Spring Context可以看出来都是Annotation类型的,这也符合Spring Boot设计的初衷 用Annotation代替XML的配置,然后通过starter来实现功能的模块化和自动注入。

2.2 SpringApplication.run代码

//常用的Spring boot启动调用方法
    public static ConfigurableApplicationContext run(Class<?> primarySource,String... args) {
            return run(new Class<?>[] { primarySource }, args);
    }
    //上面调用的是这个
    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);
    }

通过上面三个方法可以看到最后是调用了

new SpringApplication(primarySources).run(args)

就来看一下SpringApplication的构造方法

public SpringApplication(Class<?>... primarySources) {
            this(null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            //设置resourceLoader -- 这个地方为NULL
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            //判断Application类型
            this.webApplicationType = deduceWebApplicationType();
            //Context初始化
            setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));
                //设置监听器
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            //设置主类
            this.mainApplicationClass = deduceMainApplicationClass();
    }

这里主要关注下两个地方:Context初始化 和 监听器的设置,这两个调用的都是getSpringFactoriesInstances 方法。

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 = Thread.currentThread().getContextClassLoader();
            //获取Factory名称
            Set<String> names = new LinkedHashSet<>(
                    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            //实例化Factory
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                    classLoader, args, names);
            //加载顺序排序
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
    }

这里我们重点关注下SpringFactoriesLoader.loadFactoryNames(type, classLoader)这个方法。

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) {
            MultiValueMap<String, String> result = cache.get(classLoader);
            if (result != null) {
                return result;
            }

            try {
                   //加载资源路径-META-INF/spring.factories
                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()) {
                        List<String> factoryClassNames = Arrays.asList(
                                StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                        result.addAll((String) entry.getKey(), factoryClassNames);
                    }
                }
                cache.put(classLoader, result);
                return result;
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load factories from location [" +
                        FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
        }

说明:这里主要关注一个就是META-INF/spring.factories这个路径。加载的Spring Factory都配置在这里。
20191102100574\_1.png
到这里SpringApplication的自身初始化完成。

2.3 SpringApplication.run执行

SpringApplication实例化完成后调用run方法来执行。下面关注下run方法

public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            configureHeadlessProperty();
            //获取SpringApplicationRunListener--包括SpringBoot的和开发自定义的
            SpringApplicationRunListeners listeners = getRunListeners(args);
            //启动监听
            listeners.starting();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                configureIgnoreBeanInfo(environment);
                //打印Banner --控制台的默认的Spring Boot
                Banner printedBanner = printBanner(environment);
                //创建Context
                context = createApplicationContext();
                exceptionReporters = getSpringFactoriesInstances(
                        SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
                //处理context 
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                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.4 prepareContext 方法

在SpringApplication调用中有一个prepareContext 方法,下面来重点关注下这个方法的作用。

private void prepareContext(ConfigurableApplicationContext context,
                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments, Banner printedBanner) {
            //设置context的Environment
            context.setEnvironment(environment);
            postProcessApplicationContext(context);
            //应用ApplicationContextInitializer
            applyInitializers(context);
            //设置监听器的context
            listeners.contextPrepared(context);
            if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
            }

            // Add boot specific singleton beans
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
            if (printedBanner != null) {
                beanFactory.registerSingleton("springBootBanner", printedBanner);
            }
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
            // Load the sources
            Set<Object> sources = getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            load(context, sources.toArray(new Object[0]));
            listeners.contextLoaded(context);
        }

来源:http://ddrv.cn

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring Boot -- 启动源码分析

相关推荐