BeanDefinition的解析时机

传统的SpringMVC项目允许标签和注解(@Configration)同时存在,从标签和注解中解析BeanDefinition的时机是不一样的。

启动流程

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 这个监听类会捕获Tomcat启动时的ServletContextEvent事件,调用contextInitialized,作为父容器启动的入口 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 父容器启动时,加载的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-core.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>mvc</servlet-name>
        <!-- DispatcherServlet加载过程中会完成子容器的创建 -->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 子容器加载的配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:beans.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

容器启动流程

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			prepareRefresh();

			// 从标签中解析BeanDefinition
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			prepareBeanFactory(beanFactory);

			try {
				postProcessBeanFactory(beanFactory);

				// 从@Configration注解类中解析BeanDefinition
				invokeBeanFactoryPostProcessors(beanFactory);

				// 仅仅是注册,并没有执行回掉方法
				// 在finishBeanFactoryInitialization中真实创建Bean时才会执行回调方法
				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();

				// 此时已经完成所有BeanDefinition的解析,这个方法会创建Bean实例(懒加载Bean除外)
				finishBeanFactoryInitialization(beanFactory);

				// 发布刷新事件ContextRefreshedEvent
				// 被org.springframework.web.servlet.FrameworkServlet.ContextRefreshListener 接受,调用org.springframework.web.servlet.DispatcherServlet#initStrategies开始初始化各种组件
				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();
			}
		}
	}

从标签解析

从标签中解析BeanDefinition是在org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory中完成的。
在这里插入图片描述

父容器配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <mvc:annotation-driven/>
    <context:component-scan base-package="com.example"/>

</beans>

以mvc标签为例,找到解析这个标签的具体类。在spring-webmvc的jar文件中的META-INF/spring.handlers中,是org.springframework.web.servlet.config.MvcNamespaceHandler

找到标签解析类

可以看到这个类对mvc的所有标签都做了解析
在这里插入图片描述
以annotation-driven标签为例,进入到org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser#parse,可以看到Spring自动添加了RequestMappingHandlerMapping、RequestMappingHandlerAdapter。

@Configration 解析

对@Configration 的解析是在org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors中完成的,这个函数负责执行BeanFactoryPostProcessor

在这里插入图片描述
被@Configration注释的类会被Spring增强(org.springframework.context.annotation.ConfigurationClassPostProcessor#enhanceConfigurationClasses),在后续通过BeanDefinition实例化Bean的时候,是从代理类中获取Bean,代理类会保证多次执行得到的Bean是同一个对象。
在这里插入图片描述