传统的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是同一个对象。