spring容器启动流程

工作至今,一直在使用spring,并且对spring的强大的功能无不折服,虽然一直使用,但对spring容器的启动流程却不知其所以然,最近花了点时间查阅了部分资料,配合源码对spring容器的启动仔细的了解一下,在此,记录一下整个过程,这里以spring-web项目的启动为基础进行分析。
在web项目中,首先会注意到web.xml这个文件,里面配置各种监听、过滤器,servlet等,spring容器的启动入口就在web.xml里进行配置:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

ContextLoaderListener:上下文加载监听器,spring容器的启动就自从这里开始,跟随代码到ContextLoaderListener类中,有这么一个方法

/**
 * Initialize the root web application context.
 */
@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}

initWebApplicationContext方法,从方法名称上可以看出,这里开始初始化web容器了,跟随代码进入该方法:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
            throw new IllegalStateException(
                    "Cannot initialize context because there is already a root application context present - " +
                    "check whether you have multiple ContextLoader* definitions in your web.xml!");
        }
        Log logger = LogFactory.getLog(ContextLoader.class);
        servletContext.log("Initializing Spring root WebApplicationContext");
        if (logger.isInfoEnabled()) {
            logger.info("Root WebApplicationContext: initialization started");
        }
        long startTime = System.currentTimeMillis();
        try {
            // Store context in local instance variable, to guarantee that
            // it is available on ServletContext shutdown.
            if (this.context == null) {
                this.context = createWebApplicationContext(servletContext);
            }
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent ->
                        // determine parent for root web application context, if any.
                        ApplicationContext parent = loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
            ClassLoader ccl = Thread.currentThread().getContextClassLoader();
            if (ccl == ContextLoader.class.getClassLoader()) {
                currentContext = this.context;
            }
            else if (ccl != null) {
                currentContextPerThread.put(ccl, this.context);
            }

            if (logger.isDebugEnabled()) {
                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
            }
            if (logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
            }
            return this.context;
        }
        catch (RuntimeException ex) {
            logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
            throw ex;
        }
        catch (Error err) {
            logger.error("Context initialization failed", err);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
            throw err;
        }
    }

该方法的实现位于ContextLoaderListener的父类ContextLoader中,在此方法中我们重点可以看到这块代码:

if (this.context == null) {
    this.context = createWebApplicationContext(servletContext);
}

不难看出,这里是判断当前上下文是否为空,如果为空则调用创建上下文的方法,那么我们就可以进入到创建方法中

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        Class<?> contextClass = determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }
        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

在该方法中我们重点关注一下determineContextClass(sc)这里的实现:

protected Class<?> determineContextClass(ServletContext servletContext) {
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load custom context class [" + contextClassName + "]", ex);
            }
        }
        else {
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load default context class [" + contextClassName + "]", ex);
            }
        }
    }

这个方法的主要工作就是,获取初始化参数contextClass,如果我们在web.xml中配置了该参数,则使用ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());,即前面提到的上下文对象。本文中的实力项目中没有在web.xml中配置该参数,则会进入else处理逻辑,在这里我们可以看到有这么一句代码:

contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

这里的defaultStrategies我们可以在该类的顶部静态块中看到它的初始化逻辑:

static {
        // Load default strategy implementations from properties file.
        // This is currently strictly internal and not meant to be customized
        // by application developers.
        try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
        }
    }

这里的意思很明确,就是加载默认的配置项,读取spring默认的上下文类,全文搜索一下这个DEFAULT_STRATEGIES_PATH文件,在spring-web包下的/resources/org/springframework/web/context目录下可以找到这个文件,里面的内容为:

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

这里就是配置了默认的WebApplicationContextd的类型为XmlWebApplicationContext
接下来回到determineContextClass方法中,后面就是使用ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());来加载这个上下文对象并且返回。
回到createWebApplicationContext方法继续往下走,

if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

首先判断指定的webApplicationContext是否是ConfigurableWebApplicationContext类型,如果不是则抛出异常终止容器启动,如果是则实例化该webApplicationContext对象,这里我们就是创建XmlWebApplicationContext对象,并返回到initWebApplicationContext方法中。到此整个spring容器的初始化就完成了,也就是容器启动完成了。但是,在上面提到的内容中,并没有涉及到spring各种bean的实例化和初始化,那么回过头来看看initWebApplicationContext方法中有这么几行代码:

if (this.context instanceof ConfigurableWebApplicationContext) {
          ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
           if (!cwac.isActive()) {
            // The context has not yet been refreshed -> provide services such as
            // setting the parent context, setting the application context id, etc
            if (cwac.getParent() == null) {
              // The context instance was injected without an explicit parent ->
              // determine parent for root web application context, if any.
                 ApplicationContext parent = loadParentContext(servletContext);
                 cwac.setParent(parent);
              }
              configureAndRefreshWebApplicationContext(cwac, servletContext);
       }
}

其中的configureAndRefreshWebApplicationContext(cwac, servletContext);进入方法详情查看一下:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // The application context id is still set to its original default value
            // -> assign a more useful id based on available information
            String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
            if (idParam != null) {
                wac.setId(idParam);
            }
            else {
                // Generate default id...
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }

        wac.setServletContext(sc);
        String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
        if (configLocationParam != null) {
            wac.setConfigLocation(configLocationParam);
        }

        // The wac environment's #initPropertySources will be called in any case when the context
        // is refreshed; do it eagerly here to ensure servlet property sources are in place for
        // use in any post-processing or initialization that occurs below prior to #refresh
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
        }

        customizeContext(sc, wac);
        wac.refresh();
    }

在这个方法里我们重点看一下customizeContext(sc, wac)和wac.refresh(),customizeContext(sc, wac)这里是初始化定制的容器,在此就不做深入,我们重点看一下wac.refresh()方法,了解过spring源码的同学对这个方法就比较熟悉了,因为它就是spring的各种bean创建的入口了,下面我们进入到方法里面看一下,在这里进入到实现类的的时候回出现让选择具体的实现类,如图:


refresh实现.png

这里我们进入AbstractApplicationContext的refresh(),该方法的主要内容如下:

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

这个方法包含了spring启动的大部分操作

prepareRefresh()

包含了几个操作:
1-设置启动的时间戳
2-设置容器为未关闭状态
3-设置容器为活动状态
4-在上下文环境中初始化任何占位符属性源
5-在环境变量中验证必须的属性
6-初始化上下文事件容器(本质是一个LinkedHashSet)

obtainFreshBeanFactory()

获取BeanFactory,包含了两个操作:
1.refreshBeanFactory(),刷新beanFactory,该方法的具体实现在AbstractRefreshableApplicationContext中,如果已经存在了beanFactory,先销毁所有的单例bean,然后关闭beanFactory,接着就开始创建beanFactory,
定制beanFacotory属性(allowBeanDefinitionOverriding和allowCircularReferences),之后这开始加载bean定义-loadBeanDefinitions(beanFactory),加载bean定义这里就变得比较灵活了,不同的webApplocationContext都有自己的实现方式:


loadBeanDefinition实现类.png

此处,我们来看XmlWebApplicationContext中的loadBeanDefinitions,因为默认的容器类型是XmlWebApplicationContext。跟踪代码,一直到XmlBeanDefinitionReader中的loadBeanDefinitions(EncodedResource encodedResource)方法中有这么一段代码:

try {
    InputStream inputStream = encodedResource.getResource().getInputStream();
    try {
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
        }
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    }
    finally {
        inputStream.close();
    }
}

这里是resource就是我们在web.xml中配置的spring的相关的配置文件路径,通过doLoadBeanDefinitions(inputSource, encodedResource.getResource())来加载对应的配置文件,然后我们进入到doLoadBeanDefinitions方法去看一下:

Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

主要功能代码就是这两行,其他的可以忽略,doLoadDocument从字面上理解就是去加载文本,这个文本就是我们的spring的相关xml文件,这里就不再继续深入了,总之,就是解析spring的xml文件返回一个document对象。
registerBeanDefinitions(doc, resource)方法则是吧document解析到beanDefinition中,具体的操作可以看一下DefaultBeanDefinitionDocumentReader中的doRegisterBeanDefinitions(Element root)方法,主要有4个重要的操作:
①创建委托者createDelegate(getReaderContext(), root, parent)
②处理xml数据的前置处理preProcessXml(root),类似于bean的前置处理,这个就是留给用户扩展的口子
③解析Document到beanDefinition中parseBeanDefinitions(root, this.delegate)
④处理xml数据的后置处理postProcessXml(root),类似于bean的后置处理,这个也是留给用户扩展的口子

再细看parseBeanDefinitions(root, this.delegate)的实现,这里就是典型的委派模式,跟踪代码到BeanDefinitionParserDelegate中的parseCustomElement(Element ele, @Nullable BeanDefinition containingBd):

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        String namespaceUri = getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        }
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

从代码中可以看到,这里是根据namespaceUri来获取对应的配置处理器的,也就是说,如果用户自己创建了一个非spring内置的xml格式,需要自己定义对应的解析器来解析用户自定义的xml(当然,这里的Xml只是代表,不代表只能使用xml来进行配置,也可以通过其他格式的配置文件来进行配置,只要配合对应的解析器即可),把从配置从定义的beanName和beanDefinition存放在beanDefinitionMap(本质为ConcurrentHashMap:key为beanName,value为beanDefinition)中,loadBeanDefinition到这里就结束了

2.getBeanFactory(),获取beanFactory,这里的beanFactory是在loadBeanDefinition中创建的,这里只是获取一下,获取到之后就返回beanFactory对象

prepareBeanFactory(ConfigurableListableBeanFactory beanFactory)

prepareBeanFactory里主要包含几个内容:
①设置beanFactory的加载器:beanFactory.setBeanClassLoader(getClassLoader())
②设置beanFactory的表达式语言处理器:beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()))
③为beanFactory增加了一个默认的属性管理器:beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()))
④添加一个处理aware相关接口的beanPostProcessor扩展,这里主要是使用BeanPostProcessor的postProcessBeforeInitialization()和 postProcessAfterInitialization来做增强,在Bean初始化前和后提供更多的可操作性:beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))
⑤忽略几个接口的自动装配:默认情况下只有BeanFactoryAware会被忽略,这里添加了EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware这个几个接口被忽略
⑥设置几个接口的自动装配规则:如果为BeanFactory类型,则注入beanFactory,如果是ResourceLoader、ApplicationEventPublisher、ApplicationContext,则注入当前对象
⑦注册后置处理器ApplicationListenerDetector,检测是否有事件监听以及判断是否有对编译时AspectJ的支持,这些都是通过后置处理器来完成的,最后主要是给BeanFactory注册一些能通用的组件
⑧判断是否包含了loadTimeWeaver,有则注册对应的后置处理,并这只临时的类加载器
⑨判断是否包含environment、systemProperties、systemEnvironment,如果存在则进行单例注册

postProcessBeanFactory(beanFactory)

①添加ServletContextAwareProcessor子类的后置处理扩展
②忽略ServletContextAware、ServletConfigAware的自动装配
③注册web相关作用于bean:request、session、application,添加ServletRequest、ServletResponse、HttpSession、WebRequest的自动装配规则
④在web容器启动过程中注册web相关环境bean

invokeBeanFactoryPostProcessors(beanFactory)

调用上下文中注册为bean的工厂处理器。

registerBeanPostProcessors(beanFactory)

实例化和调用所有已注册的BeanPostProcessor bean
如果给出显式顺序,则按顺序执行
必须在应用程序bean的任何实例化之前调用
其中的详细流程包含以下几步:
获取所有类型为BeanPostProcessor的bean的name的数组
添加BeanPostProcessorChecker子类的后置处理器
分离实现了PriorityOrdered、Ordered和其他接口的BeanPostProcessors
注册实现了PriorityOrdered的BeanPostProcessors
注册实现了Ordered的BeanPostProcessors
注册常规的BeanPostProcessors
最后重新排序,然后重新注册所有的BeanPostProcessors
将检测内部bean的后处理器重新注册为ApplicationListeners(监听),并将其移动到处理器链的末尾

initMessageSource()

初始化MessageSource组件,主要为做国际化功能;消息绑定,消息解析做准备,这里就不深入了

initApplicationEventMulticaster()

初始化应用事件广播,这里我们可以这样理解,我们定制一个spring的事件监听,如实现ApplicationListener<ApplicationStartedEvent>,需要再应用启动完成后获得通知并进行我们自己的相关逻辑处理,这个时候的事件是如何通知过来的呢?那么就是在initApplicationEventMulticaster方法初始化这个事件广播,我们来看一下initApplicationEventMulticaster这个方法:

protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            this.applicationEventMulticaster =
                    beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
            if (logger.isDebugEnabled()) {
                logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        }
        else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                        APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                        "': using default [" + this.applicationEventMulticaster + "]");
            }
        }
    }

首先会查看当前beanFactory中是否包含了名为applicationEventMulticaster的bean,如果有则通过名称beanFactory中获取这个bean,赋给当前容器的applicationEventMulticaster对象,如果没有,就需要去实例化一个该对象,我们可以进入到SimpleApplicationEventMulticaster类里面进行查看,在这个类中我们重点看以下方法:

public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != null) {
            try {
                doInvokeListener(listener, event);
            }
            catch (Throwable err) {
                errorHandler.handleError(err);
            }
        }
        else {
            doInvokeListener(listener, event);
        }
    }

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            String msg = ex.getMessage();
            if (msg == null || msg.startsWith(event.getClass().getName())) {
                // Possibly a lambda-defined listener which we could not resolve the generic event type for
                Log logger = LogFactory.getLog(getClass());
                if (logger.isDebugEnabled()) {
                    logger.debug("Non-matching event type for listener: " + listener, ex);
                }
            }
            else {
                throw ex;
            }
        }
    }

当我们初始化了名为applicationEventMulticaster的事件广播后,我们很好理解的是,之后有一旦有新的事件,就会调用multicastEvent方法来广播事件,然而这个方法的实质就是调用对应的listener,即spring定义的一系列时间监听,listener.onApplicationEvent(event)这句代码就很好的印证了这一点,下面是我测试时实现的一个事件监听:

public class ApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent> {
    @Override
    public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {

        log.info("in my ApplicationStartedEvent");
    }
}

listener.onApplicationEvent(event)实际上调用的就是我们自定义的这部分代码,完成了我们想要的效果。
当然,在这里只是做了应用事件的广播初始化,还需要把对应的监听注册到广播之后才能发挥它的强大功能,至于注册动作,这个留到下面去分析。

onRefresh()

从各个实现类来看,该方法主要是初始化主题源,具体使用我也没有接触过,暂不做赘述

registerListeners()

官方注释:检查侦听器bean并注册它们
进入方法的实现可以看到:

protected void registerListeners() {
        // Register statically specified listeners first.
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        // Publish early application events now that we finally have a multicaster...
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

遍历所的监听,然后getApplicationEventMulticaster(),获取到applicationEventMulticaster,这里就是上面初始化的那个事件广播,通过addApplicationListener(listener),让广播来通知对应的监听,这就是上面未详细描述的一个事件监听注册的实现,如果有早期事件需要广播,这里就指定getApplicationEventMulticaster().multicastEvent(earlyEvent)来广播事件。

finishBeanFactoryInitialization(beanFactory)

官方注释:实例化所有剩余的(非懒加载)单例
这里是整个容器最核心的部分,因为这里包含了大部分bean的创建,依赖注入的实现逻辑,动态代理bean都在这里创建。下面开始进入这个方法。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
                @Override
                public String resolveStringValue(String strVal) {
                    return getEnvironment().resolvePlaceholders(strVal);
                }
            });
        }
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        }
        beanFactory.setTempClassLoader(null);
        beanFactory.freezeConfiguration();
        beanFactory.preInstantiateSingletons();
    }

首先关注一下beanFactory.freezeConfiguration(),这个方法在代码中有这一段注释
"Allow for caching all bean definition metadata, not expecting further changes."翻译过来就是允许缓存所有bean定义的元数据,不考虑改变,也就是冻结配置,就算此事元数据发生了改变也不参考。此处冻结了配置,也保证了后面取用beanDefiniton来创建Bean的一致性,不会再创建bean的时候因为元数据发生了改变而造成其他问题。接下来进入重点关注的方法beanFactory.preInstantiateSingletons()
preInstantiateSingletons()的方法实现位于DefaultListableBeanFactory中,之前在spring-beans重点类说明一文中有提到,DefaultListableBeanFactory乃ioc容器的始祖,这个类就是BeanFactory的具体实现,是整个spring的bean的生产车间。
preInstantiateSingletons方法主要包含两部分功能
①实例化bean
②为所有需要适用的bean执行实例化后的回调
我们先来看看是如何实例化bean的:
要创建bean就必须得拿到对应Bean的元数据,这个时候我们首先会想到在执行obtainFreshBeanFactory()方法中说到的beanDefinition,所以首先得拿到这个beanDefinition,调用方法:

RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

从beanDefiniton中可以得知,该bean是否为抽象、是否是单例、是否为是懒加载(延迟加载),如果不是抽象类,不是懒加载,是单例的情况下,在进行判断,判断bean是否为FactoryBean(FactoryBean因为它是一种Factory类型的bean,所以在获取的时候要通过&+beanName才能获取对应的FactoryBean),然后处理对需要优先创建FactoryBean进行优先创建。之后的逻辑就是创建普通的bean了,进入getBean方法查看,跟踪代码一直到doGetBean方法,该方法位于AbstractBeanFactory类中。spring所有类中,但凡是以doXx命名的方法就是真正做事的方法了,doGetBean说明这个方法里在进行bean的创建了。我们来看一下这个方法中的这段代码:

String[] dependsOn = mbd.getDependsOn();
    if (dependsOn != null) {
        for (String dep : dependsOn) {
            if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
            }
            registerDependentBean(dep, beanName);
            getBean(dep);
        }
    }

这段代码的意思是获取当前bean的依赖mbd.getDependsOn(),如果有依赖其他的bean,就判断当前的bean和依赖的bean有没有循环依赖的关系,如果有则抛出异常,如果没有就去注册依赖关系,然后创建依赖的bean,这里的getBean(dep)就是一个递归调用,循环创建bean,这里能很好的体现出依赖注入了。接下来我们继续看到这段代码:

 sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
    @Override
    public Object getObject() throws BeansException {
        try {
            return createBean(beanName, mbd, args);
        }catch (BeansException ex) {
            destroySingleton(beanName);
            throw ex;
        }
    }
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

注意这里的ObjectFactory,等会儿在后面还会用到,得到单例实例后进入到getObjectForBeanInstance方法中一直跟踪代码到FactoryBeanRegistrySupport类中的doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)这个方法中:

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
            throws BeanCreationException {
        Object object;
        try {
            if (System.getSecurityManager() != null) {
                AccessControlContext acc = getAccessControlContext();
                try {
                    object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                        @Override
                        public Object run() throws Exception {
                                return factory.getObject();
                            }
                        }, acc);
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                object = factory.getObject();
            }
        }
        catch (FactoryBeanNotInitializedException ex) {
            throw new BeanCurrentlyInCreationException(beanName, ex.toString());
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
        }
        // Do not accept a null value for a FactoryBean that's not fully
        // initialized yet: Many FactoryBeans just return null then.
        if (object == null && isSingletonCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(
                    beanName, "FactoryBean which is currently in creation returned null from getObject");
        }
        return object;
    }

这里最终返回的对象名称叫object,从代码中我们可以看到,其实最终返回的都是factory.getObject()获得的对象,这个factory就是刚刚在方法中传进来的shareInstance实例,这个时候实际上调用的是实例的getObject()方法即:

@Override
public Object getObject() throws BeansException {
    try {
        return createBean(beanName, mbd, args);
    }catch (BeansException ex) {
            destroySingleton(beanName);
            throw ex;
    }
}

看到这里的creatBean我们就放心了,它最终还是回到了创建Bean的正道当中来了,createBean的具体实现在AbstractAutowireCapableBeanFactory类中,跟踪代码,进入到doCreateBean方法,然后再进入createBeanInstance方法,这个方法里提供了两种创建bean实例的方式,
一种是autowireConstructor(beanName, mbd, null, null):这个方法作用是获取被包装后的bean,包装后的对象是BeanWrapper对象,这个对象的实现类是BeanWrapperImpl。其中包含被封装后待处理的bean,和设置bean属性的属性编辑器
另一种是instantiateBean(beanName, mbd):即加载实例化bean
先来看容易理解一点的instantiateBean,具体的实现代码:

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (bd.getMethodOverrides().isEmpty()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                                @Override
                                public Constructor<?> run() throws Exception {
                                    return clazz.getDeclaredConstructor((Class[]) null);
                                }
                            });
                        }
                        else {
                            constructorToUse =  clazz.getDeclaredConstructor((Class[]) null);
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

这里就判断了是否存在override的方法,如果不存在则获取构造器,然后通过构造器来.newInstance(args),获得bean的实例对象
如果存在方法的覆写,则必须生成CGLIB子类,这里我们就能感受到,基于cglib的动态代理类是如何生成的了,具体的生成方式我们可以到CglibSubclassingInstantiationStrategy类中查看,具体创建代理类的方法:

private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(beanDefinition.getBeanClass());
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            if (this.owner instanceof ConfigurableBeanFactory) {
                ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
                enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
            }
            enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
            enhancer.setCallbackTypes(CALLBACK_TYPES);
            return enhancer.createClass();
}

这里就不继续深入下去了,有兴趣的朋友可以专门研究一下。
不管是哪一种方式来创建bean的实例,最终返回一个beanInstance包装过后的包装对象BeanWrapper,随即调用populateBean方法从beanWrapper中获取beanDefinition填充bean实例中的属性,然后还会再调用initializeBean(beanName, exposedObject, mbd),这个方法就主要做一些扩展的功能了:
①如果bean实现了Aware接口,就可以执行一些增强的方法,如:setBeanName,setBeanClassLoader,setBeanFactory
②执行bean初始化的前置处理(扩展)
③执行bean的初始化方法,在这里面还会执行属性设置的后置处理(扩展)afterPropertiesSet()
④执行bean初始化完成后的后置处理(扩展)
到此,bean的创建流程就完成了,最后为当前bean的销毁提供策略和实现 registerDisposableBeanIfNecessary,整个bean创建流程结束。

finishRefresh()

官方注释:发布相应的事件
主要操作:
①初始化此上下文的生命周期处理器
②getLifecycleProcessor().onRefresh();生命周期处理器处理器onRefresh方法看到其实是执行的DefaultLifecycleProcessor中的startBeans,就是启动所有的bean,在启动完成后则把DefaultLifecycleProcessor中的running设置为true表示应用启动了
③发布ContextRefreshedEvent类型的事件
④最后会检查一个配置spring.liveBeansView.mbeanDomain是否存在,有就会创建一个MBeanServer

resetCommonCaches()

重置常用的缓存,这里应该跟上面提到的beanFactory.freezeConfiguration()对应起来了,这里因为bean已经创建完成,不会再使用到bean的相关元数据了,可以刷新一下对应缓存,也不会印象bean的正常工作。

end

至此,整个spring-web容器就启动完成了,整个流程的分析也到此结束,可能其中有理解得不正确或遗漏的地方,我也将继续探究,并实时作出修改。

总结

spring经历的这么年的发展,其功能的强大不可言表,我也会在以后的学习中进一步去探究跟多的功能原理,也建议朋友们去深入理解一下,从中肯定能获益良多!
最后附上一个简单的容器启动流程图:


spring容器启动流程图.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342