在使用Spring MVC的時候,標準的配置是如下這樣的:
注意注意:小編整理了一份Spring全家桶筆記:Spring+Spring Boot+Spring Cloud+Spring MVC,有需要的朋友可以私信“spring”免費領取
1.ContextLoaderListener配置:
<!-- Spring讀取Spring的配置文件 --> <context-param> <!-- 名稱 --> <param-name>contextConfigLocation</param-name> <!-- 文件的位置 --> <param-value>classpath:Application-context.xml</param-value> </context-param> <context-param> <param-name>webAppRootKey</param-name> <param-value>meipian</param-value> </context-param> <!-- Spring 的監聽器配置 --> <listener> <!-- 在Spring-web包下 context --> <listener-class>org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
2.DispatcherServlet的配置:
<servlet> <servlet-name>meipian</servlet-name> <!-- 配置SpringMVC核心控制器 --> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>meipian</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
ContextLoaderListener監聽器的作用就是啟動Web容器時,自動裝配ApplicationContext的配置信息。因為它實現了ServletContextListener這個接口,在web.xml配置這個監聽器,啟動容器時,就會默認執行它實現的方法。
Spring MVC的使用的容器是WebApplicationContext。那么他和ContextLoaderListener所初始化的ApplicationContext有什么關系呢?
可以從DispatcherServlet的初始化過程說起:DispatcherServlet攔截了所有的請求,所以訪問任何一個接口都會初始化DispatcherServlet對象。
初始化DispatcherServlet只是做了一個很簡單的事:
public DispatcherServlet() { super(); setDispatchOptionsRequest(true); }
其父類FrameworkServlet初始化什么也沒有:
public FrameworkServlet() { }
然后Tomcat會調用DispatcherServlet的init方法,在 Servlet 的生命期中,僅執行一次 init() 方法。它是在服務器裝入 Servlet 時執行的。 可以配置服務器,以在啟動服務器或客戶機首次訪問 Servlet 時裝入 Servlet。 無論有多少客戶機訪問 Servlet,都不會重復執行 init() 。別問我為什么會調用init方法,這是servlet的規范。DispatcherServlet的init方法是在父類FrameworkServlet的父類HttpServlet中實現的:
@Override public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. 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) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); throw ex; } // Let subclasses do whatever initialization they like. initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } }
會調用DispatcherServlet的initServletBean()方法就是初始化WebApplicationContext的方法。這個方法在其子類FrameServlet方法中實現:
@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 { this.webApplicationContext = initWebApplicationContext(); //初始化WebApplicationContext對象 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"); } }
initWebApplicationContext方法如下所示:
protected WebApplicationContext initWebApplicationContext() { 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. 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; }
會默認走到
createWebApplicationContext方法:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); wac.setConfigLocation(getContextConfigLocation()); configureAndRefreshWebApplicationContext(wac); return wac; }
在沒有 contexloaderListener的情況下,parent是空的。
在有 contexloaderListener的情況下,發現parent不是空的。
而且在Spring MVC的配置中,如果你將Service的配置與mvc的配置寫在一起,有沒有contexloaderListener無所謂的。
原文鏈接:
https://juejin.im/post/58e5b7d3b123db15eb8143ae