传统的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
// 如果配置类上有@Import注解,则导入的配置类也会被解析
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是同一个对象。