Spring源码学习:SpringMVC(2)DispatcherServlet初始化【子容器&9大组件】

DispatcherServlet类图

Web应用启动的最后一个步骤就是创建和初始化相关Servlet,我们配置了DispatcherServlet类前端控制器,前端控制器作为中央控制器是整个Web应用的核心,用于获取分发用户请求并返回响应。

在这里插入图片描述

通过类图可以看出DispatcherServlet类的间接父类实现了Servlet接口,因此其本质上依旧是一个Servlet

HttpServletBean#init

DispatcherServelt类的本质是Servlet,所以在Web应用部署到容器后进行Servlet初始化时会调用相关的init(ServletConfig)方法,因此,DispatchServlet类的初始化过程也由该方法开始:

(注意:DispatcherServelt 没有init方法,会走到父类HttpServletBean的init方法)

	/**
 * DispatcherServlet 初始化入口
 * Map config parameters onto bean properties of this servlet, and
 * invoke subclass initialization.
 * @throws ServletException if bean properties are invalid (or required
 * properties are missing), or if subclass initialization fails.
 */
	@Override
	public final void init() throws ServletException {
   

		// Set bean properties from init parameters.

		/*
		 * 1.加载初始化参数,如:
		 * <servlet>
		 * 		<servlet-name>DispatcherServlet</servlet-name>
		 * 		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		 * 		<init-param>
		 * 			<param-name>name</param-name>
		 * 			<param-value>zimu</param-value>
		 * 		</init-param>
		 * 		<load-on-startup>1</load-on-startup>
		 * 	</servlet>
		 * 	这里会解析init-param列表。
		 */
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
   
			try {
   
				// 2.将当前的这个 Servlet 类转化为一个 BeanWrapper,从而能够以 Spring 的方法来对 init-param 的值进行注入
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				// 3.注册自定义属性编辑器,一旦遇到 Resource 类型的属性将会使用 ResourceEditor 进行解析
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				// 4.空实现,留给子类覆盖
				initBeanWrapper(bw);
				// 5.属性注入
				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.
		// 【重点】6.子类负责实现,主要来初始化ApplicationContext及一些Resolve
		initServletBean();
	}

new ServletConfigPropertyValues()

		public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
				throws ServletException {
   

			Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ?
					new HashSet<>(requiredProperties) : null);
			// 获取 web.xml 中 DispatcherServlet 配置的 init-params 属性内容
			Enumeration<String> paramNames = config.getInitParameterNames();
			while (paramNames.hasMoreElements()) {
   
				String property = paramNames.nextElement();
				Object value = config.getInitParameter(property);
                 保存 init-params 属性 
				addPropertyValue(new PropertyValue(property, value));
				if (missingProps != null) {
   
					missingProps.remove(property);
				}
			}

			// Fail if we are still missing properties.
			if (!CollectionUtils.isEmpty(missingProps)) {
   
				throw new ServletException(
						"Initialization from ServletConfig for servlet '" + config.getServletName() +
						"' failed; the following required properties were missing: " +
						StringUtils.collectionToDelimitedString(missingProps, ", "));
			}
		}

​ 该方法最主要的作用就是初始化init-param,如果我们没有配置任何init-param,那么该方法不会执行任何操作。从这里我们没有拿到有用的信息,但是在该方法结尾有initServletBean(),这是一个模板方法,可以由子类来实现,那么接下来我们就去看其子类FrameworkServlet中initServletBean

FrameworkServlet#initServletBean

继续查看 initServletBean()。父类 FrameworkServlet 覆盖了 HttpServletBean 中的 initServletBean 函数,如下:

protected void initServletBean() throws ServletException {
   
}
@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 {
   
			//【重点】,用于初始化子ApplicationContext对象,
			// 主要是用来加载<servlet/>对应的servletName-servlet.xml文件如:springMVC.xml
			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);
		}

		//当我们看到这句日志,就能知道dispatcherServlet已经初始化完成,web子容器也就初始化完成了
		if (logger.isInfoEnabled()) {
   
			logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
		}
	}

​ 其中我们可以只有一个核心方法:initWebApplicationContext(),主要用来初始化springmvc子容器,其余都是一些日志相关的,关于 initWebApplicationContext 的实现是在 FrameworkServlet#initWebApplicationContext 中完成,下面我们就来看看其实现过程。

initWebApplicationContext

protected WebApplicationContext initWebApplicationContext() {
   
		 /*
        	获取由ContextLoaderListener创建的根IoC容器
      	    获取根IoC容器有两种方法,还可通过key直接获取
        */
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		// 如果当前webApplicationContext不为null(context 实例在构造函数中被注入),则为其设置父容器
		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
						/*
                        如果当前Servelt存在一个WebApplicationContext即子IoC容器
                        并且上文获取的根IoC容器存在,则将根IoC容器作为子IoC容器的父容器
                        */
						cwac.setParent(rootContext);
					}
					//配置并刷新当前的子IoC容器,用于构建相关Bean
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}

		// 未能通过构造函数注入,则尝试去ServletContext容器中查找有无WebApplicationContext
		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
			//如果当前Servlet不存在一个子IoC容器则去查找一下
			wac = findWebApplicationContext();
		}

		// 以上均无WebApplicationContext,则创建一个新的WebApplicationContext
		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);
			}
		}

		//我们是否需要吧我们的容器发布出去,作为ServletContext的一个属性值呢?默认值为true哦
		if (this.publishContext
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值