天天看点

springmvc源码分析2-FrameworkServlet

 1.springmvc中DispatcherServlet继承体系

springmvc源码分析2-FrameworkServlet

2.概述

1)FrameworkServlet继承HttpServletBean,实现了HttpServletBean的initServletBean方法来初始化当前上下文,使用web.xml中的init-param作为属性,其中一个重要的配置contextConfigLocation,配置解析xml文件的路径,我们在xml经常看到配置的这个属性,这个是在FrameworkServlet进行处理,使用XmlWebApplicationContext来解析对应的xml配置文件

web.xml中的init-param主要有

  1. contextClass:默认上下文是XmlWebApplicationContext,可以自己指定对应的上下文
  2. contextInitializerClasses:指定一个或多个ApplicationContextInitializer,可以在容器刷新之前进行一些处理
  3. contextConfigLocation:xml配置文件,可以,或空格分割,
  4. namespace:servlet的命名空间
  5. contextAttribute:标明当前的ApplicationContext在ServletContext中的名称

还有一些其他的属性,具体看源码中FrameworkServlet的属性

2)其次就是重写了HttpServlet的doGet,doPost,doPut,doDelete,doOptions,doTrace等方法,每个方法都调用了processRequest方法,也就是每进来一个无论什么请求,都会调用processRequest方法

3.FrameworkServlet整个代码用工具翻译后的

https://blog.csdn.net/qq_39482039/article/details/118944033

4.实现ApplicationContextAware接口的作用

每个servlet只有一个上下文,由于默认使用XmlWebApplicationContext,这里实现ApplicationContextAware接口可以外部进行设置上下文,也就是ApplicationContext

5.FrameworkServlet实现了HttpServletBean的抽象方法initServletBean

/**
	 * {@link HttpServletBean}的重写方法,在任何bean属性之后调用
	 * *已经准备好了。创建此servlet的WebApplicationContext。
	 */
	@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 {
			//设置当前的上下文
			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);
		}

		if (logger.isInfoEnabled()) {
			logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
		}
	}
           

主要作用设置当前的上下文环境,最主要一个方法initWebApplicationContext

/**
	 * 初始化并发布此Servlet的WebApplicationContext。
	 * <p>代表{@link #createWebApplicationContext}进行实际创建
	 * 的上下文。可以在子类中覆盖。
	 *
	 * @return the WebApplicationContext instance
	 * @see #FrameworkServlet(WebApplicationContext)
	 * @see #setContextClass
	 * @see #setContextConfigLocation
	 */
	protected WebApplicationContext initWebApplicationContext() {
		//找到ServletContext中父上下文,也就是ContextLoaderListener初始化的上下文
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		//如果ApplicationContextAware注入的上下文存在
		if (this.webApplicationContext != null) {
			// 在构造时注入了一个上下文实例->使用它
			wac = this.webApplicationContext;
			//如果是可配置的上下文环境
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					//上下文尚未刷新->提供以下服务
					//设置父上下文、设置应用程序上下文id等
					if (cwac.getParent() == null) {
						// 在没有显式父->集的情况下注入了上下文实例
						// 根应用程序上下文(如果有;可以为空)作为父级
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		//如果上下文不存在,用contextAttribute查找是否在servletCoontext中注册
		if (wac == null) {
			//在构造时没有注入上下文实例->查看是否有
            //已在servlet上下文中注册。如果存在,则假定
            //父上下文(如果有)已经设置,并且
            //用户已执行任何初始化,如设置上下文id
			wac = findWebApplicationContext();
		}
		//没有找到创建一个可配置上下文环境,默认是xml文
		if (wac == null) {
			// 没有为此servlet定义上下文实例->创建本地实例
			wac = createWebApplicationContext(rootContext);
		}
		//留给子类实现,自定义刷新
		if (!this.refreshEventReceived) {
			// 上下文不是具有刷新功能的ConfigurableApplicationContext
			// 在构建时注入的支持或上下文已经
			// 刷新->在此处手动触发初始刷新.
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}
		//是否将上下文保存在ServletContext
		if (this.publishContext) {
			// 将上下文发布为servlet上下文属性。
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}
           
  1. 获取父上下文也就是ContextLoaderListener初始化的上下文
  2. 如果ApplicationContextAware在外部注入了一个上下文使用它,如果是注解上下文环境,设置父上下文,并刷新上下文
  3. 在ServletContext中根据contextAttribute查找是否有指定的上下文
  4. 如果ServletContext中没有找到,使用默认XmlWebApplicationContext创建一个上下文
  5. refreshEventReceived是false默认会调用自定义刷新,留给子类实现
  6. 如果在web.xml配置为true,将当前上下保存在ServletContext

主要方法

1.configureAndRefreshWebApplicationContext

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			//应用程序上下文id仍设置为其原始默认值
            //->根据可用信息分配更有用的id
			if (this.contextId != null) {
				wac.setId(this.contextId);
			} else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// 在上下文中,无论如何都将调用wac环境的#initPropertySources
        //刷新;在此处急切地执行此操作,以确保servlet属性源到位
        //用于以下#refresh之前的任何后处理或初始化
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		wac.refresh();
	}
           

 2.createWebApplicationContext创建一个上下文

/**
	 * 为这个servlet实例化WebApplicationContext,可以是默认的
	 * *{@link org.springframework.web.context.support.XmlWebApplicationContext}
	 * *或者{@link#setContextClass自定义上下文类}(如果已设置)。
	 * *委托给#createWebApplicationContext(ApplicationContext)。
	 *
	 * @param parent 要使用的父WebApplicationContext,如果没有则为{@code null}
	 * @return the WebApplicationContext for this servlet
	 * @see org.springframework.web.context.support.XmlWebApplicationContext
	 * @see #createWebApplicationContext(ApplicationContext)
	 */
	protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
		return createWebApplicationContext((ApplicationContext) parent);
	}
           
/**
	 * 为这个servlet实例化WebApplicationContext,可以是默认的
	 * *{@link org.springframework.web.context.support.XmlWebApplicationContext}
	 * *或者{@link#setContextClass自定义上下文类}(如果已设置)。
	 * *<p>此实现要求自定义上下文实现
	 * *{@link org.springframework.web.context.ConfigurableWebApplicationContext}
	 * *接口。可以在子类中重写。
	 * *<p>不要忘记在
	 * *已创建上下文(用于触发其{@link#onRefresh callback},并调用
	 * *{@link org.springframework.context.ConfigurableApplicationContext#refresh()}
	 * *在返回上下文实例之前。
	 *
	 * @param parent 要使用的父ApplicationContext,如果没有则为{@code null}
	 * @return 这个servlet的WebApplicationContext
	 * @see org.springframework.web.context.support.XmlWebApplicationContext
	 */
	protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
		Class<?> contextClass = getContextClass();
		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);
		String configLocation = getContextConfigLocation();
		if (configLocation != null) {
			wac.setConfigLocation(configLocation);
		}
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}
           
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			//应用程序上下文id仍设置为其原始默认值
            //->根据可用信息分配更有用的id
			if (this.contextId != null) {
				wac.setId(this.contextId);
			} else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// 在上下文中,无论如何都将调用wac环境的#initPropertySources
        //刷新;在此处急切地执行此操作,以确保servlet属性源到位
        //用于以下#refresh之前的任何后处理或初始化
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}
		//留给子类调用
		postProcessWebApplicationContext(wac);
		//调用ApplicationContextInitializer.initialize方法
		applyInitializers(wac);
		//加载或刷新配置,可能是xml或java配置类等
		wac.refresh();
	}
           

6.processRequest方法,设置当前请求的语言环境和属性,实际分派留给子类处理

/**
	 * 处理此请求,发布事件而不管结果如何。
	 * *<p>实际的事件处理由抽象
	 * *{@link#doService}模板方法。
	 */
	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
		//获取当前的语言环境
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		//给当前请求设置语言环境
		LocaleContext localeContext = buildLocaleContext(request);
		//获取当前请求范围或会话范围内的属性
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		//绑定到当前请求
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
		//获取异步处理类
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
		
		initContextHolders(request, localeContext, requestAttributes);

		try {
			//处理请求实际分派,留给子类实现
			doService(request, response);
		} catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		} catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		} finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}
           

继续阅读