抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SpringMVC-初始化启动过程

SpringBoot MVC启动

​ Springmvc 是基于 servlet 规范来完成的一个请求响应模块,也是 spring 中比较大的一个 模块,现在基本上都是零 xml 配置了,采用的是约定大于配置的方式,所以我们的 springmvc 也是采用这种零 xml 配置的方式。要完成这种过程,要解决两个问题 :

  1. 取代 web.xml 配置
  2. 取代 springmvc.xml 配置

WebApplicationInitializer

SpringServletContainerInitializer 类使用了@HandlesTypes(WebApplicationInitializer.class)注解。表明给onStart方法传递了类路径下所有实现了WebApplicationInitializer类。spring将初始化所有这些具体实现类,并调用他们的WebApplicationInitializer#onStartup(servletContext)方法。这些类可以自由地在onStart方法中,通过可编程方式注册和初始化servlet组件。

​ 在 servlet 中有一个规范,就是当 servlet 容器启动的时候会根据 spi 规范加载 META-INF/services 文件夹下面的 javax.servlet.ServletContainerInitializer 文件,该文件下面的 类会实现 javax.servlet.ServletContainerInitializer 接口。如图:

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
//会将所有的WebApplicationInitializer接口的类扫描到注入到webAppInitializerClasses集合中
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {

List<WebApplicationInitializer> initializers = new LinkedList<>();

if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
} catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}

if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}

servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}

}

​ 该类在启动的时候会被 servlet 容器实例化,然后调用 onStartup 方法,并且 servlet 容 器会收集实现了@HandlesTypes 注解里面的接口的类,并且做为入参传入到 onStartup 方法 中,我们拿到 set 容器中的类就可以反射调用接口里面的方法了,这是 servlet 规范,该规范 就能保证 servlet 容器在启动的时候就会完成这些操作。Springmvc 就借助这一点完成了取代 web.xml 的工作。

收集的是实现了 WebApplicationInitializer 接口的类,在 springmvc 工程中实现该接口的类有一下一下:

AbstractContextLoaderInitializer

完成org.springframework.web.context.ContextLoaderListener的注册,替换了

org.springframework.web.context.ContextLoaderListener
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
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {

/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());


@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}


protected void registerContextLoaderListener(ServletContext servletContext) {
//创建spring上下文,注册了SpringContainer
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
//创建监听器
/*
形如这种配置
* <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
*
* */
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}


@Nullable
protected abstract WebApplicationContext createRootApplicationContext();


@Nullable
protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
return null;
}

}
AbstractDispatcherServletInitializer

完成DispatcherServlet的注册,替换了

DispatcherServlet org.springframework.web.servlet.DispatcherServlet 1
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
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {


public static final String DEFAULT_SERVLET_NAME = "dispatcher";


@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
//注册DispatcherServlet
registerDispatcherServlet(servletContext);
}

protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");

//创建springmvc的上下文,注册了MvcContainer类
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

//创建DispatcherServlet
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
/*
* 如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
* 如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,
* 值越小,servlet的优先级越高,就越先被加载
*/
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
//钩子方法获取Servlet监听器
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
//注册servletContext监听器
registerServletFilter(servletContext, filter);
}
}
//扩展点对注册的servlet做修改空方法
customizeRegistration(registration);
}
...
}
小结

​ 到这里为止tomcat启动会调用WebApplicationInitializer#onStartup方法,会加载所有实现了WebApplicationInitializer接口,然后调用onStartup方法,AbstractContextLoaderInitializer完成了ContextLoaderListener的注册,AbstractDispatcherServletInitializer完成了DispatcherServlet的注册。

ContextLoaderListener启动

ContextLoaderListener通过AbstractContextLoaderInitializer注册完成后会调用contextInitialized进行初始化,调用contextDestroyed进行销毁

contextInitialized

初始化ContextLoaderListener,调用initWebApplicationContext初始化spring的父容器

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@Override
public void contextInitialized(ServletContextEvent event) {
//初始化Spring的web容器
initWebApplicationContext(event.getServletContext());
}

/**
* 初始化Spring的web容器 并返回WebApplicationContext
*
* @param servletContext current servlet context
* @return the new WebApplicationContext
* @see #ContextLoader(WebApplicationContext)
* @see #CONTEXT_CLASS_PARAM
* @see #CONFIG_LOCATION_PARAM
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//如果servletContext中有spring父容器报错
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}

servletContext.log("Initializing Spring root WebApplicationContext");
Log logger = LogFactory.getLog(ContextLoader.class);
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();

try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
//创建spring容器
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
// 将容器添加到ServletContext上下文
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
} else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}

if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}

return this.context;
} catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}
configureAndRefreshWebApplicationContext

调用refresh 完成spring容器的启动

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
/**
* 调用refresh 完成spring容器的启动
*
* @param wac
* @param sc
*/
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
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()));
}
}

wac.setServletContext(sc);
//设置CONFIG_LOCATION_PARAM参数
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
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);
}

customizeContext(sc, wac);
//调用refresh方法完成spring容器启动
wac.refresh();
}

小结

​ 到此完成spring父容器的启动

DispatcherServlet启动

AbstractDispatcherServletInitializer完成了DispatcherServlet的注册,然后根据servlet规范会调用init方法进行初始化

DispatcherServlet的继承关系

HttpServletBean#init

Servlet 的初始化方法,servlet注册完成后tomcat会自动调用该方法,完成servlet的初始化

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
@Override
public final void init() throws ServletException {

// Set bean properties from init parameters.
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;
}
}

// Let subclasses do whatever initialization they like.
//初始化ServletBean
initServletBean();
}
FrameworkServlet#initServletBean

初始化ServletBean,开始初始化Servlet的父容器

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
/**
* 初始化ServletBean
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();

try {
//初始化Spring容器
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
} catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}

if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}

if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
initWebApplicationContext
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
protected WebApplicationContext initWebApplicationContext() {
//这里会从servletContext中获取到父容器,就是通过监听器加载的容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;

if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
//设置父子容器
cwac.setParent(rootContext);
}
//容器加载
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}

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);
}
}

if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}

return wac;
}
configureAndRefreshWebApplicationContext

调用refresh 完成spring容器的启动

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
/**
* 调用refresh 完成spring容器的启动
*
* @param wac
*/
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
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
if (this.contextId != null) {
wac.setId(this.contextId);
} else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}

wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
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);
//调用Spring容器的核心方法refresh
wac.refresh();
}
DispatcherServlet#onRefresh

完成SpringMVC启动初始化工作

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
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* <p>
* 初始化SpringMVC的一些策略,也是springMVC主要的组件
*/
protected void initStrategies(ApplicationContext context) {
//文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析
initMultipartResolver(context);
//本地化解析
initLocaleResolver(context);
//主题解析
initThemeResolver(context);
//通过HandlerMapping,将请求映射到处理器
initHandlerMappings(context);
//通过HandlerAdapter支持多种类型的处理器
initHandlerAdapters(context);
//如果执行过程中遇到异常,将交给HandlerExceptionResolver来解析
initHandlerExceptionResolvers(context);
//直接解析请求到视图名
initRequestToViewNameTranslator(context);
//通过viewResolver解析逻辑视图到具体视图实现
initViewResolvers(context);
//flash映射管理器
initFlashMapManager(context);
}

小结

​ 到此为止完成spring子容器的启动,并从ServletContext中获取ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE父容器设置进行,绑定父子容器方法,调用onRefresh完成SpringMVC的初始化工作。

SpringMVC的初始化

getDefaultStrategy

从DispatcherServlet.properties配置文件中读取的默认的配置

1
2
3
4
5
6
7
8
9
10
11
/**
* 获取默认的配置策略
*/
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
List<T> strategies = getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException(
"DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
}
return strategies.get(0);
}
getDefaultStrategies

获取属性中的配置

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
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
private static final Properties defaultStrategies;

static {
//启动时加载DispatcherServlet.properties的属性文件
try {
//获取DispatcherServlet.properties的资源对象
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
//读取DispatcherServlet.properties属性文件
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
//获取需要的类型
String key = strategyInterface.getName();
//从属性文件中获取默认的策略
String value = defaultStrategies.getProperty(key);
if (value != null) {
//将“,”分割的字符串转换成数组
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
//遍历所有的className
for (String className : classNames) {
try {
//返回获取Class
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
//通过Spring创建实例
Object strategy = createDefaultStrategy(context, clazz);
//添加到策略列表
strategies.add((T) strategy);
} catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
} catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
//返回策略
return strategies;
} else {
return new LinkedList<>();
}
}
DispatcherServlet.properties

DispatcherServlet.properties 默认配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

initMultipartResolver

文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 初始化multipartResolver文件上传处理器
*/
private void initMultipartResolver(ApplicationContext context) {
try {
//从Spring容器获取id是multipartResolver或者类型是MultipartResolver的类
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.multipartResolver);
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
}
} catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
//默认multipartResolver的值为空
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}
MultipartResolver 接口

MultipartResolver接口定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface MultipartResolver {
/**
* 检查请求头是否包含文件流上传
*/
boolean isMultipart(HttpServletRequest request);

/**
* 文件流上传请求解析方法,解析后封装在 MultipartHttpServletRequest 对象中
*/
MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;

/**
* 清除文件流
*/
void cleanupMultipart(MultipartHttpServletRequest request);

}
常用的实现类

配置例子

CommonsFileUploadSupport实现了 MultipartResolver 接口

1
2
3
4
5
6
7
8
//上传文件配置
@Bean(name = "multipartResolver")
public CommonsFileUploadSupport commonsFileUploadSupport(){
CommonsFileUploadSupport resolver = new CommonsMultipartResolver();
resolver.setMaxInMemorySize(40960);
resolver.setMaxUploadSize(10485760000L);
return resolver;
}

initLocaleResolver

本地化解析,支持web应用程序国际化。

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
/**
* 本地化解析,支持web应用程序国际化,在springMVC应用程序中,用户的区域是通过区域解析器事变的
* spring采用的是默认区域解析器 AcceptHeaderLocaleResolver ,通过检验HTTP请求的accept-language头部来解析区域
* 这个头部是有用户的浏览器根据底层操作系统的区域设置进行设定,这个区域解析器无法改变用户的区域,因为无法修改用户
* 操作系统的区域设置
*/
private void initLocaleResolver(ApplicationContext context) {
try {
//在配置多语言本地化时会注入bean名称为localeResolver,默认实现的类有FixedLocaleResolver ,SessionLocaleResolver ,CookieLocaleResolver, AcceptHeaderLocaleResolver
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.localeResolver);
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
} catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
//如果没有配置使用DispatcherServlet.properties默认配置的设置
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}

DispatchServlet中定义一个静态的容器初始化默认defaultStrategies方法块,如果用户没有自定义的话,就使用默认的,springMVC国际化提供了四个国际化的实现的类AcceptHeaderLocaleResolver(默认),FixedLocaleResolver、CookieLocaleResolver和SessionLocaleResolver。

initThemeResolver

动态样式支持

主题的实现原理:大概就是把网站版面的css样式表和图片之类的文件和网站的程序进行解耦,程序读取theme的持久化配置,然后找到相应的css样式表和图片,配置网站版面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 动态样式支持
*/
private void initThemeResolver(ApplicationContext context) {
try {
//需要在配置文件中注入bean名称为themeResolver的,FixedThemeResolver, SessionThemeResolver和CookieThemeResolver
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.themeResolver);
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());
}
} catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
//如果没有配置使用DispatcherServlet.properties默认配置的设置
this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
"': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
}
}
}

​ 要在程序中使用主题,必须设置一个org.springframework.ui.context.ThemeSource的实现类。SpringMVC IOC容器本身实现了ThemeSource,这个类只是简单的把责任代理给了一个特定的实现类,默认情况下这个代理类是:org.springframework.ui.context.support.ResourceBundleThemeSource,这个实现类可以从classpath目录下载入一个properties文件。如设置setBasenamePrefix、setDefaultEncoding等。

配置例子

如:SpringMVC中一套主题对应一个cool.properties文件,该文件在classpath根目录下, 这个文件列出了主题组成的资源:

1
2
styleSheet=/themes/cool/style.css
background=/themes/cool/img/coolBg.jpg

properties文件中的key是指视图文件中元素的名称,value值主题存放的位置。jsp中可以通过jsp文件可以通过spring:theme来访问这个key,然后找到value(对应的主题)。

ResourceBundleThemeSourceuses(默认的ThemeSource)的作用是根据主题名找到具体的主题,这个例子中就是找到配置文件themedemo.properties中的,默认情况下ResourceBundleThemeSource使用空的前缀名称,所以,classpath根目录下的properties文件会被载入。如果想制定位置,则可以使用basenamePrefix

找到主题文件后,如何去解析?此时就出现了ThemeResolver。

SpringMVC中有实现主题类有3个:

  1. FixedThemeResolver(默认):固定格式的theme,不能在系统运行时动态更改theme。
  2. SessionThemeResolver:可在运行中通过更改cookie中的相应的key值来动态调整theme的值。
  3. CookieThemeResolver:可在运行中通过更改session中的相应的key值来动态调整theme的值

主题加载策略和initLocaleResolver类似,也是如果用户没有自定义就采用默认的方式。默认的方式为FixedThemeResolver。

application context .xml配置文件
1
2
3
4
5
6
<bean class="org.springframework.ui.context.support.ResourceBundleThemeSource" id="themeSource"> 
<property name="basenamePrefix" value="themes/"></property>
</bean>
<bean id="themeResolver" class="org.springframework.web.servlet.theme.SessionThemeResolver">
<property name="defaultThemeName" value="red" />
</bean>

简单叙述为:ThemeResolver找到themes/目录下的所有properties中,然后SessionThemeResolver在发生改变主题请求后来解析主题。

附:如果需要根据用户请求来改变主题,则需要使用ThemeChangeInterceptor拦截器来改变主题。(拦截器有关的知识可以看本文下面的handlerMapping)

initHandlerMappings

initHandlerMappings初始化HandlerMapping,HandlerMapping的工作就是为每个请求找到合适的请求找到一个处理器handler

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
/**
* Initialize the HandlerMappings used by this class.
* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
* we default to BeanNameUrlHandlerMapping.
* 会加载HandlerMapping,默认使用BeanNameUrlHandlerMapping
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;

if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
//默认加载所有的HandlerMapping,
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
//数组中含有多个HandlerMapping
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
try {
//否则值加载id是handlerMapping或者类型是HandlerMapping的HandlerMapping
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}

// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
//如果没有合适的handlerMappings获取dispatchServlet.properties默认的HandlerMapping
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}

HandlerMappings是一个List类型数据,也就是说初始化可以存放多种Mapping,和其他几种组件加载方式一样。如果用户没有在配置文件选择 HanderMapping则会到DispatcherServlet.properties文件获取:

1
2
3
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping

​ HanderMapping接口只有一个方法getHandler(request),它的作用是获取HandlerExecutionChain,HandlerExecutionChain对象封装了一个handler处理对象和一些interceptors,也就是说每一次请求要执行hander与这些拦截器相关的逻辑。

initHandlerAdapters

initHandlerAdapters是初始化HandlerAdapter,HandlerAdapter是真正调用Controller操作的类

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
/**
* Initialize the HandlerAdapters used by this class.
* <p>If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
* we default to SimpleControllerHandlerAdapter.
*
* 初始化HandlerAdapters,默认使用的是SimpleControllerHandlerAdapter
*/
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;

if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
//数组中含有多个HandlerAdapters
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
} else {
try {
//否则值加载id是handlerAdapter或者类型是HandlerAdapter的HandlerAdapter
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
} catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}

// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
if (this.handlerAdapters == null) {
//如果没有合适的HandlerAdapter获取dispatchServlet.properties默认的HandlerAdapter
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}

与HandlerMappings一样,HandlerAdapters是一个List类型数据,默认的HandlerAdapters为HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter,RequestMappingHandlerAdapter,HandlerFunctionAdapter如下:

1
2
3
4
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter

​ SpringMVC 中通过Handler来找到支持它的HandlerAdapter,找到Handler对应的HandlerAdapter后,处理这个Hander(也就是处理这个请求)。其中AnnotationMethodHandlerAdapter已经被废除。

​ HandlerAdapter字面上的意思就是处理适配器,它的作用用一句话概括就是调用具体的方法对用户发来的请求来进行处理。当handlerMapping获取到执行请求的controller时,DispatcherServlte会根据controller对应的controller类型来调用相应的HandlerAdapter来进行处理。

一个误区

​ 由于在项目中常见也是常有的处理器用的是配置的方式来做的,因此很容易以为Controller接口就是所有的处理器的接口,眼里就只有Controller了。处理器根本就不只有Controller这一种。还有HttpRequestHandler,Servlet等处理器。

RequestMappingHandlerMapping 会把Controller里面带有@RequestMapping注解的方法都加到一个容器里面,然后RequestMappingHandlerAdapter根据里面的自定义配置可以对经过这些方法的请求的数据做一些额外的处理。

SimpleControllerHanderAdapter 主要用于用于自定Controller接口的handleRequest,我们还记得刚开始学习Controller时候,自定Controller要么继承AbstractController,要么implements Controller接口。

HttpRequestHandlerAdapter 处理HttpRequestHandler类型的Handler,如: SpringMVC中请求经过dispatcherServlet时并不能找到资源,当设置了默认的servlet时候,就会被DefaultServletHttpRequestHandler处理。

initHandlerExceptionResolvers

initHandlerExceptionResolvers是初始化HandlerExceptionResolver,用来操作异常,接下来我们会对其进行详细分析

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
/**
* 初始化HandlerExceptionResolver
*/
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;

if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
//默认加载所有的HandlerExceptionResolver
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
//数组中含有多个HandlerExceptionResolver
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
} else {
try {
//否则值加载id是handlerExceptionResolver或者类型是HandlerExceptionResolver的HandlerExceptionResolver
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
} catch (NoSuchBeanDefinitionException ex) {
// Ignore, no HandlerExceptionResolver is fine too.
}
}

// Ensure we have at least some HandlerExceptionResolvers, by registering
// default HandlerExceptionResolvers if no other resolvers are found.
if (this.handlerExceptionResolvers == null) {
//如果没有合适的HandlerExceptionResolver获取dispatchServlet.properties默认的HandlerExceptionResolver
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}

​ 由对象实现的接口,可以解析在处理程序映射或执行期间抛出的异常,尤其是在视图解析是发生的异常错误。 HandlerExceptionResolver实现者通常在应用程序上下文中注册为bean。 错误视图类似于JSP错误页面,但可以与任何类型的异常一起使用,包括任何已检查的异常,以及特定处理程序的潜在细粒度映射。

initRequestToViewNameTranslator

initRequestToViewNameTranslator是初始化到ViewName的处理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 返回一个对应的视图名称
*/
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
//默认加载id是viewNameTranslator或者类型是RequestToViewNameTranslator的RequestToViewNameTranslator
this.viewNameTranslator =
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.viewNameTranslator.getClass().getSimpleName());
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.viewNameTranslator);
}
} catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
//如果没有合适的RequestToViewNameTranslator获取dispatchServlet.properties默认的RequestToViewNameTranslator
this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
if (logger.isTraceEnabled()) {
logger.trace("No RequestToViewNameTranslator '" + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
"': using default [" + this.viewNameTranslator.getClass().getSimpleName() + "]");
}
}
}

它主要与视图解析有关,RequestToViewNameTranslator主要是获取请求中的viewName,然后可以根据这个viewName获取ModelAndView对象。

RequestToViewNameTranslator接口定义:

1
2
3
4
5
6
7
8
9
10
11
12
public interface RequestToViewNameTranslator {

/**
* Translate the given {@link HttpServletRequest} into a view name.
* @param request the incoming {@link HttpServletRequest} providing
* the context from which a view name is to be resolved
* @return the view name, or {@code null} if no default found
* @throws Exception if view name translation fails
*/
@Nullable
String getViewName(HttpServletRequest request) throws Exception;
}

简单的来说就是根据request请求获取来组装视图名称,仅此而已。

在DispatcherServlet的doDispatch函数中会设置默认的视图名:

1
2
3
4
5
6
7
// 设置默认的视图名称
applyDefaultViewName(processedRequest, mv);
applyDefaultViewName中会判断ModelAndView的hasView为空时,就设置viewName:

if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}

initViewResolvers

initViewResolvers初始化View的解析器

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
/**
* 初始化viewResolver
*/
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;

if (this.detectAllViewResolvers) {
// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
//默认加载所有的ViewResolver
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
//数组中含有多个viewResolvers
this.viewResolvers = new ArrayList<>(matchingBeans.values());
// We keep ViewResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
} else {
try {
//否则值加载id是viewResolver或者类型是ViewResolver的ViewResolver
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
} catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default ViewResolver later.
}
}

// Ensure we have at least one ViewResolver, by registering
// a default ViewResolver if no other resolvers are found.
if (this.viewResolvers == null) {
//如果没有合适的viewResolvers获取dispatchServlet.properties默认的ViewResolver
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}

ViewResolvers是一个List<ViewResolver>类型数据,视图解析是链式的,如果一个视图解析器根据viewName和local参数没有找到对应的View,则开始使用第二的解析器来进行查询,直到找到为止。默认的ViewResolvers为InternalResourceViewResolver:

1
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

ViewResolver的作用就是通过解析viewName返回一个View对象:

1
2
3
4
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}

initFlashMapManager

initFlashMapManager初始化FlashMapManager,与链接跳转相关的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 初始化 FlashMapManager
*/
private void initFlashMapManager(ApplicationContext context) {
try {
//默认加载id是flashMapManager或者类型是FlashMapManager的FlashMapManager
this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.flashMapManager.getClass().getSimpleName());
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.flashMapManager);
}
} catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
//如果没有合适的FlashMapManager获取dispatchServlet.properties默认的FlashMapManager
this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
if (logger.isTraceEnabled()) {
logger.trace("No FlashMapManager '" + FLASH_MAP_MANAGER_BEAN_NAME +
"': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");
}
}
}
官方描述

官方文档中指出:FlashMapManager用于检索和保存FlashMap实例的策略界面

那FlashMap是什么呢?

​ FlashMap为一个请求提供了一种存储属性的功能,用于另一个请求中使用这些属性。这种方式通常在 一个URL重定向到另一个URL时最常需要 - 例如Post / Redirect / Get模式。FlashMap在重定向之前保存(通常在会话中),并在重定向后可用并立即删除。

​ 可以使用请求路径和请求参数来设置FlashMap,以帮助识别目标请求。如果没有此信息,FlashMap将可用于下一个请求(该请求可能是也可能不是预期)。在一个重定向中,目标URL是已知的,可以使用该信息更新FlashMap,这个过程是自动完成的,它通过org.springframework.web.servlet.view.RedirectView完成此操作。

​ 注意:带注释的控制器通常不会直接使用FlashMap。 有关在带注释的控制器中使用Flash属性的概述,请参阅org.springframework.web.servlet.mvc.support.RedirectAttributes。

​ 通过上面的描述可知,FlashMap和FlashMapManager主要用于重定向数据保存。重定向就不得不提到RedirectView,RedirectView跳转时会将跳转之前的请求中的参数保存到FlashMap中,然后通过FlashManager保存起来:

1
2
3
4
5
//获取原请求所携带的数据 
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);

//将数据保存起来,作为跳转之后请求的数据使用
flashMapManager.saveOutputFlashMap(flashMap, request, response);

当重定向的请求在浏览器中重定向之后会再次会被DispathcerServlet进行拦截,在DispatcherServlet的doService方法中,有一步会从FlashManager中获取保存的FlashMap中的值:

1
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);

这样重定向的请求就可以使用以前的一些属性值了。

总结

​ 这样就完成了SpringMVC工程启动时初始化的所有工作了,主要就是完成IOC容器的初始化和一些默认实现的初始化,这样就完成了SpringMVC的初始化工作,接下来就是对Controller的访问链接的处理,我们会一步一步的写源码博客进行分析的。

评论