springMVC初始化和流程(二)
一、Spring MVC初始化配置
上一篇已经介绍了Spring MVC的执行流程。这篇主要介绍Spring MVC 的初始化。Spring MVC的配置一般是在web.xml中配置系统变量contextConfigLocation和DispatcherSevlet,配置内容如下:
配置方法一:通过初始化DispatcherServlet然后初始化contextConfigLocation
<!-- SpringMVC --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-*.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> 配置方法二: <!-- 配置SpringMVC 配置文件路径 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-configuration/*.xml</param-value> </context-param> <!-- 配置ContextLoaderListener用以消除还换Spring Ioc 容器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置ContextLoaderListener用以消除还换Spring Ioc 容器--> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
配置内容:
• 系统变量contextConfigLocation的配置告诉Spring MVC 其Spring Ioc的配置文件在哪里,这样Spring会找到这些配置文件去加载它们。多个文件可用逗号隔开,正则匹配。
• ContextLoadListener实现了接口ServletContextListener,可在WEB工程初始化之前,完成对Spring Ioc 容器的初始化,也可在WEB工程关闭之时完成Spring Ioc容器的资源释放。
• 配置DispatcherServlet,初始化映射请求上下文。
二、Spring MVC 初始化
由上面配置文件的配置内容就可看出,Spring MVC 的初始化包含初始化Spring Ioc上下文和初始化映射请求上下文两个步骤,只是映射请求上下文是基于Spring Ioc上下文扩展出来的,以适应Java Web工程的需要。
1、初始化Spring Ioc上下文
Java Web 容器为其生命周期中提供ServletContextListener接口,这个接口可以在Web容器初始化和结束期中执行一定逻辑。通过它可以在DispatcherServlet初始化之前完成Spring Ioc容器的初始化,也可以在结束完成对Spring Ioc容器的销毁,只需要实现ServletContextListener接口的方法就可以。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent event) {
//初始化Spring IOC容器- initWebApplicationContext(event.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent event) {
//关闭web Ioc容器 closeWebApplicationContext(event.getServletContext());
//清除相关参数 ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
2、初始化映射请求上下文
映射请求上下文是通过DispatcherServlet初始化的,它和普通的Servlet也是一样的。如果在WEB工程中并没有注册ContextLoadListener,这个时候DispatcherSerclet就会在其初始化的时候对Spring Ioc容器进行初始化。
初始化一个Spring Ioc容器是耗时操作,如果放大用户请求上,会让一个用户陷入长时间的等待,因此一般在WEB容器启动的时候,或Web 容器载入DispatcherServlet的时候就初始Spring Ioc容器。所以一般建议使用ContextLoadListener进行初始化。
图一、DispatcherServlet 的设计
Web 容器对于Servlet的初始化,首先调用其ini方法,对于DispatcherServlet也是如此,这个方法位于其父类HttpServletBean里如下:
代码一:HttpServletBean中init方法
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. //根据参数初始化Bean try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); 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,这个方法交由子类FramworkServlet实现,接着看代码清单二中实现 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); }
代码清单二:framworkServlet中的initServletBean方法实现
@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { //初始化Spring Ioc容器 this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } } protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; //判断Spring Ioc容器是否已经初始化 if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it //如果Web Ioc容器已经在启动的时候创建,那么就沿用它
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 // 如果Spring Ioc容器还没有刷新,那么久进行刷新父容器上下文,设置ID等操作 if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // 处理父容器为空的情况 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 // 没有被初始化,则查找是否存在Spring Web Ioc容器 wac = findWebApplicationContext(); } if (wac == null) { // 没有被初始化也没有被找到Spring Ioc容器,则DispatcherServlet自己创建 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 // 当onRefresh方法没有被调用过,执行onRefresh方法,这个方法在DispatcherServlet中,参加代码清单三 onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
代码清单三:DispatcherServlet中初始化Spring MVC的组件
/** * This implementation calls {@link #initStrategies}. */ @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) { //初始化文件的解析 initMultipartResolver(context); //本地解析化 initLocaleResolver(context); //主题解析 initThemeResolver(context); //处理器映射 initHandlerMappings(context); //处理器的适配器 initHandlerAdapters(context); //Handler 的异常处理器 initHandlerExceptionResolvers(context); //当处理器没有返回逻辑视图名等相关信息时,自动将请求URL映射为逻辑视图名 initRequestToViewNameTranslator(context); //视图逻辑名称转换器,即允许返回逻辑视图名称,然后它会找到真实的视图 initViewResolvers(context); // initFlashMapManager(context); }
以上是Spring MVC主要组件的初始化,实际上,对这些组件的初始化会根据配置文件DispatcherServlet.properties来进行初始化
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!