<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>/WEB-INF/app-context.xmlparam-value>
context-param>
<servlet>
<servlet-name>appservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>param-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>appservlet-name>
<url-pattern>/app/*url-pattern>
servlet-mapping>
web-app>
在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。
public interface ServletContextListener extends EventListener {
//当Servlet 容器启动Web 应用时调用该方法。在调用完该方法之后,容器再对Filter 初始化,并且对那些在Web 应用启动时就需要被初始化的Servlet 进行初始化。
default void contextInitialized(ServletContextEvent sce) {
}
//当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet 和Filter 过滤器。
default void contextDestroyed(ServletContextEvent sce) {
}
}
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
//Tomcat启动时,会自动调用该方法,初始化Spring容器
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
public WebApplicationContext initWebApplicationContext(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!");
} else {
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 {
if (this.context == null) {
//创建Spring容器
this.context = this.createWebApplicationContext(servletContext);
}
//如果context是 ConfigurableWebApplicationContext 这种类型
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
if (!cwac.isActive()) {
//则设置其父容器
if (cwac.getParent() == null) {
//这里parent 是null
ApplicationContext parent = this.loadParentContext(servletContext);
cwac.setParent(parent);
}
//执行Spring容器初始化,加载并初始化Bean
this.configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
//将Spring放到ServletContext上下文中,使初始化SpringMVC容器时可以拿到Spring容器并且设置为自己的父容器
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 (Error | RuntimeException var8) {
logger.error("Context initialization failed", var8);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
throw var8;
}
}
}
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = this.determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
} else {
//利用反射创建Spring容器,这时Spring容器中属性都还没有值,仅仅是new了一个对象出来而已
return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
}
}
// Servlet的加载和实例化可以发生在容器启动时,也可以延迟初始化直到有请求需要处理时
public interface Servlet {
// 负责初始化Servlet对象,容器创建好Servlet对象后由容器调用调用,只执行一次
// 当load-on-startup设置为负数或者不设置时会在Servlet第一次用到时才被调用
void init(ServletConfig var1) throws ServletException;
// 获取该Servlet的初始化参数信息
ServletConfig getServletConfig();
// 负责响应客户端的请求,当容器接收到客户端要求访问特定Servlet对象的请求时,会调用该Servlet对象的service()方法,每次请求都会执行
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
// 返回Servlet信息,包含创建者、版本、版权等信息
String getServletInfo();
// Servlet结束生命周期时调用,释放Servlet对象占用的资源
void destroy();
}
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {}
public abstract class HttpServlet extends GenericServlet {}
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
public final void init() throws ServletException {
// 将init-param映射到bean属性 将配置参数自动转换成BeanWrapper
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
}
throw var4;
}
}
this.initServletBean();
}
}
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring " + this.getClass().getSimpleName() + " '" + this.getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("Initializing Servlet '" + this.getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
//初始化SpringMVC容器
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
} catch (RuntimeException | ServletException var4) {
this.logger.error("Context initialization failed", var4);
throw var4;
}
if (this.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";
this.logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value);
}
if (this.logger.isInfoEnabled()) {
this.logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
}
protected WebApplicationContext initWebApplicationContext() {
//从Servlet上下文中获取Spring容器
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
//SpringMVC容器不为null
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if (!cwac.isActive()) {
//则设置父容器
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
//加载并初始化SpringMVC中的Bean
this.configureAndRefreshWebApplicationContext(cwac);
}
}
}
//根据ContextAttribute找SpringMVC容器
if (wac == null) {
wac = this.findWebApplicationContext();
}
//如果没有找到,则创建SpringMVC容器
if (wac == null) {
wac = this.createWebApplicationContext(rootContext);
}
//加载并初始化SpringMVC中的Bean
if (!this.refreshEventReceived) {
synchronized(this.onRefreshMonitor) {
//初始化DispatcherServlet及九大组件
this.onRefresh(wac);
}
}
if (this.publishContext) {
String attrName = this.getServletContextAttributeName();
//将SpringMVC容器放入到Servlet上下文中
this.getServletContext().setAttribute(attrName, wac);
}
return wac;
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = this.getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
} else {
//获取利用反射获取SringMVC容器
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
//设置系统环境变量
wac.setEnvironment(this.getEnvironment());
//设置父容器
wac.setParent(parent);
//获取web.xml配置的 contextConfigLocation
String configLocation = this.getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
//加载初始化Bean
this.configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
//设置SpringMVC容器id
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
if (this.contextId != null) {
wac.setId(this.contextId);
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName());
}
}
//设置Servlet上下文
wac.setServletContext(this.getServletContext());
//设置Serlvet一些配置参数
wac.setServletConfig(this.getServletConfig());
//设置namespace
wac.setNamespace(this.getNamespace());
//设置ContextRefreshListener
wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener()));
//获取Environment并调用initPropertySources替换servletContextInitParams和servletConfigInitParams参数
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
}
//暂无实现
this.postProcessWebApplicationContext(wac);
this.applyInitializers(wac);
wac.refresh();
}
public class DispatcherServlet extends FrameworkServlet {
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
}
protected void initStrategies(ApplicationContext context) {
//初始化上传文件解析器
this.initMultipartResolver(context);
//初始化国际化相关解析器
this.initLocaleResolver(context);
//初始化主题解析器
this.initThemeResolver(context);
//初始化处理器映射器
this.initHandlerMappings(context);
//初始化处理器适配器
this.initHandlerAdapters(context);
//初始化处理器异常解析器
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);、
//初始化视图解析器
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
private void initLocaleResolver(ApplicationContext context) {
try {
//从Spring容器中获取LocaleResolver的实现类
this.localeResolver = (LocaleResolver)context.getBean("localeResolver", LocaleResolver.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Detected " + this.localeResolver);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
} catch (NoSuchBeanDefinitionException var3) {
this.localeResolver = (LocaleResolver)this.getDefaultStrategy(context, LocaleResolver.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No LocaleResolver 'localeResolver': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
private void initThemeResolver(ApplicationContext context) {
try {
//从Spring容器中获取ThemeResolver的实现类
this.themeResolver = (ThemeResolver)context.getBean("themeResolver", ThemeResolver.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Detected " + this.themeResolver);
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());
}
} catch (NoSuchBeanDefinitionException var3) {
this.themeResolver = (ThemeResolver)this.getDefaultStrategy(context, ThemeResolver.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No ThemeResolver 'themeResolver': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
}
}
}
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
//如果detectAllHandlerMappings为true 则从Spring容器中获取所有实现HandlerMapping接口的实现类
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
//并且根据@Order注解 进行排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {//否则从Spring容器中获取beanName为"handlerMapping"的实现类
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
//并且赋值给handlerMappings
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
//如果handlerMappings还是null
if (this.handlerMappings == null) {
//则获取默认配置文件配置的HandlerMapping(DispatcherServlet.properties)
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
//遍历所有的HandlerMappings
for (HandlerMapping mapping : this.handlerMappings) {
if (mapping.usesPathPatterns()) {//如果mapping 是自动使用路径匹配
//则parseRequestPatch设置为true
this.parseRequestPath = true;
break;
}
}
}
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
//如果detectAllHandlerAdapters=true
if (this.detectAllHandlerAdapters) {
//则从Spring容器中获取所有实现HandlerAdapter接口的实现类
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// 并且根据@Order注解进行排序
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
//如果detectAllHandlerAdapters=false,则获取beanName为"handlerAdpater"的handlerApater实现类
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.
}
}
//如果handlerApaters==null 则从获取默认配置文件中配置的实现类
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
//如果detectAllHandlerExceptionResolvers=true 则从Spring容器中获取所有实现HandlerExeptionResolver接口的实现类
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
// 根据@Order进行排序
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
try {
//如果detectAllHandlerExceptionResolvers=false 则从spring容器中获取beanName是"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.
}
}
// 如果handlerExceptionResolvers==null 则获取默认配置文件中默认的实现类
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
//从Spring容器中获取beanName是"viewNameTranslator"的实现类
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) {
// 如果抛出异常 则获取默认配置文件中配置的实现类
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() + "]");
}
}
}
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
//如果detectAllViewResolvers=true 则从Spring容器获取ViewResolver的实现类
if (this.detectAllViewResolvers) {
// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList<>(matchingBeans.values());
// 然后根据@Order进行排序
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
}
else {
try {
//如果detectAllViewResolvers=false 则从Spring容器获取beanName是"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.
}
}
// 如果viewResolvers==null 则获取配置文件中指定默认的ViewResolver实现类
if (this.viewResolvers == null) {
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
private void initFlashMapManager(ApplicationContext context) {
try {
//从Spring容器中获取beanName是"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) {
// 异常 则从配置文件中获取默认的实现类
this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
if (logger.isTraceEnabled()) {
logger.trace("No FlashMapManager '" + FLASH_MAP_MANAGER_BEAN_NAME +
"': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");
}
}
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
//defaultStrategies为空 则还没有加载过DispatcherServlet.properties文件
if (defaultStrategies == null) {
try {
//根据DispatcherServlet.properties 根据ClassPatchRsource
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
//将properties文件内容加载到defaultStrategies中
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
//根据传入的参数name 作为key
String key = strategyInterface.getName();
//从defaultStrategies(配置文件)中查找
String value = defaultStrategies.getProperty(key);
if (value != null) {
//找到 则根据,进行分割,转化为数组
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {//遍历数组 通过ClassUtils.forName 构建Class
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
//根据class 创建Bean
Object strategy = createDefaultStrategy(context, clazz);
//添加到strategies集合中
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 Collections.emptyList();
}
}
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