美文网首页
SpringBoot启动流程源码分析

SpringBoot启动流程源码分析

作者: 太曜道人 | 来源:发表于2019-08-18 11:59 被阅读0次

    先写个HelloApplication.java :

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

    run方法最终在SpringApplication中调用

    return new SpringApplication(primarySources).run(args);
    

    可以看到包括两部分:

    1. 实例化一个SpringApplication对象

    2. 调用其run方法

    SpringApplication对象的实例化过程

        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();
        }
    
    • this.webApplicationType = WebApplicationType.deduceFromClasspath();
    • setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
    • setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class));
    • this.mainApplicationClass = deduceMainApplicationClass();

    可以看到主要就是这几个方法调用,逐个分析:

    deduceFromClasspath

        public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
            try {
                forName(className, classLoader);
                return true;
            }
            catch (IllegalAccessError err) {
                throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
                        className + "]: " + err.getMessage(), err);
            }
            catch (Throwable ex) {
                // Typically ClassNotFoundException or NoClassDefFoundError...
                return false;
            }
        }
    
    • isPresent会尝试根据字符串来加载类,从而判断当前的classpath中有没有对应的类
    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;
        }
    
    • deduceFromClasspath的作用就是根据一些流程来判断有没有类似"org.springframework.web.reactive.DispatcherHandler"的这些类存在,从而判断类型。
    NONEThe application should not run as a web application and should not start an embedded web server.
    REACTIVEThe application should run as a reactive web application and should start an embedded reactive web server.
    SERVLETThe application should run as a servlet-based web application and should start an embedded servlet web server.

    setInitializers

    调用源码,按照层次换行

    setInitializers(
    ​       (Collection)getSpringFactoriesInstances(
    ​           ApplicationContextInitializer.class
    ​       )
    )
    

    其中setInitializers

        public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
            this.initializers = new ArrayList<>();
            this.initializers.addAll(initializers);
        }
    

    没什么好说的
    关键在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 = 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;
        }
    
    //createSpringFactoriesInstances的关键部分,一个工厂方法,没啥好说的
        private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
                ClassLoader classLoader, Object[] args, Set<String> names)
    {
                    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);
    }
    

    单步调试发现names有这么多

    1. "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
    2. "org.springframework.boot.context.ContextIdApplicationContextInitializer"
    3. "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
    4. "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
    5. "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
    6. "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"

    所以这一步会注册6个initializers,后续可以看到对他们的调用

    setListeners

    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
        //老老实实的setter
        public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
            this.listeners = new ArrayList<>();
            this.listeners.addAll(listeners);
        }
    

    剩下的和上一步一模一样,只是names变成了

    0 = "org.springframework.boot.ClearCachesApplicationListener"
    1 = "org.springframework.boot.builder.ParentContextCloserApplicationListener"
    2 = "org.springframework.boot.context.FileEncodingApplicationListener"
    3 = "org.springframework.boot.context.config.AnsiOutputApplicationListener"
    4 = "org.springframework.boot.context.config.ConfigFileApplicationListener"
    5 = "org.springframework.boot.context.config.DelegatingApplicationListener"
    6 = "org.springframework.boot.context.logging.ClasspathLoggingApplicationListener"
    7 = "org.springframework.boot.context.logging.LoggingApplicationListener"
    8 = "org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"
    9 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"

    deduceMainApplicationClass

    看名字是推测主类的类型

        private Class<?> deduceMainApplicationClass() {
            try {
                StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
                for (StackTraceElement stackTraceElement : stackTrace) {
                    if ("main".equals(stackTraceElement.getMethodName())) {
                        return Class.forName(stackTraceElement.getClassName());
                    }
                }
            }
            catch (ClassNotFoundException ex) {
                // Swallow and continue
            }
            return null;
        }
    

    获取当前调用栈,看看哪个方法名字是main

    然而main方法也可以被用作实例方法,各种重载啥的,当然一般生物不会那么做

    run方法的调用

    激动人心的时刻到了,搞了这么久,关键时刻,是时候掉链子了

        public ConfigurableApplicationContext run(String... args) {
            //计时器不用管他
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            //一些初始化和配置,不用管
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            
            //关键点1:listener start
            listeners.starting();
            try {
                //应用参数,一般是个CommandLineArgs
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                //小关键点1
                ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                //根据上一步的配置搞些事情,貌似影响不大的样子
                configureIgnoreBeanInfo(environment);
                
                //打印图标
                Banner printedBanner = printBanner(environment);
                
                //关键点2
                context = createApplicationContext();
                
                //用于后面的catch语句中处理异常,不用管它
                exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
                
                //关键点3
                prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                
                //关键点4
                refreshContext(context);
                
                //关键点5
                afterRefresh(context, applicationArguments);
                
                //计时器不用管
                stopWatch.stop();
                //日志啥的
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                }
                
                //关键点6
                listeners.started(context);
                
                //关键点7
                callRunners(context, applicationArguments);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, listeners);
                throw new IllegalStateException(ex);
            }
    
            try {
                //关键点8:listener
                listeners.running(context);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, null);
                throw new IllegalStateException(ex);
            }
            //返回值没人接收,不用管它
            return context;
        }
    

    所以要分析8个主要矛盾和一个次要矛盾

    先拿次要矛盾练练手

    prepareEnvironment

    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    
        private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
            // Create and configure the environment
            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;
        }
    

    environment的初始化,配置文件加载,包括大名鼎鼎的profile

    listeners.starting()

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

    发布一个ApplicationStartingEvent事件

    createApplicationContext()

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

    根据不同的webApplication类型初始化同的容器,使用BeanUtils来实例化,其内部是用反射来搞的

    prepareContext

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        
            context.setEnvironment(environment); //1
            postProcessApplicationContext(context);//2
            applyInitializers(context);//3
        
        //发消息
            listeners.contextPrepared(context);
        
        //日志里打出被激活的profile,不用管
            if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
            }
        
            // Add boot specific singleton beans
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//4
            beanFactory.registerSingleton("springApplicationArguments", applicationArguments);//5
        
        //打出banner等信息,不用管
            if (printedBanner != null) {
                beanFactory.registerSingleton("springBootBanner", printedBanner);
            }
        
            if (beanFactory instanceof DefaultListableBeanFactory) {//6 允许同名覆盖吗?
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
            // Load the sources
            Set<Object> sources = getAllSources();//7
            Assert.notEmpty(sources, "Sources must not be empty");
            load(context, sources.toArray(new Object[0]));//8
            //发消息   
            listeners.contextLoaded(context);
        }
    

    context.setEnvironment(environment);

    @Overridepublic void setEnvironment(ConfigurableEnvironment environment) { 
        super.setEnvironment(environment);                                                               this.reader.setEnvironment(environment);   
        this.scanner.setEnvironment(environment);
    }
    
    public void setEnvironment(Environment environment) {
        this.conditionEvaluator = new ConditionEvaluator(this.registry, environment, null);
    }
    

    可以看到条件注解的解析工具是在这里注册的,根据环境配置文件来搞的

    postProcessApplicationContext

        /**
         * Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
         * apply additional processing as required.
         * @param context the application context
         */
        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());
            }
        }
    

    getBeanFactory的结果是一个DefaultListableBeanFactory

    setConversionService,ConversionService是用来做类型转换的

    有个converse接口

    applyInitializers

        /**
         * 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) {
            for (ApplicationContextInitializer initializer : getInitializers()) {
                Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                        ApplicationContextInitializer.class);
                Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
                initializer.initialize(context);
            }
        }
    

    之前分析过setInitializers方法,这里先获取到Initializers,逐个执行其初始化方法

    一般在初始化里面会进行

    • listener的注册

    • 注册添加一些beans啥的

    • 添加postProcess

    beanFactory

    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    

    一个DefaultListableBeanFactory

    registerSingleton

    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);//5    
    

    注册一个这样的单例,但不知道有啥用啊

    setAllowBeanDefinitionOverriding

            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            
    

    同名覆盖

    原博客地址

    所谓的Spring容器,其实就是一个大Map,其管理的bean的id(也就是bean的name)应该都是不一样的,假如项目中有两个bean的name一样了,在项目启动时一般会报类似于下图的同名异常:

    å�¨è¿�é��æ��å ¥å�¾ç��æ��è¿°

    但在有些情况下,spring并不会报"同名异常" ,其处理方式是:
    如果两个bean的name相同,则后出现的bean会覆盖前面出现的同名bean
    1
    所带来的问题:
    如果启动时,对于同名的bean加载没有异常信息,出现问题后将会很难进行定位。
    1
    产生原因:
    spring中的DefaultListableBeanFactory类有个属性:allowBeanDefinitionOverriding,
    默认情况下为true,即允许重名的bean可以被覆盖。

    可参考下面源码DeafaultListable的

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); //先获取已经存在的同名bean
            if (existingDefinition != null) {
                if (!isAllowBeanDefinitionOverriding()) {//存在,不允许覆盖,搞事情
                    throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
                }
                //存在,允许覆盖
                else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                    // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                    if (logger.isInfoEnabled()) {
                        logger.info("Overriding user-defined bean definition for bean '" + beanName +
                                "' with a framework-generated bean definition: replacing [" +
                                existingDefinition + "] with [" + beanDefinition + "]");
                    }
                }
                else if (!beanDefinition.equals(existingDefinition)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Overriding bean definition for bean '" + beanName +
                                "' with a different definition: replacing [" + existingDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                }
                else {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Overriding bean definition for bean '" + beanName +
                                "' with an equivalent definition: replacing [" + existingDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                }
                this.beanDefinitionMap.put(beanName, beanDefinition); //拿新的替换掉旧的
            }
    

    getAllSources

    Set<Object> sources = getAllSources();
    
        public Set<Object> getAllSources() {
            Set<Object> allSources = new LinkedHashSet<>();
            if (!CollectionUtils.isEmpty(this.primarySources)) {
                allSources.addAll(this.primarySources);
            }
            if (!CollectionUtils.isEmpty(this.sources)) {
                allSources.addAll(this.sources);
            }
            return Collections.unmodifiableSet(allSources);
        }
    

    调试时发现就加载了个主类,HelloApplication的那个

    load

    load(context, sources.toArray(new Object[0]))
        
        /**
         * Load beans into the application context.
         * @param context the context to load beans into
         * @param sources the sources to load
         */
        protected void load(ApplicationContext context, Object[] sources) {
            if (logger.isDebugEnabled()) {
                logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
            }
            BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
            if (this.beanNameGenerator != null) {
                loader.setBeanNameGenerator(this.beanNameGenerator);
            }
            if (this.resourceLoader != null) {
                loader.setResourceLoader(this.resourceLoader);
            }
            if (this.environment != null) {
                loader.setEnvironment(this.environment);
            }
            loader.load();
        }
    

    BeanDefinitionLoader用于从源加载Bean的定义信息,并封装成BeanDefinition对象,并注册到ApplicationContext中,加载的源可以是类注解、XML文件、package、classpath、Groovy文件等。

    这里加载的source只有一个HelloApplication,也就是主类

    最后一步加载BeanDefinition,注意在doRegisterBean中有一个

            if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
                return;
            }
    

    对应前面提到的条件判断

    最终的核心部分就是一个

    beanDefinition = definitionHolder.getBeanDefinition()               
    this.beanDefinitionMap.put(beanName, beanDefinition);
    this.beanDefinitionNames.add(beanName);
    removeManualSingletonName(beanName);
    

    BeanDefinition中包含了当前Bean有哪些注解,名字,哪个类等等信息

    总结:load这一步就是读取source文件,从中获取各种BeanDefinition,加到beanFactory的map里面完事,Bean的实例化、初始化还在后面

    refreshContext(context)

    private void refreshContext(ConfigurableApplicationContext context) {
            refresh(context);
            if (this.registerShutdownHook) {
                try {
                    context.registerShutdownHook();
                }
                catch (AccessControlException ex) {
                    // Not allowed in some environments.
                }
            }
        }
        protected void refresh(ApplicationContext applicationContext) {
            Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
            ((AbstractApplicationContext) applicationContext).refresh();
        }
    
    

    最终调用的还是AbstractApplicationContext的refresh,在IOC中继续研究

    afterRefresh(context, applicationArguments)

        protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
        }
    

    没实现啊,可以在这里做一些钩子?

    listeners.started(context)

        public void started(ConfigurableApplicationContext context) {
            context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
        }
    

    他又来发事件了,但这次是通过context来发的,跟上次不同

    callRunners(context, applicationArguments)

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

    逐个调用Runner中的run方法,但是我的Runner是空的哇

    立刻实现一个:

    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    import java.util.concurrent.TimeUnit;
    
    @Component
    public class MRunner implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            for (int i = 0; i < 100; i++) {
                try {
                    System.out.println(i);
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    启动后果然开始输出数字了

    listeners.running(context)

        @Override
        public void running(ConfigurableApplicationContext context) {
            context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
        }
    

    所以listener的作用就是发事件?

    对start事件的单步调试

    发了三次事件,对第一次仔细研究一下

    发现最终接受的只有一个loglisterner,所以控制台的输出使用事件机制实现的吗?

        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ApplicationStartingEvent) {
                onApplicationStartingEvent((ApplicationStartingEvent) event);
            }
            else if (event instanceof ApplicationEnvironmentPreparedEvent) {
                onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
            }
            else if (event instanceof ApplicationPreparedEvent) {
                onApplicationPreparedEvent((ApplicationPreparedEvent) event);
            }
            else if (event instanceof ContextClosedEvent
                    && ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
                onContextClosedEvent();
            }
            else if (event instanceof ApplicationFailedEvent) {
                onApplicationFailedEvent();
            }
        }
    

    相关文章

      网友评论

          本文标题:SpringBoot启动流程源码分析

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