文档章节

Spring Boot启动过程(二)

drv
 drv
发布于 2017/05/26 20:01
字数 2349
阅读 9
收藏 0

  书接上篇

  该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractApplicationContext类型并调用它的refresh方法。由于AnnotationConfigEmbeddedWebApplicationContext继承自EmbeddedWebApplicationContext,所以会执行EmbeddedWebApplicationContext的refresh方法,继而执行其中的super.refresh。这个refresh也就是AbstractApplicationContext的refresh方法了,它内部是一个synchronized锁全局的代码块,同样的加锁方法还有这个类里的close和registerShutdownHook方法。

  同步代码块中第一个方法prepareRefresh,首先会执行AnnotationConfigEmbeddedWebApplicationContext的prepareRefresh方法:

protected void prepareRefresh() {
        this.scanner.clearCache();
        super.prepareRefresh();
    }

  这个super也就是AbstractApplicationContext,它的prepareRefresh方法逻辑是:生成启动时间;设置closed状态为false;active状态为true;initPropertySources方法主要是调用了AbstractEnvironment的getPropertySources方法获取了之前SpringApplication的prepareEnvironment方法中getOrCreateEnvironment方法准备的各种环境变量及配置并用于初始化ServletPropertySources。具体的servletContextInitParams这些是在环境对象初始化时由各集成级别Environment的customizePropertySources方法中初始化的。

   接着的getEnvironment().validateRequiredProperties()方法实际执行了AbstractEnvironment中的this.propertyResolver.validateRequiredProperties(),主要是验证了被占位的key如果是required的值不能为null。prepareRefresh的最后是初始化this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>()。*****

  只够是获取BeanFactory实例的方法obtainFreshBeanFactory(),首先在refreshBeanFactory方法中用原子布尔类型判断是否刷新过,BeanFactory实例是在createApplicationContext创建Context实例时被创建的,如果没有刷新则设置一个用于序列化的id,id是ContextIdApplicationContextInitializer初始化设置的(如未配置该初始化器,是有一个默认ObjectUtils.identityToString(this)生成的),这个id的生成规则是spring.config.name截取的+":"+server.port的占位截取。设置序列化id时,同时保存了一个id和弱引用DefaultListableBeanFactory实例映射。

  得到了beanFactory后就是prepareBeanFactory(beanFactory)了,逻辑是注册了BeanClassLoader用于注入的bean实例的创建;StandardBeanExpressionResolver用于EL表达式,比如配置文件或者@Value("#{...}")等使用;用ResourceEditorRegistrar注册属性转换器,比如xml配置的bean属性都是用的字符串配置的要转成真正的属性类型;addBeanPostProcessor(new ApplicationContextAwareProcessor(this))注册ApplicationContextAwareProcessor,它的invokeAwareInterfaces方法会对实现指定接口的bean调用指定的set方法;ignoreDependencyInterface忽略对这些接口的自动装配,比如Aware这些是要做独立处理的,不适合通用的方法;然后是有几个类型直接手动注册,比如BeanFactory,这个很好理解;接着注册一个后置处理器ApplicationListenerDetector的实例,addBeanPostProcessor注册的会按照注册先后顺序执行;这个方法的最后判断了特定的4个bean名字,如果存在会做相应注册,包括loadTimeWeaver、environment、systemProperties和systemEnvironment。补充一点,在最开始创建实例的时候还执行过ignoreDependencyInterface(BeanNameAware.class);ignoreDependencyInterface(BeanFactoryAware.class);ignoreDependencyInterface(BeanClassLoaderAware.class)。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        beanFactory.setBeanClassLoader(getClassLoader());
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        // Configure the bean factory with context callbacks.
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

        // BeanFactory interface not registered as resolvable type in a plain factory.
        // MessageSource registered (and found for autowiring) as a bean.
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        // Register early post-processor for detecting inner beans as ApplicationListeners.
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        // Register default environment beans.
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }

   之后到了refresh的postProcessBeanFactory方法,首先是会走到AnnotationConfigEmbeddedWebApplicationContext的Override,需要注意的一点是,这是web环境,如果不是是不会加载这个上下文的,也就不会这么走。它重写的第一步是先走super也就是EmbeddedWebApplicationContext的postProcessBeanFactory,这里又注册了个后置处理器WebApplicationContextServletContextAwareProcessor的实例,构造参数是this,也就是当前上下文,同时忽略ServletContextAware接口,这个接口是用于获取ServletContext的,为什么要忽略呢,我猜应该是因为我们既然有了web应用并且内嵌servlet的上下文实例,还要ServletContext的实现就没什么用了,还有可能出现冲突的问题,有空我再确认下。然后是配置的basePackages和annotatedClasses:

    @Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.postProcessBeanFactory(beanFactory);
        if (this.basePackages != null && this.basePackages.length > 0) {
            this.scanner.scan(this.basePackages);
        }
        if (this.annotatedClasses != null && this.annotatedClasses.length > 0) {
            this.reader.register(this.annotatedClasses);
        }
    }

  到了invokeBeanFactoryPostProcessors方法,这个方法就是执行之前注册的BeanFactory后置处理器的地方。代码一目了然,PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors中只是有些排序的逻辑,我就不说了:

/**
     * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
     * respecting explicit order if given.
     * <p>Must be called before singleton instantiation.
     */
    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

        // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
        // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
        if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }
    }

   BeanFactory后置处理器执行之后是注册Bean的后置处理器方法registerBeanPostProcessors。例如new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)会在Bean没有合适的后置处理器时记条info级日志。ApplicationListenerDetector也注册了一个。

  initMessageSource这个方法在我这没什么用,都说是国际化的,随便百度一下一堆一堆的,而且其实严格来说这篇多数不属于spring boot的部分,这方法我就不细写了。

  initApplicationEventMulticaster方法主要也就是初始化并注册applicationEventMulticaster的这两句代码:

this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);

   onRefresh也是根据环境不同加载的上下文不同而不同的,用于支持子类扩展出来的上下文特定的逻辑的。EmbeddedWebApplicationContext的onRefresh首先依然是super.onRefresh,逻辑就是初始化了主题;createEmbeddedServletContainer方法名我就不翻译了,一般情况下是使用getBeanFactory .getBeanNamesForType方法找到EmbeddedServletContainerFactory类型的实例,这也就是我之前那个问题解决过程中,为什么只要排除掉tomcat引用,引入jetty引用就可以自动换成jetty的原因。创建容器的过程中初始化方法selfInitialize注册了filter和MappingForUrlPatterns等,代码在AbstractFilterRegistrationBean等onStartup,这里就不细说了,如果能抽出时间说说之前查问题的时候查的容器代码再说。然后初始化PropertySources,servletContextInitParams和servletConfigInitParams:

public static void initServletPropertySources(
            MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {

        Assert.notNull(propertySources, "'propertySources' must not be null");
        if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
                propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
            propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
                    new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
        }
        if (servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
                propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
            propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
                    new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
        }
    }

  registerListeners首先注册静态监听:

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        synchronized (this.retrievalMutex) {
            this.defaultRetriever.applicationListeners.add(listener);
            this.retrieverCache.clear();
        }
    }

  接着是:

  registerListeners的最后,初始化过的earlyApplicationEvents如果有事件,这时候会被发布。

  finishBeanFactoryInitialization结束BeanFactory的初始化并初始化所有非延迟加载的单例。事实上我们自定义的单例Bean都是在这里getBean方法初始化的,所以如果注册的Bean特别多的话,这个过程就是启动过程中最慢的。初始化开始前先设置configurationFrozen为true,并this.frozenBeanDefinitionNames = StringUtils.toStringArray ( this. beanDefinitionNames )。如果有bean实例实现了SmartInitializingSingleton会有后置处理触发,不包括延迟加载的。例如:org.springframework.context.event. internalEventListenerProcessor会触发EventListenerMethodProcessor的afterSingletonsInstantiated方法对所有对象(Object的子类)处理。

  finishRefresh:Refresh的最后一步,发布相应事件。同样先执行EmbeddedWebApplicationContext中对应方法的super(EmbeddedWebApplicationContext)的对应方法:

/**
     * Finish the refresh of this context, invoking the LifecycleProcessor's
     * onRefresh() method and publishing the
     * {@link org.springframework.context.event.ContextRefreshedEvent}.
     */
    protected void finishRefresh() {
        // Initialize lifecycle processor for this context.
        initLifecycleProcessor();

        // Propagate refresh to lifecycle processor first.
        getLifecycleProcessor().onRefresh();

        // Publish the final event.
        publishEvent(new ContextRefreshedEvent(this));

        // Participate in LiveBeansView MBean, if active.
        LiveBeansView.registerApplicationContext(this);
    }

   初始化生命周期处理器,逻辑是判断beanFactory中是否已经注册了lifecycleProcessor,没有就new一个DefaultLifecycleProcessor并setBeanFactory(beanFactory),然后将它赋值给私有LifecycleProcessor类型的this变量。然后执行生命周期处理器的onRefresh,其中先startBeans,被start的beans是通过getBeanNamesForType(Lifecycle.class, false, false)从beanFactory中取出来的,例如endpointMBeanExporter和lifecycleProcessor,会去调用bean的start方法,endpointMBeanExporter的start中执行 locateAndRegisterEndpoints方法并设置running属性为true,这个过程加了ReentrantLock锁。bean都启动完会设置处理器的running为true。刷新完会发布ContextRefreshedEvent事件,这个事件除了都有的记录时间还执行了ConfigurationPropertiesBindingPostProcessor的freeLocalValidator方法,我这的逻辑是实际上执行了ValidatorFactoryImpl的close方法。这个逻辑的最后会检查一个配置spring.liveBeansView.mbeanDomain是否存在,有就会创建一个MBeanServer:

static void registerApplicationContext(ConfigurableApplicationContext applicationContext) {
        String mbeanDomain = applicationContext.getEnvironment().getProperty(MBEAN_DOMAIN_PROPERTY_NAME);
        if (mbeanDomain != null) {
            synchronized (applicationContexts) {
                if (applicationContexts.isEmpty()) {
                    try {
                        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
                        applicationName = applicationContext.getApplicationName();
                        server.registerMBean(new LiveBeansView(),
                                new ObjectName(mbeanDomain, MBEAN_APPLICATION_KEY, applicationName));
                    }
                    catch (Throwable ex) {
                        throw new ApplicationContextException("Failed to register LiveBeansView MBean", ex);
                    }
                }
                applicationContexts.add(applicationContext);
            }
        }
    }

  finishRefresh最后会启动前面创建的内嵌容器,并发布EmbeddedServletContainerInitializedEvent事件,启动这一部分算是容器的逻辑了,有机会整理容器逻辑再细写,我这里是Tomcat的:

@Override
    public void start() throws EmbeddedServletContainerException {
        try {
            addPreviouslyRemovedConnectors();
            Connector connector = this.tomcat.getConnector();
            if (connector != null && this.autoStart) {
                startConnector(connector);
            }
            checkThatConnectorsHaveStarted();
            TomcatEmbeddedServletContainer.logger
                    .info("Tomcat started on port(s): " + getPortsDescription(true));
        }
        catch (ConnectorStartFailedException ex) {
            stopSilently();
            throw ex;
        }
        catch (Exception ex) {
            throw new EmbeddedServletContainerException(
                    "Unable to start embedded Tomcat servlet container", ex);
        }
        finally {
            Context context = findContext();
            ContextBindings.unbindClassLoader(context, getNamingToken(context),
                    getClass().getClassLoader());
        }
    }

  然后是resetCommonCaches:

/**
     * Reset Spring's common core caches, in particular the {@link ReflectionUtils},
     * {@link ResolvableType} and {@link CachedIntrospectionResults} caches.
     * @since 4.2
     * @see ReflectionUtils#clearCache()
     * @see ResolvableType#clearCache()
     * @see CachedIntrospectionResults#clearClassLoader(ClassLoader)
     */
    protected void resetCommonCaches() {
        ReflectionUtils.clearCache();
        ResolvableType.clearCache();
        CachedIntrospectionResults.clearClassLoader(getClassLoader());
    }

  refreshContext的最后是注册shutdown的钩子:

if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }

    /**
     * Register a shutdown hook with the JVM runtime, closing this context
     * on JVM shutdown unless it has already been closed at that time.
     * <p>Delegates to {@code doClose()} for the actual closing procedure.
     * @see Runtime#addShutdownHook
     * @see #close()
     * @see #doClose()
     */
    @Override
    public void registerShutdownHook() {
        if (this.shutdownHook == null) {
            // No shutdown hook registered yet.
            this.shutdownHook = new Thread() {
                @Override
                public void run() {
                    synchronized (startupShutdownMonitor) {
                        doClose();
                    }
                }
            };
            Runtime.getRuntime().addShutdownHook(this.shutdownHook);
        }
    }

 

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公众号:

                      

© 著作权归作者所有

drv

drv

粉丝 2
博文 57
码字总数 75382
作品 0
东城
架构师
私信 提问
微服务 SpringBoot 2.0(四):启动剖析之SpringApplication.run

我以为只是运行了个main方法,可却启动了服务 —— Java面试必修 引言 前一章我们讲解了启动原理的部分,仔细跟着看代码还是很好理解,若需观看@SpringBootApplication注解部分请点击,接下来...

阿郎_
2018/09/25
0
0
spring boot 1.5.4 入门和原理(二)

1 spring boot入门 1.1 环境准备 JDK 7及以上 eclipse开发工具 项目管理工具Maven 本文采用、RELEASE(或1.5.2.RELEASE)调试通过。 spring-boot相关项目源码, 码云地址:https://git.oschi...

wyait
2017/09/18
0
0
Hello , Spring Boot

Spring Boot Spring Boot主要目的是简化了Spring的引用开发,遵循约定大于配置的思想,不需要我们再去配置就能运行Spring应用,Spring Boot管理Spring容器,第三方插件,并且提供了很多默认系...

胖先森
2018/09/04
0
0
史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现(Eureka)

一、spring cloud简介spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环...

方宏春
2018/04/14
292
1
[springboot](十三)spring boot admin 监控集群

版权声明:本文为博主原创文章,未经博主允许不得转载。有任何问题请邮件联系ashuo.com@qq.com https://blog.csdn.net/drdongshiye/article/details/82808083 参考https://www.cnblogs.com/i...

菜鸟腾飞
2018/09/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Taro 兼容 h5 踩坑指南

最近一周在改造 公羊阅读🐏 Taro 版本适配 h5 端,过程中改改补补,好不酸爽。 本文记录📝遇到的问题,希望为有相同需求的哥们👬节约点时间。 Taro 版本:1.3.9。 client_mobile_taro...

dkvirus
今天
4
0
Spring boot 静态资源访问

0. 两个配置 spring.mvc.static-path-patternspring.resources.static-locations 1. application中需要先行的两个配置项 1.1 spring.mvc.static-path-pattern 这个配置项是告诉springboo......

moon888
今天
3
0
hash slot(虚拟桶)

在分布式集群中,如何保证相同请求落到相同的机器上,并且后面的集群机器可以尽可能的均分请求,并且当扩容或down机的情况下能对原有集群影响最小。 round robin算法:是把数据mod后直接映射...

李朝强
今天
4
0
Kafka 原理和实战

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/bV8AhqAjQp4a_iXRfobkCQ 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双语班)。先后从事过电子商务、开放平...

vivo互联网技术
今天
19
0
java数据类型

基本类型: 整型:Byte,short,int,long 浮点型:float,double 字符型:char 布尔型:boolean 引用类型: 类类型: 接口类型: 数组类型: Byte 1字节 八位 -128 -------- 127 short 2字节...

audience_1
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部