SpringMVC启动流程
文章目录
1.启动前的相关配置
- spring容器相关配置
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">
<context:component-scan base-package="com.anyi"></context:component-scan>
<bean id="user" class="com.anyi.beans.User">
<property name="username" value="anyi"></property>
<property name="password" value="123"></property>
</bean>
</beans>
- springmvc容器相关配置
java
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
</beans>
- web.xml配置
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">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-config.xml</param-value>
</context-param>
<servlet>
<servlet-name>mvc-test</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-test</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
2.启动流程
1.启动tomcat容器
使用idea会自动启动tomcat容器,调用spring容器的初始化方法
2.创建spring容器
- 在初始化tomcat容器之前,必须先启动tomcat容器,启动之后tomcat会调用初始化方法initWebApplicationContext()来对容器进行初始化
java
@Override
public void contextInitialized(ServletContextEvent event) {
// 初始化web容器,先开始初始化的spring容器
initWebApplicationContext(event.getServletContext());
}
- 在这里会调用configureAndRefreshWebApplicationContext() 方法来进行spring容器的初始化
java
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
// 设置一个容器id
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()));
}
}
// 设置servlet上下文
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
// 设置spring配置文件路径
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);
}
// 可以定制化context
customizeContext(sc, wac);
// 调用刷新方法
wac.refresh();
}
- 刷新完成后,就完成了spring容器的初始化
3.创建springmvc容器
上述完成了tomcat、spring容器的创建和启动,接下来就是springmvc容器的启动了,而springmvc中最重要的就是Servlet,而Servlet的生命周期包含三个init、service、destroy。
- init方法调用是在DispatcherServlet的父类HttpServletBean中调用init()方法
java
@Override
public final void init() throws ServletException {
// 调用初始化springmvc容器
// Set bean properties from init parameters.
// 从web.xml中获取servlet的配置文件
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// 初始化ServletBean
// Let subclasses do whatever initialization they like.
initServletBean();
}
- 在initServletBean方法中会调用initWebApplicationContext()来进行springmvc容器的初始化
java
protected WebApplicationContext initWebApplicationContext() {
// 获取父容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
...
// 进行容器刷新
configureAndRefreshWebApplicationContext(cwac);
// 判断是否有事件发布
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
...
return wac;
}
- 调用configureAndRefreshWebApplicationContext()方法来刷新容器
java
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// id 验证
...
// 设置一些属性值
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// 放入一个ContextRefreshListener监听器
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// 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(getServletContext(), getServletConfig());
}
// 可以进行拓展修改
postProcessWebApplicationContext(wac);
// 设置一些初始化器
applyInitializers(wac);
// 开始容器刷新
wac.refresh();
}
- 调用AbstractApplicationContext类中的refresh()方法来创建BeanFactory等容器刷新工作,详细见spring篇
- 在完成刷新后回到用一个方法finishRefresh()
java
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
// 清理缓存
clearResourceCaches();
// Initialize lifecycle processor for this context.
// 实例化生命周期处理器
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
// 发布事件
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
- 这里会调用publishEvent()方法,由多播器来广播发布一个容器已经完成刷新的事件,会调用具体的监听器来进行处理。
java
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
...
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 调用多播器广播事件,让监听器来处理
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
...
}
- 遍历所有的监听器,最终会由我们前面注册的一个ContextRefreshListener监听器来进行处理,最终会调用他的onApplicationEvent方法
java
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
- 调用DispatcherServlet类中onRefresh(event.getApplicationContext())方法来完成springmvc九大模块的实例化
- 初始化 MultipartResolver:主要用来处理文件上传
- 初始化 LocaleResolver:主要用来处理国际化配置,基于URL参数的配置(AcceptHeaderLocaleResolver)
- 初始化 ThemeResolver:主要用来设置主题
- 初始化 HandlerMappings:映射器,用来完成request和controller的对应
- 初始化 HandlerAdapters:处理适配器,主要包含Http请求处理适配器,简单控制处理适配器,注解方法处理适配器
- 初始化 HandlerExceptionResolvers:基于HandlerExceptionResolver接口的异常处理
- 初始化RequestToViewNameTranslator:当controller处理器方法没有返回一个View对象或者逻辑视图名称,并且方法中没有直接往response的输入流中写入数据的时候
- 初始化 ViewResolvers:将ModelAndView选择合适的视图进行渲染处理器
- 初始化 FlashMapManager:提供请求存储属性,可供其他请求使用
java
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
// 初始化 MultipartResolver:主要用来处理文件上传
initMultipartResolver(context);
// 初始化 LocaleResolver:主要用来处理国际化配置,基于URL参数的配置(AcceptHeaderLocaleResolver)
initLocaleResolver(context);
// 初始化 ThemeResolver:主要用来设置主题
initThemeResolver(context);
// 初始化 HandlerMappings:映射器,用来完成request和controller的对应
initHandlerMappings(context);
// 初始化 HandlerAdapters:处理适配器,主要包含Http请求处理适配器,简单控制处理适配器,注解方法处理适配器
initHandlerAdapters(context);
// 初始化 HandlerExceptionResolvers:基于HandlerExceptionResolver接口的异常处理
initHandlerExceptionResolvers(context);
// 初始化RequestToViewNameTranslator:当controller处理器方法没有返回一个View对象或者逻辑视图名称,并且方法中没有直接往response的输入流中写入数据的时候
initRequestToViewNameTranslator(context);
// 初始化 ViewResolvers:将ModelAndView选择合适的视图进行渲染处理器
initViewResolvers(context);
// 初始化 FlashMapManager:提供请求存储属性,可供其他请求使用
initFlashMapManager:(context);
}
- 至此完成springmvc的所有启动流程,可以等待请求进入了。