寻找起源
先来看springmv中,web.xml的配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55<listener>
<description>Spring监听器</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- ContextLoaderListener初始化Spring上下文时需要使用到的contextConfigLocation参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 配置spring.xml和spring-mybatis.xml这两个配置文件的位置,固定写法 ,classpath:dubbo-provider.xml-->
<param-value>classpath:spring.xml,classpath:spring-mybatis.xml,classpath:spring-redis.xml,classpath:dubbo-provider.xml</param-value>
</context-param>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置log4j配置文件路径 -->
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:property/log4j.properties</param-value>
</context-param>
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10000</session-timeout>
</session-config>
web.xml加载的顺序
- context-param
初始化运行环境需要的配置信息
- listener
监听器
- filter
过滤器
- servlet
处理
构造一个 无参ContextLoadListener
1 | Alice->Bob: Hello Bob, how are you? |
类图
- 根据加载顺序构造一个无参的
ContextLoadListener
初始化spring的解析策略
实际就是将WebApplicationContext
初始化为XmlWebApplicationContext
1
2
3
4
5
6
7# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.
org.springframework.web.context.WebApplicationContext
=
org.springframework.web.context.support.XmlWebApplicationContext
- 因为
ContextLoadListener
继承ServletContextListener
所以在tomcat容器启动的时候,会调用contextInitialized
。详细说下 ContextLoader.initWebApplicationContext
1 | public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { |
configureAndRefreshWebApplicationContext的处理逻辑1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
.......
// 获取web.xml中标签是context-param并且 param-name 是contextConfigLocation的配置值
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
// 奖这些配置文件缓存到webApplicationContext中
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);
}
// 定制化设置applicationcontext
/*
* 步骤
* 1、判断web.xml中是否配置了 globalInitializerClasses,contextInitializerClasses
* 如果有的话就他们放到 List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>中
* 2、循环1的结果实例化每个class后放到 contextInitializers
* 3、对contextInitializers排序
* 4、循环3结果,然后对每个元素调用initialize(wac);
*
* */
customizeContext(sc, wac);
wac.refresh();
}
refresh的步骤
步骤分析
1、prepareRefresh()
准备刷新,里面主要是加载一些系统配置信息1
2
3// Initialize any placeholder property sources in the context environment.
initPropertySources();
// 具体实现是返回null
2、ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()
构建beanFactory信息1
2
3
4
5
6
7
8
9protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
1 | protected final void refreshBeanFactory() throws BeansException { |
loadBeanDefinitions过程
2.1 装载context-param文件信息
1 | protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { |
2.2 实例化bean信息
3、prepareBeanFactory
如下图所示,往beanfactory中注册,排除一些系统环境必须的bean信息
4、postProcessBeanFactory
注册后置处理器1
2
3
4
5
6
7
8
9
10
11
12/**
* Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
*/
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
对 ServletContextAwareProcessor
的说明:Web application contexts will automatically register this with their
underlying bean factory. Applications do not use this directly.
5、invokeBeanFactoryPostProcessors
Instantiate and invoke all registered BeanFactoryPostProcessor beans,
实例化并注册所有注册的BeanFactoryPostProcessor
5.1 获取所有beanFactoryPostprocess
1 | /** |
5.2 注入BeanFactoryPostProcessors
invokeBeanFactoryPostProcessors
它有个先后顺序。首先注入实现了PriorityOrdered
,然后注入实现了Ordered