Spring web 工具类 WebApplicationContextUtils

编程入门 行业动态 更新时间:2024-10-24 18:23:43

概述

Spring web工具WebApplicationContextUtils 位于包

org.springframework.web.context.support

是访问一个ServletContext的根WebApplicationContext的便捷方法类。该工具类提供了如下工具方法 :

  1. web容器启动过程中注册web相关作用域bean : request,session , globalSession , application

  2. web容器启动过程中注册相应类型的工厂bean,开发人员依赖注入相应的bean时能访问到正确的请求/响应/会话对象 : ServletRequest,ServletResponse,HttpSession,WebRequest

  3. web容器启动过程中注册web相关环境bean : contextParameters , contextAttributes

  4. web容器启动过程中初始化servlet propertySources

  5. 在客户化web视图(custom web view)或者MVC action中,使用该工具类可以很方便地在程序中访问Spring应用上下文(application context)。

源码解析

package org.springframework.web.context.support;

// 省略 imports

/**
 * Convenience methods for retrieving the root WebApplicationContext for
 * a given ServletContext. This is useful for programmatically accessing
 * a Spring application context from within custom web views or MVC actions.
 *
 * Note that there are more convenient ways of accessing the root context for
 * many web frameworks, either part of Spring or available as an external library.
 * This helper class is just the most generic way to access the root context.
 *
 * @author Juergen Hoeller
 * @see org.springframework.web.context.ContextLoader
 * @see org.springframework.web.servlet.FrameworkServlet
 * @see org.springframework.web.servlet.DispatcherServlet
 * @see org.springframework.web.jsf.FacesContextUtils
 * @see org.springframework.web.jsf.el.SpringBeanFacesELResolver
 */
public abstract class WebApplicationContextUtils {

	private static final boolean jsfPresent =
			ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());


	/**
	 * Find the root WebApplicationContext for this web app, typically
	 * loaded via org.springframework.web.context.ContextLoaderListener.
	 * 
	 * 根据参数sc指定的ServletContext,找到当前web应用的根WebApplicationContext,该根WebApplicationContext
	 * 典型情况下由加载org.springframework.web.context.ContextLoaderListener,
	 * 并记录为ServletContext的属性,属性名称使用
	 * WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 
	 * 
	 * Will rethrow an exception that happened on root context startup,
	 * to differentiate between a failed context startup and no context at all.
	 *  
	 * 如果根上下文(root context)启动过程中有异常发生,这里会把该异常重新抛出;如果根本找不到根上下文,
	 * 抛出另外一个异常IllegalStateException,带如下错误信息 : 
	 * No WebApplicationContext found: no ContextLoaderListener registered?
	 * 
	 * @param sc ServletContext to find the web application context for
	 * @return the root WebApplicationContext for this web app
	 * @throws IllegalStateException if the root WebApplicationContext could not be found
	 * @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
	 */
	public static WebApplicationContext getRequiredWebApplicationContext(ServletContext sc) 
		throws IllegalStateException {
		WebApplicationContext wac = getWebApplicationContext(sc);
		if (wac == null) {
			throw new IllegalStateException(
				"No WebApplicationContext found: no ContextLoaderListener registered?");
		}
		return wac;
	}

	/**
	 * Find the root WebApplicationContext for this web app, typically
	 * loaded via org.springframework.web.context.ContextLoaderListener.
	 * 
	 * 根据参数sc指定的ServletContext,找到当前web应用的根WebApplicationContext,该根WebApplicationContext
	 * 典型情况下由加载org.springframework.web.context.ContextLoaderListener,
	 * 并记录为ServletContext的属性,属性名称使用
	 * WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
	 * 	 
	 * Will rethrow an exception that happened on root context startup,
	 * to differentiate between a failed context startup and no context at all.
	 * 
	 * 如果根上下文(root context)启动过程中有异常发生,这里会把该异常重新抛出;如果根本找不到根上下文,
	 * 返回null,注意,这一点是该方法和方法getRequiredWebApplicationContext不同的地方

	 * @param sc ServletContext to find the web application context for
	 * @return the root WebApplicationContext for this web app, or null if none
	 * @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
	 */
	public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
		return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
	}

	/**
	 * Find a custom WebApplicationContext for this web app.
	 * 
	 * 根据指定的名称找到ServletContext的某个WebApplicationContext,
	 * 这些WebApplicationContext会在容器启动过程中添加为ServletContext实例特定名字的属性对象,
	 * 如果这些WebApplicationContext在容器准备他们的过程中遇到了异常,相应的属性对象记录的是相应的异常
	 * @param sc ServletContext to find the web application context for
	 * @param attrName the name of the ServletContext attribute to look for
	 * @return the desired WebApplicationContext for this web app, or null if none
	 */
	public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
		Assert.notNull(sc, "ServletContext must not be null");
		Object attr = sc.getAttribute(attrName);
		if (attr == null) {
		//如果该属性不存在,返回null
			return null;
		}
		if (attr instanceof RuntimeException) {
		// 如果该属性对象是运行时异常,抛出该异常,这种情况说明容器在启动过程中准备该WebApplicationContext
		// 遇到了异常
			throw (RuntimeException) attr;
		}
		if (attr instanceof Error) {
		// 如果该属性对象是错误,抛出该错误,这种情况说明容器在启动过程中准备该WebApplicationContext
		// 遇到了异常		
			throw (Error) attr;
		}
		if (attr instanceof Exception) {
		// 如果该属性对象是一般异常,抛出该异常,这种情况说明容器在启动过程中准备该WebApplicationContext
		// 遇到了异常		
			throw new IllegalStateException((Exception) attr);
		}
		if (!(attr instanceof WebApplicationContext)) {
		// 如果该属性对象不是异常,不是错误,也不是WebApplicationContext,抛出一个异常表明该问题
			throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
		}
		return (WebApplicationContext) attr;
	}

	/**
	 * Find a unique WebApplicationContext for this web app: either the
	 * root web app context (preferred) or a unique WebApplicationContext
	 * among the registered ServletContext attributes (typically coming
	 * from a single DispatcherServlet in the current web application).
	 * 
	 * 找到当前Web应用唯一的那个WebApplicationContext:或者是缺省根web应用上下文(期望的情况),
	 * 或者是注册的ServletContext属性中唯一的那个WebApplicationContext(典型情况下,来自当前
	 * web应用中的DispatcherServlet)。如果发现注册的ServletContext属性中有多个WebApplicationContext,
	 * 会抛出异常声明发现了多个WebApplicationContext。
	 * 
	 * Note that DispatcherServlet's exposure of its context can be
	 * controlled through its publishContext property, which is true
	 * by default but can be selectively switched to only publish a single context
	 * despite multiple DispatcherServlet registrations in the web app.
	 * @param sc ServletContext to find the web application context for
	 * @return the desired WebApplicationContext for this web app, or null if none
	 * @since 4.2
	 * @see #getWebApplicationContext(ServletContext)
	 * @see ServletContext#getAttributeNames()
	 */
	public static WebApplicationContext findWebApplicationContext(ServletContext sc) {
		// 尝试获取根web应用上下文,如果找得到则返回根web应用上下文
		WebApplicationContext wac = getWebApplicationContext(sc);
		if (wac == null) {
		// 如果没有找到根web应用上下文,尝试从ServletContext的属性中查找唯一的一个WebApplicationContext
		// 并返回,如果找到的WebApplicationContext不唯一,则抛出异常声明该情况
			Enumeration<String> attrNames = sc.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = attrNames.nextElement();
				Object attrValue = sc.getAttribute(attrName);
				if (attrValue instanceof WebApplicationContext) {
					if (wac != null) {
						throw new IllegalStateException(
							"No unique WebApplicationContext found: more than one " +
								"DispatcherServlet registered with publishContext=true?");
					}
					wac = (WebApplicationContext) attrValue;
				}
			}
		}
		return wac;
	}


	/**
	 * Register web-specific scopes ("request", "session", "globalSession")
	 * with the given BeanFactory, as used by the WebApplicationContext.
	 * @param beanFactory the BeanFactory to configure
	 */
	public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) {
		registerWebApplicationScopes(beanFactory, null);
	}

	/**
	 * Register web-specific scopes ("request", "session", "globalSession", "application")
	 * with the given BeanFactory, as used by the WebApplicationContext.
	 *  
	 * 向WebApplicationContext使用的BeanFactory注册web有关作用域对象 :
	 * request, session, globalSession, application
	 * @param beanFactory the BeanFactory to configure
	 * @param sc the ServletContext that we're running within
	 */
	public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, 
		ServletContext sc) {
		// 注册web相关作用域bean
		beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
		beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
		beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
		if (sc != null) {
			ServletContextScope appScope = new ServletContextScope(sc);
			beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
			// Register as ServletContext attribute, for ContextCleanupListener to detect it.
			sc.setAttribute(ServletContextScope.class.getName(), appScope);
		}

		// 注册ServletRequest的工厂bean,当开发人员依赖注入ServletRequest对象时,注入的bean其实是这里的
		// RequestObjectFactory工厂bean
		beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
		
		// 注册ServletResponse的工厂bean,当开发人员依赖注入ServletResponse对象时,注入的bean其实是这里的
		// ResponseObjectFactory工厂bean		
		beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
		
		// 注册HttpSession的工厂bean,当开发人员依赖注入HttpSession对象时,注入的bean其实是这里的
		// SessionObjectFactory工厂bean				
		beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
		
		// 注册WebRequest的工厂bean,当开发人员依赖注入WebRequest对象时,注入的bean其实是这里的
		// WebRequestObjectFactory工厂bean			
		beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
		
		if (jsfPresent) {
			FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
		}
	}

	/**
	 * Register web-specific environment beans ("contextParameters", "contextAttributes")
	 * with the given BeanFactory, as used by the WebApplicationContext.
	 * @param bf the BeanFactory to configure
	 * @param sc the ServletContext that we're running within
	 */
	public static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf, ServletContext sc) {
		registerEnvironmentBeans(bf, sc, null);
	}

	/**
	 * Register web-specific environment beans ("contextParameters", "contextAttributes")
	 * with the given BeanFactory, as used by the WebApplicationContext.
	 * @param bf the BeanFactory to configure
	 * @param servletContext the ServletContext that we're running within
	 * @param servletConfig the ServletConfig of the containing Portlet
	 */
	public static void registerEnvironmentBeans(
			ConfigurableListableBeanFactory bf, ServletContext servletContext, ServletConfig servletConfig) {

		if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) {
			bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext);
		}

		if (servletConfig != null && 
				!bf.containsBean(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME)) {
			bf.registerSingleton(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME, 
				servletConfig);
		}

		if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) {
			Map<String, String> parameterMap = new HashMap<String, String>();
			if (servletContext != null) {
				Enumeration<?> paramNameEnum = servletContext.getInitParameterNames();
				while (paramNameEnum.hasMoreElements()) {
					String paramName = (String) paramNameEnum.nextElement();
					parameterMap.put(paramName, servletContext.getInitParameter(paramName));
				}
			}
			if (servletConfig != null) {
				Enumeration<?> paramNameEnum = servletConfig.getInitParameterNames();
				while (paramNameEnum.hasMoreElements()) {
					String paramName = (String) paramNameEnum.nextElement();
					parameterMap.put(paramName, servletConfig.getInitParameter(paramName));
				}
			}
			bf.registerSingleton(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME,
					Collections.unmodifiableMap(parameterMap));
		}

		if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) {
			Map<String, Object> attributeMap = new HashMap<String, Object>();
			if (servletContext != null) {
				Enumeration<?> attrNameEnum = servletContext.getAttributeNames();
				while (attrNameEnum.hasMoreElements()) {
					String attrName = (String) attrNameEnum.nextElement();
					attributeMap.put(attrName, servletContext.getAttribute(attrName));
				}
			}
			bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME,
					Collections.unmodifiableMap(attributeMap));
		}
	}

	/**
	 * Convenient variant of #initServletPropertySources(MutablePropertySources,
	 * ServletContext, ServletConfig) that always provides null for the
	 * ServletConfig parameter.
	 * @see #initServletPropertySources(MutablePropertySources, ServletContext, ServletConfig)
	 */
	public static void initServletPropertySources(MutablePropertySources propertySources, 
		ServletContext servletContext) {
		initServletPropertySources(propertySources, servletContext, null);
	}

	/**
	 * Replace Servlet-based StubPropertySource stub property sources with
	 * actual instances populated with the given servletContext and
	 * servletConfig objects.
	 * This method is idempotent with respect to the fact it may be called any number
	 * of times but will perform replacement of stub property sources with their
	 * corresponding actual property sources once and only once.
	 * @param propertySources the MutablePropertySources to initialize (must not
	 * be null)
	 * @param servletContext the current ServletContext (ignored if null
	 * or if the StandardServletEnvironment#SERVLET_CONTEXT_PROPERTY_SOURCE_NAME
	 * servlet context property source has already been initialized)
	 * @param servletConfig the current ServletConfig (ignored if null
	 * or if the StandardServletEnvironment#SERVLET_CONFIG_PROPERTY_SOURCE_NAME
	 * servlet config property source} has already been initialized)
	 * @see org.springframework.core.env.PropertySource.StubPropertySource
	 * @see org.springframework.core.env.ConfigurableEnvironment#getPropertySources()
	 */
	public static void initServletPropertySources(
			MutablePropertySources propertySources, 
			ServletContext servletContext, ServletConfig servletConfig) {

		Assert.notNull(propertySources, "'propertySources' must not be null");
		if (servletContext != null && 
			propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
				propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) 	
					instanceof StubPropertySource) {
			propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
					new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, 
					servletContext));
		}
		if (servletConfig != null && 
			propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
				propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) 
					instanceof StubPropertySource) {
			propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
					new 	ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, 
					servletConfig));
		}
	}

	/**
	 * Return the current RequestAttributes instance as ServletRequestAttributes.
	 * 
	 * 获取当前RequestAttributes实例,返回类型为ServletRequestAttributes,
	 * 该方法使用RequestContextHolder获取和当前请求处理线程绑定的ServletRequestAttributes对象,
	 * 进而可以获取其中的HttpServletRequest/HttpServletResponse对象
	 * 
	 * 该类定义该方法的目的是给该类的以下四个私有嵌套静态类使用 : 
	 * RequestObjectFactory,ResponseObjectFactory,SessionObjectFactory,WebRequestObjectFactory
	 * @see RequestContextHolder#currentRequestAttributes()
	 */
	private static ServletRequestAttributes currentRequestAttributes() {
		RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
		if (!(requestAttr instanceof ServletRequestAttributes)) {
			throw new IllegalStateException("Current request is not a servlet request");
		}
		return (ServletRequestAttributes) requestAttr;
	}

// 在下面的代码中,该类定义了四个私有嵌套静态类 : 
// RequestObjectFactory,ResponseObjectFactory,SessionObjectFactory,WebRequestObjectFactory
// 这四个静态类是四个工厂类,分别用于生成ServletRequest,ServletResponse,HttpSession,WebRequest对象,
// 当开发人员使用@Autowired分别注入了以上四种类型的bean时,返回的其实是下面四个工厂类的对象,这四个工厂类对象
// 均使用了上面定义的currentRequestAttributes()方法,能从当前请求处理线程中获取正确的ServletRequestAttributes
// 对象,进而获取正确的ServletRequest,ServletResponse,HttpSession,WebRequest对象

	/**
	 * Factory that exposes the current request object on demand.
	 */
	@SuppressWarnings("serial")
	private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

		@Override
		public ServletRequest getObject() {
			return currentRequestAttributes().getRequest();
		}

		@Override
		public String toString() {
			return "Current HttpServletRequest";
		}
	}


	/**
	 * Factory that exposes the current response object on demand.
	 */
	@SuppressWarnings("serial")
	private static class ResponseObjectFactory implements ObjectFactory<ServletResponse>, Serializable {

		@Override
		public ServletResponse getObject() {
			ServletResponse response = currentRequestAttributes().getResponse();
			if (response == null) {
				throw new IllegalStateException("Current servlet response not available - " +
						"consider using RequestContextFilter instead of RequestContextListener");
			}
			return response;
		}

		@Override
		public String toString() {
			return "Current HttpServletResponse";
		}
	}


	/**
	 * Factory that exposes the current session object on demand.
	 */
	@SuppressWarnings("serial")
	private static class SessionObjectFactory implements ObjectFactory<HttpSession>, Serializable {

		@Override
		public HttpSession getObject() {
			return currentRequestAttributes().getRequest().getSession();
		}

		@Override
		public String toString() {
			return "Current HttpSession";
		}
	}


	/**
	 * Factory that exposes the current WebRequest object on demand.
	 */
	@SuppressWarnings("serial")
	private static class WebRequestObjectFactory implements ObjectFactory<WebRequest>, Serializable {

		@Override
		public WebRequest getObject() {
			ServletRequestAttributes requestAttr = currentRequestAttributes();
			return new ServletWebRequest(requestAttr.getRequest(), requestAttr.getResponse());
		}

		@Override
		public String toString() {
			return "Current ServletWebRequest";
		}
	}


	/**
	 * Inner class to avoid hard-coded JSF dependency.
 	 */
	private static class FacesDependencyRegistrar {

		public static void registerFacesDependencies(ConfigurableListableBeanFactory beanFactory) {
			beanFactory.registerResolvableDependency(FacesContext.class, new ObjectFactory<FacesContext>() {
				@Override
				public FacesContext getObject() {
					return FacesContext.getCurrentInstance();
				}
				@Override
				public String toString() {
					return "Current JSF FacesContext";
				}
			});
			beanFactory.registerResolvableDependency(ExternalContext.class, new ObjectFactory<ExternalContext>() {
				@Override
				public ExternalContext getObject() {
					return FacesContext.getCurrentInstance().getExternalContext();
				}
				@Override
				public String toString() {
					return "Current JSF ExternalContext";
				}
			});
		}
	}

}

更多推荐

Spring web 工具类 WebApplicationContextUtils

本文发布于:2023-06-11 01:37:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1367999.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:工具   Spring   web   WebApplicationContextUtils

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!