在上一篇博客SpringMVC源码分析--容器初始化(三)HttpServletBean我们介绍了HttpServletBean的init函数,其主要作用是初始化了一下SpringMVC配置文件的地址contextConfigLocation的配置属性,然后其调用的子类FrameworkServlet的initServletBean方法。

其实FrameworkServlet是springMVC初始化IOC容器的核心,通过读取配置的contextConfigLocation配置的springMVC配置文件的地址来读取各种初始化信息和Bean注入信息,来完成springMVC IOC 容器的初始化。接下来我们通过源码一步一步进行分析。

首先我们看initServletBean函数方法,其主要的操作就是this.webApplicationContext = initWebApplicationContext()完成webApplicationContext的初始化,webApplicationContext 即为springMVC的IOC容器,其实现类是XMLWebApplicationContext,

这样我们发现,初始化工作在initWebApplicationContext函数方法中。

       //创建springMVC的IOC容器
	@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 {
			//创建springMVC的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");
		}
	}

initWebApplicationContext函数方法是整个springMVC IOC实现的实现过程,函数代码段如下,

WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext())是从ServletContext中获取IOC容器,因为我们知道spring web在初始化时会将初始化的webApplicationContex保存到ServletContext中,这样我们就可以获取到spring web容器了,并且把它作为父容器。

if (this.webApplicationContext != null)  当容器已经存在时,我们就不需要重新创建,只需要刷新一下,将springMVC配置文件中的属性加载到webApplicationContext中就可以了,当不存在时我们就需要一步一步的创建webApplicationContext了。

wac = findWebApplicationContext()在ServletContext中查找WebApplicationContext,说不定它已经被存放到ServletContext中去了。

如果没有我们就需要创建wac = createWebApplicationContext(rootContext),这个步骤我们会接下来分析,创建完之后我们调用onRefresh(wac),这个方法在子类DispatcherServlet中实现,去初始化一些其他操作,现在已经完成了webApplicationContext的初始化工作了接下来就需要把webApplicationContext存放到ServletContext中去了getServletContext().setAttribute(attrName,
wac),最后调用return wac完成springMVC IOC容器的初始化。

        /**
	 *初始化和发布web应用上下文,封装了建立Spring容器上下文的整个过程
	 *
	 */
	protected WebApplicationContext initWebApplicationContext() {
		//获取由ContextLoaderListener初始化并注册在ServletContext中的根上下文,一般是Spring的IOC容器记为rootContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		//如果webApplicationContext已经不为空,表示这个Servlet类是通过编程式注册到容器中的(Servlet 3.0 +中的ServletContext.addServlet()),
		//上下文也由编程式传入。若这个传入的上下文还没有被初始化,将rootContext上下文设置为它的父上下文,然后将其初始化,否则直接使用。
		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);
				}
			}
		}
		//判断wac是否为空判断是否已经完成上面的上下文的设置
		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不是由编程式注册到容器中的,在ServletContext中查找上下文,查找得到
			//说明上下文已经以别的方式初始化并注册在contextAttribute下,直接使用
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			//此时还为空,调用以下方法创建一个全新的以rootContext为父上下文的上下文,作为SpringMVC配置元素的上下文
			//大多数情况下我们所使用的上下文就是这个新建的上下文
			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.
			//在DispatcherServlet中复写,会初始化一系列属性
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			//将这个上下文发布到ServletContext中,也就是将上下文以一个和Servlet类在web.xml中注册名字有关的值为键
			//设置为ServletContext的一个属性
			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;
	}

上面我们介绍了springMVC IOC容器的初始化操作,但很多具体操作是在其他函数中完成的,接下来我们一一分析,

findWebApplicationContext函数是从ServletContext中查找webApplicationContext,springMVC初始化完成之后我们会看到它会将webApplicationContext保存到ServletContext中,这样我们可以在ServletContext中获取到它。

       //从当前容器中查找SpringMVC的IOC容器
	protected WebApplicationContext findWebApplicationContext() {
		String attrName = getContextAttribute();
		if (attrName == null) {
			return null;
		}
		WebApplicationContext wac =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
		if (wac == null) {
			throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
		}
		return wac;
	}

在上面介绍中我们并没有真正介绍到webApplicationContext初始化的完整过程,其完整实现是在createWebApplicationContext函数中,其首先会实例化一个IOC容器,这只是一个空容器,并没有相关属性wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

接下来的操作就是对wac设置相关属性

wac.setEnvironment(getEnvironment())设置环境

wac.setParent(parent)设置parent,这样就把springMVC和Spring的两个IOC容器连接了在一起

wac.setConfigLocation(getContextConfigLocation())设置web.xml中的springMVC的配置文件,各种bean的注入

configureAndRefreshWebApplicationContext(wac),这个函数是关键,是解析springMVC配置文件完成初始化的过程。

return wac返回已经初始化的webApplicationContext

       //创建springMVC的IOC容器,
	protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		//获取类XMLWebApplicationContext
		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");
		}
		//实例化一个IOC容器
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
		//设置IOC容器相关的属性,这样springMVC的IOC容器就创建好了
		wac.setEnvironment(getEnvironment());

		//设置parent,这样就把springMVC和Spring的两个IOC容器连接了在一起
		wac.setParent(parent);
		//设置web.xml中的springMVC的配置文件,各种bean的注入
		wac.setConfigLocation(getContextConfigLocation());

		//初始化springMVC各种的相关配置,各种注入的Controller,配置文件等
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}

上面我们介绍到configureAndRefreshWebApplicationContext函数是完成springMVC配置文件初始化过程的函数,其实它也是比较简单的,也是设置springMVC IOC容器的相关属性,

最终调用wac.refresh(),通过AbstractApplicationContext来完成对springMVC xml配置文件的解析初始化操作,这边我们就不再深入探究,在spring IOC容器初始化时我们再详细介绍。

        /*配置或者刷新应用上下文*/
	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
			}
		}
		//设置springMVC IOC容器的相关属性
		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		//初始化springMVC各种的相关配置,各种注入的Controller,配置文件等
		wac.refresh();
	}

通过以上函数的操作就完成了springMVC IOC容器的初始化操作,并且把spring web初始化的IOC容器作为父容器,这样springMVC就可以获得父容器中注入的各种Bean,同时初始化的IOC容器webApplicationContext也会被存放到ServletContex中,这样在整个的web容器中都可以得到并使用IOC容器中的各种属性。

FrameworkServlet完整源码解析:

 //FrameworkServlet类的设计目的就是用来建立一个和Servlet关联的Spring容器上下文
 //并将其注册到ServletContext中,跳脱开SpringMVC体系,我们也能通过继承FrameworkServlet类
 //得到与Spring容器整合的好处,FrameworkServlet和HttpServletBean一样,是一个可以独立使用的类

@SuppressWarnings("serial")
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

	//servlet命名空间前缀
	public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";

	//FrameworkServlet类的上下文
	public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;

	public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";

	private static final String INIT_PARAM_DELIMITERS = ",; \t\n";

	private static final boolean responseGetStatusAvailable =
			ClassUtils.hasMethod(HttpServletResponse.class, "getStatus");

	private String contextAttribute;

	private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;

	private String contextId;

	private String namespace;

	// web.xml中配置的contextConfigLocation属性,就是springMVC的配置文件
	private String contextConfigLocation;

	/** Actual ApplicationContextInitializer instances to apply to the context */
	private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
			new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();

	/** Comma-delimited ApplicationContextInitializer class names set through init param */
	private String contextInitializerClasses;

	/** Should we publish the context as a ServletContext attribute? */
	private boolean publishContext = true;

	/** Should we publish a ServletRequestHandledEvent at the end of each request? */
	private boolean publishEvents = true;

	/** Expose LocaleContext and RequestAttributes as inheritable for child threads? */
	private boolean threadContextInheritable = false;

	/** Should we dispatch an HTTP OPTIONS request to {@link #doService}? */
	private boolean dispatchOptionsRequest = false;

	/** Should we dispatch an HTTP TRACE request to {@link #doService}? */
	private boolean dispatchTraceRequest = false;

	/** WebApplicationContext for this servlet */
	private WebApplicationContext webApplicationContext;

	/** If the WebApplicationContext was injected via {@link #setApplicationContext} */
	private boolean webApplicationContextInjected = false;

	/** Flag used to detect whether onRefresh has already been called */
	private boolean refreshEventReceived = false;

	public FrameworkServlet() {
	}

	public FrameworkServlet(WebApplicationContext webApplicationContext) {
		this.webApplicationContext = webApplicationContext;
	}

	public void setContextAttribute(String contextAttribute) {
		this.contextAttribute = contextAttribute;
	}

	public String getContextAttribute() {
		return this.contextAttribute;
	}

	public void setContextClass(Class<?> contextClass) {
		this.contextClass = contextClass;
	}

	public Class<?> getContextClass() {
		return this.contextClass;
	}

	public void setContextId(String contextId) {
		this.contextId = contextId;
	}

	public String getContextId() {
		return this.contextId;
	}

	public void setNamespace(String namespace) {
		this.namespace = namespace;
	}

	public String getNamespace() {
		return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
	}

	//web.xml中配置文件的地址
	public void setContextConfigLocation(String contextConfigLocation) {
		this.contextConfigLocation = contextConfigLocation;
	}

	public String getContextConfigLocation() {
		return this.contextConfigLocation;
	}

	@SuppressWarnings("unchecked")
	public void setContextInitializers(ApplicationContextInitializer<?>... initializers) {
		if (initializers != null) {
			for (ApplicationContextInitializer<?> initializer : initializers) {
				this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer);
			}
		}
	}

	public void setContextInitializerClasses(String contextInitializerClasses) {
		this.contextInitializerClasses = contextInitializerClasses;
	}

	public void setPublishContext(boolean publishContext) {
		this.publishContext = publishContext;
	}

	public void setPublishEvents(boolean publishEvents) {
		this.publishEvents = publishEvents;
	}

	public void setThreadContextInheritable(boolean threadContextInheritable) {
		this.threadContextInheritable = threadContextInheritable;
	}

	public void setDispatchOptionsRequest(boolean dispatchOptionsRequest) {
		this.dispatchOptionsRequest = dispatchOptionsRequest;
	}

	public void setDispatchTraceRequest(boolean dispatchTraceRequest) {
		this.dispatchTraceRequest = dispatchTraceRequest;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {
			this.webApplicationContext = (WebApplicationContext) applicationContext;
			this.webApplicationContextInjected = true;
		}
	}

	//创建springMVC的IOC容器
	@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 {
			//创建springMVC的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");
		}
	}

	/**
	 *初始化和发布web应用上下文,封装了建立Spring容器上下文的整个过程
	 *
	 */
	protected WebApplicationContext initWebApplicationContext() {
		//获取由ContextLoaderListener初始化并注册在ServletContext中的根上下文,一般是Spring的IOC容器记为rootContext
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;
		//如果webApplicationContext已经不为空,表示这个Servlet类是通过编程式注册到容器中的(Servlet 3.0 +中的ServletContext.addServlet()),
		//上下文也由编程式传入。若这个传入的上下文还没有被初始化,将rootContext上下文设置为它的父上下文,然后将其初始化,否则直接使用。
		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);
				}
			}
		}
		//判断wac是否为空判断是否已经完成上面的上下文的设置
		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不是由编程式注册到容器中的,在ServletContext中查找上下文,查找得到
			//说明上下文已经以别的方式初始化并注册在contextAttribute下,直接使用
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			//此时还为空,调用以下方法创建一个全新的以rootContext为父上下文的上下文,作为SpringMVC配置元素的上下文
			//大多数情况下我们所使用的上下文就是这个新建的上下文
			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.
			//在DispatcherServlet中复写,会初始化一系列属性
			onRefresh(wac);
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			//将这个上下文发布到ServletContext中,也就是将上下文以一个和Servlet类在web.xml中注册名字有关的值为键
			//设置为ServletContext的一个属性
			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;
	}

	 //从当前容器中查找SpringMVC的IOC容器
	protected WebApplicationContext findWebApplicationContext() {
		String attrName = getContextAttribute();
		if (attrName == null) {
			return null;
		}
		WebApplicationContext wac =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
		if (wac == null) {
			throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
		}
		return wac;
	}

	 //创建springMVC的IOC容器,
	protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		//获取类XMLWebApplicationContext
		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");
		}
		//实例化一个IOC容器
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
		//设置IOC容器相关的属性,这样springMVC的IOC容器就创建好了
		wac.setEnvironment(getEnvironment());

		//设置parent,这样就把springMVC和Spring的两个IOC容器连接了在一起
		wac.setParent(parent);
		//设置web.xml中的springMVC的配置文件,各种bean的注入
		wac.setConfigLocation(getContextConfigLocation());

		//初始化springMVC各种的相关配置,各种注入的Controller,配置文件等
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}
	/*配置或者刷新应用上下文*/
	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
				wac.setId(this.contextId);
			}
			else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
			}
		}
		//设置springMVC IOC容器的相关属性
		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}

		postProcessWebApplicationContext(wac);
		applyInitializers(wac);
		//初始化springMVC各种的相关配置,各种注入的Controller,配置文件等
		wac.refresh();
	}

	protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
		return createWebApplicationContext((ApplicationContext) parent);
	}

	protected void postProcessWebApplicationContext(ConfigurableWebApplicationContext wac) {
	}

	protected void applyInitializers(ConfigurableApplicationContext wac) {
		String globalClassNames = getServletContext().getInitParameter(ContextLoader.GLOBAL_INITIALIZER_CLASSES_PARAM);
		if (globalClassNames != null) {
			for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
				this.contextInitializers.add(loadInitializer(className, wac));
			}
		}

		if (this.contextInitializerClasses != null) {
			for (String className : StringUtils.tokenizeToStringArray(this.contextInitializerClasses, INIT_PARAM_DELIMITERS)) {
				this.contextInitializers.add(loadInitializer(className, wac));
			}
		}

		AnnotationAwareOrderComparator.sort(this.contextInitializers);
		for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
			initializer.initialize(wac);
		}
	}

	@SuppressWarnings("unchecked")
	private ApplicationContextInitializer<ConfigurableApplicationContext> loadInitializer(
			String className, ConfigurableApplicationContext wac) {
		try {
			Class<?> initializerClass = ClassUtils.forName(className, wac.getClassLoader());
			Class<?> initializerContextClass =
					GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
			if (initializerContextClass != null) {
				Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
						"Could not add context initializer [%s] since its generic parameter [%s] " +
						"is not assignable from the type of application context used by this " +
						"framework servlet [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
						wac.getClass().getName()));
			}
			return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class);
		}
		catch (Exception ex) {
			throw new IllegalArgumentException(String.format("Could not instantiate class [%s] specified " +
					"via 'contextInitializerClasses' init-param", className), ex);
		}
	}

	public String getServletContextAttributeName() {
		return SERVLET_CONTEXT_PREFIX + getServletName();
	}

	/**
	 * Return this servlet's WebApplicationContext.
	 */
	public final WebApplicationContext getWebApplicationContext() {
		return this.webApplicationContext;
	}

	protected void initFrameworkServlet() throws ServletException {
	}

	public void refresh() {
		WebApplicationContext wac = getWebApplicationContext();
		if (!(wac instanceof ConfigurableApplicationContext)) {
			throw new IllegalStateException("WebApplicationContext does not support refresh: " + wac);
		}
		((ConfigurableApplicationContext) wac).refresh();
	}

	//WebApplicationContext刷新通知
	public void onApplicationEvent(ContextRefreshedEvent event) {
		this.refreshEventReceived = true;
		onRefresh(event.getApplicationContext());
	}

	protected void onRefresh(ApplicationContext context) {
		// For subclasses: do nothing by default.
	}

	/**
	 * Close the WebApplicationContext of this servlet.
	 * @see org.springframework.context.ConfigurableApplicationContext#close()
	 */
	@Override
	public void destroy() {
		getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
		// Only call close() on WebApplicationContext if locally managed...
		if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
			((ConfigurableApplicationContext) this.webApplicationContext).close();
		}
	}

	/**
	 * Override the parent class implementation in order to intercept PATCH
	 * requests.
	 */
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		String method = request.getMethod();
		if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
			processRequest(request, response);
		}
		else {
			super.service(request, response);
		}
	}

	//get请求
	@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	//post请求
	@Override
	protected final void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	//put请求
	@Override
	protected final void doPut(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	//delete请求
	@Override
	protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

	@Override
	protected void doOptions(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
			processRequest(request, response);
			if (response.containsHeader("Allow")) {
				// Proper OPTIONS response coming from a handler - we're done.
				return;
			}
		}

		// Use response wrapper for Servlet 2.5 compatibility where
		// the getHeader() method does not exist
		super.doOptions(request, new HttpServletResponseWrapper(response) {
			@Override
			public void setHeader(String name, String value) {
				if ("Allow".equals(name)) {
					value = (StringUtils.hasLength(value) ? value + ", " : "") + RequestMethod.PATCH.name();
				}
				super.setHeader(name, value);
			}
		});
	}

	@Override
	protected void doTrace(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		if (this.dispatchTraceRequest) {
			processRequest(request, response);
			if ("message/http".equals(response.getContentType())) {
				// Proper TRACE response coming from a handler - we're done.
				return;
			}
		}
		super.doTrace(request, response);
	}

	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 ex) {
			failureCause = ex;
			throw ex;
		}
		catch (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();
			}

			if (logger.isDebugEnabled()) {
				if (failureCause != null) {
					this.logger.debug("Could not complete request", failureCause);
				}
				else {
					if (asyncManager.isConcurrentHandlingStarted()) {
						logger.debug("Leaving response open for concurrent processing");
					}
					else {
						this.logger.debug("Successfully completed request");
					}
				}
			}

			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

	protected LocaleContext buildLocaleContext(HttpServletRequest request) {
		return new SimpleLocaleContext(request.getLocale());
	}

	protected ServletRequestAttributes buildRequestAttributes(
			HttpServletRequest request, HttpServletResponse response, RequestAttributes previousAttributes) {

		if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
			return new ServletRequestAttributes(request, response);
		}
		else {
			return null;  // preserve the pre-bound RequestAttributes instance
		}
	}

	private void initContextHolders(
			HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {

		if (localeContext != null) {
			LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
		}
		if (requestAttributes != null) {
			RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Bound request context to thread: " + request);
		}
	}

	private void resetContextHolders(HttpServletRequest request,
			LocaleContext prevLocaleContext, RequestAttributes previousAttributes) {

		LocaleContextHolder.setLocaleContext(prevLocaleContext, this.threadContextInheritable);
		RequestContextHolder.setRequestAttributes(previousAttributes, this.threadContextInheritable);
		if (logger.isTraceEnabled()) {
			logger.trace("Cleared thread-bound request context: " + request);
		}
	}

	private void publishRequestHandledEvent(
			HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause) {

		if (this.publishEvents) {
			// Whether or not we succeeded, publish an event.
			long processingTime = System.currentTimeMillis() - startTime;
			int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1);
			this.webApplicationContext.publishEvent(
					new ServletRequestHandledEvent(this,
							request.getRequestURI(), request.getRemoteAddr(),
							request.getMethod(), getServletConfig().getServletName(),
							WebUtils.getSessionId(request), getUsernameForRequest(request),
							processingTime, failureCause, statusCode));
		}
	}

	protected String getUsernameForRequest(HttpServletRequest request) {
		Principal userPrincipal = request.getUserPrincipal();
		return (userPrincipal != null ? userPrincipal.getName() : null);
	}

	protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
			throws Exception;

	//监听WebApplicationContext的刷新情况
	private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

		@Override
		public void onApplicationEvent(ContextRefreshedEvent event) {
			FrameworkServlet.this.onApplicationEvent(event);
		}
	}

	private class RequestBindingInterceptor extends CallableProcessingInterceptorAdapter {

		@Override
		public <T> void preProcess(NativeWebRequest webRequest, Callable<T> task) {
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			if (request != null) {
				HttpServletResponse response = webRequest.getNativeRequest(HttpServletResponse.class);
				initContextHolders(request, buildLocaleContext(request), buildRequestAttributes(request, response, null));
			}
		}
		@Override
		public <T> void postProcess(NativeWebRequest webRequest, Callable<T> task, Object concurrentResult) {
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			if (request != null) {
				resetContextHolders(request, null, null);
			}
		}
	}

}

SpringMVC源码分析--容器初始化(四)FrameworkServlet的更多相关文章

  1. SpringMVC源码分析--容器初始化(五)DispatcherServlet

    上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...

  2. SpringMVC源码分析--容器初始化(三)HttpServletBean

    在上一篇博客springMVC源码分析--容器初始化(二)DispatcherServlet中,我们队SpringMVC整体生命周期有一个简单的说明,并没有进行详细的源码分析,接下来我们会根据博客中提 ...

  3. springMVC源码分析--容器初始化(二)DispatcherServlet

    在上一篇博客springMVC源码分析--容器初始化(一)中我们介绍了spring web初始化IOC容器的过程,springMVC作为spring项目中的子项目,其可以和spring web容器很好 ...

  4. springMVC源码分析--容器初始化(一)ContextLoaderListener

    在spring Web中,需要初始化IOC容器,用于存放我们注入的各种对象.当tomcat启动时首先会初始化一个web对应的IOC容器,用于初始化和注入各种我们在web运行过程中需要的对象.当tomc ...

  5. springMVC源码分析--SimpleUrlHandlerMapping(四)

    上一篇博客springMVC源码分析--AbstractUrlHandlerMapping(三)中我们介绍了AbstractUrlHandlerMapping,主要介绍了一个handlerMap的ur ...

  6. springMVC源码分析--HttpRequestHandlerAdapter(四)

    上一篇博客springMVC源码分析--HandlerAdapter(一)中我们主要介绍了一下HandlerAdapter接口相关的内容,实现类及其在DispatcherServlet中执行的顺序,接 ...

  7. springMVC源码分析--DispatcherServlet请求获取及处理

    在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...

  8. springMVC源码分析--HandlerAdapter(一)

    HandlerAdapter的功能实际就是执行我们的具体的Controller.Servlet或者HttpRequestHandler中的方法. 类结构如下:

  9. 框架-springmvc源码分析(一)

    框架-springmvc源码分析(一) 参考: http://www.cnblogs.com/heavenyes/p/3905844.html#a1 https://www.cnblogs.com/B ...

随机推荐

  1. thymeltesys-基于Spring Boot Oauth2的扫码登录框架

    thymeltesys thymelte是一个基于Spring Boot Oauth2的扫码登录框架,使用PostgreSQL存储数据,之后会慢慢支持其他关系型数据库.即使你不使用整个框架,只使用其中 ...

  2. 非Unicode编码的软件如何在Windows系统上运行

    我们常常会遇到这样一种情况:点开某些日文软件(我不会说就是galgame( ╯□╰ ))会出现乱码或者直接无法运行. 出现乱码的原因很简单:编码与译码的方式不一致!!!!!!!!!!! 首先大家需要知 ...

  3. Linux下双网卡Firewalld的配置流程

    实验室拟态存储的项目需要通过LVS-NAT模式通过LVS服务器来区隔内外网的服务,所以安全防护的重心则落在了LVS服务器之上.笔者最终选择通过firewalld放行端口的方式来实现需求,由于firew ...

  4. CNN中的经典结构之AlexNet

    AlexNet的基本结构 Alexnet是由5个卷积层和三个全连接层组成,一共8个权重层(池化层不是权重层因为其没有参数),其中ReLU激活函数作用在每个卷积层和全连接层上,在第一个卷积层和第二个卷积 ...

  5. axios的兼容性处理

    一.简介 看看官网的简介: "Promise based HTTP client for the browser and node.js" 译:基于 Promise 的 HTTP ...

  6. 托管C++、C++/CLI、CLR

    1.什么是托管C++? 在回答这个问题,首先要搞清楚什么是"托管"(Managed).托管是.NET的一个专门概念,它是融于通用语言运行时(CLR)中的一种新的编程理念,因此我们完 ...

  7. Pickle模块数据对象持久化操作

    Pickle模块的作用是持久化(序列化)的储存数据.因此我先解释下:什么是序列化与反序列化.什么是对象序列化和对象反序列化.通俗地讲,它们的定义如下:序列化: 把一个对象保存到一个文件或数据库字段中去 ...

  8. ACM Piggy Bank

    Problem Description Before ACM can do anything, a budget must be prepared and the necessary financia ...

  9. ACM Let the Balloon Rise

    Contest time again! How excited it is to see balloons floating around. But to tell you a secret, the ...

  10. Matlab中数据的存储方式

    简介 MATLAB提供了丰富的算法以及一个易于操作的语言,给算法研发工作者提供了很多便利.然而MATLAB在执行某些任务的时候,执行效率偏低,测试较大任务量时可能会引起较长时间的等待.未解决这个问题, ...