美文网首页
springboot源码1--启动流程

springboot源码1--启动流程

作者: xian_cheng | 来源:发表于2018-11-20 09:42 被阅读0次

    之前在使用spring boot框架搭建一个web服务时,一直想详细研究下spring boot的源码,主要是bean加载到IOC容器和Spring Aop这两个功能的具体实现,最近有时间就在家看了下spring关于这两个功能的源码,也在网上找了些资料去看,发现大部分资料写的都是偏重于某一块源码的讲解,我是希望能够按照spring boot的启动流程来分析这两个功能,这样的话能够前后连贯,理解起来也会更容易,否则单独讲这两部分的话,很多东西不知道在哪完成初始化的,因此关于spring 源码学习的第一篇文章就从spring boot框架的启动开始讲解。

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

    通过调用SpringApplication的run方法就可以快速启动一个web应用。这个run方法里最后会调用下面这个run方法。

    public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
            return new SpringApplication(sources).run(args);
        }
    
        public SpringApplication(Object... sources) {
            initialize(sources);
        }
    
        private void initialize(Object[] sources) {
            if (sources != null && sources.length > 0) {
                this.sources.addAll(Arrays.asList(sources));
            }
            this.webEnvironment = deduceWebEnvironment();
            setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            this.mainApplicationClass = deduceMainApplicationClass();
        }
    

    这个run方法中new了一个SpringApplication实例,在构造函数中调用了initialize方法,在这个方法里主要做了三件事:
    (1)检查当前启动的应用是否是一个web应用。如果是webEnvironment 变量的值为true,检测的方法是能否通过反射实例化下面这两个类来判断。

    "javax.servlet.Servlet",        "org.springframework.web.context.ConfigurableWebApplicationContext"
    

    (2) 设置ApplicationContextInitializer实现类,这些实现类是配置在META-INF/spring.factories文件中,这些实现类后面会使用。

    org.springframework.context.ApplicationContextInitializer=
    org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
    org.springframework.boot.context.ContextIdApplicationContextInitializer,
    org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
    org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
    

    (3) 设置listener监听器,这些监听器也是配置在META-INF/spring.factories下如下:

    org.springframework.context.ApplicationListener=
    org.springframework.boot.ClearCachesApplicationListener,
    org.springframework.boot.builder.ParentContextCloserApplicationListener,
    org.springframework.boot.context.FileEncodingApplicationListener,
    org.springframework.boot.context.config.AnsiOutputApplicationListener,
    org.springframework.boot.context.config.ConfigFileApplicationListener,
    org.springframework.boot.context.config.DelegatingApplicationListener,
    org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,
    org.springframework.boot.logging.ClasspathLoggingApplicationListener,
    org.springframework.boot.logging.LoggingApplicationListener
    

    这些监听器是spring启动时,自动加载的。用户也可以自己实现ApplicationListener接口,按照业务的要求实现具体的监听器,对spring的事件监听机制不熟悉的可以参考这篇文章sping监听器

    初始化SpringApplication实例后,调用SpringApplication的run方法,看下这个run方法实现。

        public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            FailureAnalyzers analyzers = null;
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                analyzers = new FailureAnalyzers(context);
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                refreshContext(context);
                afterRefresh(context, applicationArguments);
                listeners.finished(context, null);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                return context;
            }
        }
    

    这个方法比较长,spring的启动就是在这个方法中完成的,我们还是按照步骤来分析这个方法。
    (1)获取SpringApplicationRunListeners对象,获取方式与之前获取ApplicationListener实现类相同,也是从spring.factories中获取具体的SpringApplicationRunListeners实现类。

    org.springframework.boot.SpringApplicationRunListener=\
    org.springframework.boot.context.event.EventPublishingRunListener
    

    EventPublishingRunListener这个类的主要作用是根据spring启动的不同时期,触发不同的事件类型。之前设置的监听器,每个监听器对应不同的事件,事件类型匹配时会触发对应监听器的onApplicationEvent方法,这是一个典型的观察者模式。这个类的具体代码可以参考EventPublishingRunListener

    (2)触发EventPublishingRunListener的starting方法。

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

    这个方法就会触发一个ApplicationStartedEvent事件,通知这个事件对应的监听器完成具体的业务。

    (3)创建ApplicationContext对象。
    这个对象非常重要,Spring IOC的实现主要就是基于这个对象。看下怎么创建的。

        protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            if (contextClass == null) {
                try {
                    contextClass = Class.forName(this.webEnvironment
                            ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
                }
                catch (ClassNotFoundException ex) {
                
                }
            }
            return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
        }
    

    根据是否是一个web应用,确定ApplicationContext的具体类型。本文以web应用为例,初始化一个AnnotationConfigEmbeddedWebApplicationContext类,具体初始化过程参考ApplicationContext对象初始化过程

    (4)执行prepareContext操作。

        private void prepareContext(ConfigurableApplicationContext context,
                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments, Banner printedBanner) {
            context.setEnvironment(environment);
            postProcessApplicationContext(context);
            applyInitializers(context);
            listeners.contextPrepared(context);
            。。。
            // Load the sources
            Set<Object> sources = getSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            load(context, sources.toArray(new Object[sources.size()]));
            listeners.contextLoaded(context);
        }
        }
    

    这个方法中主要对上一步创建的ApplicationContext做一些初始化操作,调用applyInitializers方法。

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

    这个方法中会遍历之前添加的实现ApplicationContextInitializer的初始化子类,然后调用这些子类的initialize方法,完成对ApplicationContext的一些初始化操作。
    例如:
    ContextIdApplicationContextInitializer类会为ApplicationContext生成一个唯一的id。
    DelegatingApplicationContextInitializer类会从application.properties配置文件中读取key为context.initializer.classes的实现ApplicationContextInitializer的初始化子类,这样就允许开发者自己添加ApplicationContextInitializer子类。

    遍历完初始化子类后,执行listeners.contextPrepared(context),这个是触发监听器操作的,这个方法目前是空,不会触发监听器的执行。

    然后执行load方法,加载Spring boot框架的主函数所在的类到ApplicationContext中,看下加载过程。

        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类,然后调用load方法,省略中间调用过程,调用下面这个load方法。

        private int load(Class<?> source) {
            。。。
            if (isComponent(source)) {
                this.annotatedReader.register(source);
                return 1;
            }
            return 0;
        }
    

    首先加载的类是否含有@Component注解,然后调用annotatedReader.register(source)方法,annotatedReader实例对应的实现类是AnnotatedBeanDefinitionReader类,忽略中间调用过程,最终会调用下面这个register方法。

        public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
            AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
            if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
                return;
            }
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
            abd.setScope(scopeMetadata.getScopeName());
            String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
            AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
            if (qualifiers != null) {
                for (Class<? extends Annotation> qualifier : qualifiers) {
                    if (Primary.class == qualifier) {
                        abd.setPrimary(true);
                    }
                    else if (Lazy.class == qualifier) {
                        abd.setLazyInit(true);
                    }
                    else {
                        abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                    }
                }
            }
    
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
            definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
        }
    

    首先创建一个基于注解的AnnotatedGenericBeanDefinition实现类,这个类中保存了bean的所有信息。
    然后调用this.scopeMetadataResolver.resolveScopeMetadata(abd)这个方法。

        @Override
        public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
            ScopeMetadata metadata = new ScopeMetadata();
            if (definition instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
                AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
                        annDef.getMetadata(), this.scopeAnnotationType);
                if (attributes != null) {
                    metadata.setScopeName(attributes.getString("value"));
                    ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
                    if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
                        proxyMode = this.defaultProxyMode;
                    }
                    metadata.setScopedProxyMode(proxyMode);
                }
            }
            return metadata;
        }
    

    这个方法主要是判断类中是否有@Scope注解,如果有这个注解会获取这个注解对应的value和proxyMode属性,关于这个注解可以参考@Scope。获取完@Scope注解属性后返回ScopeMetadata对象。根据返回的ScopeMetadata对象,设置AnnotatedGenericBeanDefinition中这个bean的Scope属性。

    调用AnnotationConfigUtils.processCommonDefinitionAnnotations(abd)这个方法完成对这个类中注解的解析,代码如下。

        static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
            if (metadata.isAnnotated(Lazy.class.getName())) {
                abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));
            }
            else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {
                abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value"));
            }
    
            if (metadata.isAnnotated(Primary.class.getName())) {
                abd.setPrimary(true);
            }
            if (metadata.isAnnotated(DependsOn.class.getName())) {
                abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));
            }
    
            if (abd instanceof AbstractBeanDefinition) {
                AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
                if (metadata.isAnnotated(Role.class.getName())) {
                    absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue());
                }
                if (metadata.isAnnotated(Description.class.getName())) {
                    absBd.setDescription(attributesFor(metadata, Description.class).getString("value"));
                }
            }
        }
    

    解析完常用注解后,将这个AnnotatedGenericBeanDefinition放入BeanDefinitionHolder对象中,然后调用AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry)。

        static BeanDefinitionHolder applyScopedProxyMode(
                ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
    
            ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
            if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
                return definition;
            }
            boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
            return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
        }
    

    如果bean中含有@Scope注解,则根据proxyMode属性生成一个代理对象。
    生成代理对象后,将这个代理对象放入ApplicationContext中DefaultListableBeanFactory中的beanDefinitionMap这个map中。

    到这里prepareContext方法的大部分功能就完成了。

    (5)refresh Context
    上一步只是初始化了context的一些参数,创建了context里创建bean的DefaultListableBeanFactory,将sping boot启动的主函数类放入beanDefinitionMap这个map中,项目中的其他定义的bean还未被扫描放入beanDefinitionMap,以及beanDefinitionMap中放入的bean还没有被实例化。这些操作其实是在refresh Context这一步完成。
    这一步的内容比较多,也比较复杂,我们在bean加载详细讲解。

    我们接下来还是继续分析启动的主流程。
    (6)最后调用listeners.finished(context, null)
    看下fininshed这个方法

        public void finished(ConfigurableApplicationContext context, Throwable exception) {
            SpringApplicationEvent event = getFinishedEvent(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);
                    }
                }
                if (event instanceof ApplicationFailedEvent) {
                    this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
                }
                this.initialMulticaster.multicastEvent(event);
            }
        }
    
    

    其实就是发送一个ApplicationReadyEvent事件触发监听此事件的监听器,我看了下之前加载的监听器好像没有监听此事件的监听器,当然用户也可以自己定义关于此事件的监听器。

    到这里springboot 启动的大概流程就分析完了,大部分的文章对这部分都是一概而过,直接去讲bean的加载,但是这里还是有很多内容需要清楚的,这样才能更明白类的调用关系、监听器模式、bean是如何加载的。

    下一节就具体讲bean加载

    相关文章

      网友评论

          本文标题:springboot源码1--启动流程

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