<返回更多

还搞不清Spring 与 Spring MVC 容器之间的关系?

2021-04-13  掘金  追逐仰望星空
加入收藏

在使用Spring MVC的时候,标准的配置是如下这样的:

还搞不清Spring 与 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

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>