Spring WebApplicationContext

WebApplicationContext是專門爲web應用準備的,他允許從相對於web根目錄的路勁中裝載配置文件完成初始化工作,從WebApplicationContext中可以獲得ServletContext的引用,整個Web應用上下文對象將作爲屬性放置在ServletContext中,以便web應用可以訪問spring上下文,spring中提供WebApplicationContextUtils的getWebApplicationContext(ServletContext src)方法來獲得WebApplicationContext對象

繼承結構

WebApplicationContext

WebApplicationContext擴展了ApplicationContext的Web應用能力。WebApplicationContext中定義了一個常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文啓動時,WebApplicationContext以此爲鍵放置在ServletContext屬性列表中。


public interface WebApplicationContext extends ApplicationContext {
	String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
	String SCOPE_REQUEST = "request";
	String SCOPE_SESSION = "session";
	String SCOPE_APPLICATION = "application";
	/**
	 * Name of the ServletContext environment bean in the factory.
	 * @see javax.servlet.ServletContext
	 */
	String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
	/**
	 * Name of the ServletContext init-params environment bean in the factory.
	 * <p>Note: Possibly merged with ServletConfig parameters.
	 * ServletConfig parameters override ServletContext parameters of the same name.
	 * @see javax.servlet.ServletContext#getInitParameterNames()
	 * @see javax.servlet.ServletContext#getInitParameter(String)
	 * @see javax.servlet.ServletConfig#getInitParameterNames()
	 * @see javax.servlet.ServletConfig#getInitParameter(String)
	 */
	String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";

	/**
	 * Name of the ServletContext attributes environment bean in the factory.
	 * @see javax.servlet.ServletContext#getAttributeNames()
	 * @see javax.servlet.ServletContext#getAttribute(String)
	 */
	String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
	@Nullable
	ServletContext getServletContext();

}

ConfigurableWebApplicationContext

ConfigurableWebApplicationContext綜合了ConfigurableApplicationContext的配置能力以及WebApplicationContext的Web應用能力。

它允許通過配置的方式實例化WebApplicationContext,同時設置兩個重要方法:

//爲spring設置web應用上下文,以便兩者整合
setServletContext(ServletContext context) 
//設置Spring配置的文件地址
setConfigLocations(String[]locations) 

 


public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {

	/**
	 * Prefix for ApplicationContext ids that refer to context path and/or servlet name.
	 */
	String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";

	/**
	 * Name of the ServletConfig environment bean in the factory.
	 * @see javax.servlet.ServletConfig
	 */
	String SERVLET_CONFIG_BEAN_NAME = "servletConfig";
	void setServletContext(@Nullable ServletContext servletContext);
	void setServletConfig(@Nullable ServletConfig servletConfig);
	@Nullable
	ServletConfig getServletConfig();
	void setNamespace(@Nullable String namespace);
	@Nullable
	String getNamespace();
	void setConfigLocation(String configLocation);
	void setConfigLocations(String... configLocations);
	@Nullable
	String[] getConfigLocations();

}

AbstractRefreshableApplicationContext

AbstractRefreshableApplicationContext繼承自AbstractApplicationContext,支持多次進行刷新(多次調用refresh方法),每次刷新時在內部創建一個新的bean工廠的實例。子類僅僅需要實現loadBeanDefinitions方法,該方法在每次刷新時都會調用。



public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {

    //允許Bean定義覆蓋
	@Nullable
	private Boolean allowBeanDefinitionOverriding;

	@Nullable
    //允許解決循環引用
	private Boolean allowCircularReferences;

	/** Bean factory for this context. */
	@Nullable
	private DefaultListableBeanFactory beanFactory;

	/** Synchronization monitor for the internal BeanFactory. */
	private final Object beanFactoryMonitor = new Object();

	public AbstractRefreshableApplicationContext() {
	}
	public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
		super(parent);
	}
	public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
		this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
	}
	public void setAllowCircularReferences(boolean allowCircularReferences) {
		this.allowCircularReferences = allowCircularReferences;
	}


	/**
	 * 重新加載BeanFactory,先銷燬再創建新的
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

	@Override
	protected void cancelRefresh(BeansException ex) {
		synchronized (this.beanFactoryMonitor) {
			if (this.beanFactory != null) {
				this.beanFactory.setSerializationId(null);
			}
		}
		super.cancelRefresh(ex);
	}

	@Override
	protected final void closeBeanFactory() {
		synchronized (this.beanFactoryMonitor) {
			if (this.beanFactory != null) {
				this.beanFactory.setSerializationId(null);
				this.beanFactory = null;
			}
		}
	}

	/**
	 * Determine whether this context currently holds a bean factory,
	 * i.e. has been refreshed at least once and not been closed yet.
	 */
	protected final boolean hasBeanFactory() {
		synchronized (this.beanFactoryMonitor) {
			return (this.beanFactory != null);
		}
	}

	@Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		synchronized (this.beanFactoryMonitor) {
			if (this.beanFactory == null) {
				throw new IllegalStateException("BeanFactory not initialized or already closed - " +
						"call 'refresh' before accessing beans via the ApplicationContext");
			}
			return this.beanFactory;
		}
	}

	/**
	 * Overridden to turn it into a no-op: With AbstractRefreshableApplicationContext,
	 * {@link #getBeanFactory()} serves a strong assertion for an active context anyway.
	 */
	@Override
	protected void assertBeanFactoryActive() {
	}

	/**
    默認爲DefaultListableBeanFactory
	 */
	protected DefaultListableBeanFactory createBeanFactory() {
		return new DefaultListableBeanFactory(getInternalParentBeanFactory());
	}
	protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		if (this.allowBeanDefinitionOverriding != null) {
			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.allowCircularReferences != null) {
			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
	}

	/**
	 * 加載bean定義到給定beanFactory,通常通過 bean definition readers 加載
	 */
	protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
			throws BeansException, IOException;

}

AbstractRefreshableConfigApplicationContext

AbstractRefreshableConfigApplicationContext繼承自AbstractRefreshableApplicationContext,添加了對指定的配置文件路徑的公共的處理(ConfigurableApplicationContext接口),可以把他看作基於XML的應用上下文的基類。實現瞭如下的兩個接口:

  • BeanNameAware

用於設置上下文的bean的名稱,只有一個方法:void setBeanName(String name)

  • InitializingBean

用於上下文一切就緒後,如果還未刷新,那麼就執行刷新操作,只有一個方法:void afterPropertiesSet()

AbstractRefreshableWebApplicationContext

AbstractRefreshableWebApplicationContext繼承自AbstractRefreshableConfigApplicationContext,實現了ConfigurableWebApplicationContextThemeSource接口,主要是用於web環境下。在web程序啓動的時候,提供一個configLocations屬性,通過ConfigurableWebApplicationContext接口來進行填充。子類化這個接口是很簡單的,所有你所需要做的事情就是實現loadBeanDefinitions方法,來實現你自己的bean定義的加載邏輯。
 

public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
		implements ConfigurableWebApplicationContext, ThemeSource {

	/** Servlet context that this context runs in. */
	@Nullable
	private ServletContext servletContext;

	/** Servlet config that this context runs in, if any. */
	@Nullable
	private ServletConfig servletConfig;

	/** Namespace of this context, or {@code null} if root. */
	@Nullable
	private String namespace;

	/** the ThemeSource for this ApplicationContext. */
	@Nullable
	private ThemeSource themeSource;


	public AbstractRefreshableWebApplicationContext() {
		setDisplayName("Root WebApplicationContext");
	}


	@Override
	public void setServletContext(@Nullable ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	@Override
	@Nullable
	public ServletContext getServletContext() {
		return this.servletContext;
	}

	@Override
	public void setServletConfig(@Nullable ServletConfig servletConfig) {
		this.servletConfig = servletConfig;
		if (servletConfig != null && this.servletContext == null) {
			setServletContext(servletConfig.getServletContext());
		}
	}

	@Override
	@Nullable
	public ServletConfig getServletConfig() {
		return this.servletConfig;
	}

	@Override
	public void setNamespace(@Nullable String namespace) {
		this.namespace = namespace;
		if (namespace != null) {
			setDisplayName("WebApplicationContext for namespace '" + namespace + "'");
		}
	}

	@Override
	@Nullable
	public String getNamespace() {
		return this.namespace;
	}

	@Override
	public String[] getConfigLocations() {
		return super.getConfigLocations();
	}

	@Override
	public String getApplicationName() {
		return (this.servletContext != null ? this.servletContext.getContextPath() : "");
	}

	/**
	 * Create and return a new {@link StandardServletEnvironment}. Subclasses may override
	 * in order to configure the environment or specialize the environment type returned.
	 */
	@Override
	protected ConfigurableEnvironment createEnvironment() {
		return new StandardServletEnvironment();
	}

	/**
	 * Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
	 */
	@Override
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
		beanFactory.ignoreDependencyInterface(ServletContextAware.class);
		beanFactory.ignoreDependencyInterface(ServletConfigAware.class);

		WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
	}

	/**
	 * This implementation supports file paths beneath the root of the ServletContext.
	 * @see ServletContextResource
	 */
	@Override
	protected Resource getResourceByPath(String path) {
		Assert.state(this.servletContext != null, "No ServletContext available");
		return new ServletContextResource(this.servletContext, path);
	}

	/**
	 * This implementation supports pattern matching in unexpanded WARs too.
	 * @see ServletContextResourcePatternResolver
	 */
	@Override
	protected ResourcePatternResolver getResourcePatternResolver() {
		return new ServletContextResourcePatternResolver(this);
	}

	/**
	 * Initialize the theme capability.
	 */
	@Override
	protected void onRefresh() {
		this.themeSource = UiApplicationContextUtils.initThemeSource(this);
	}

	/**
	 * {@inheritDoc}
	 * <p>Replace {@code Servlet}-related property sources.
	 */
	@Override
	protected void initPropertySources() {
		ConfigurableEnvironment env = getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
		}
	}

	@Override
	@Nullable
	public Theme getTheme(String themeName) {
		Assert.state(this.themeSource != null, "No ThemeSource available");
		return this.themeSource.getTheme(themeName);
	}

AnnotationConfigWebApplicationContext

接受註解的類作爲輸入(特殊的@Configuration註解類,一般的@Component註解類,與JSR-330兼容的javax.inject註解)。允許一個一個的注入,同樣也能使用類路徑掃描。對於web環境,基本上是和AnnotationConfigApplicationContext等價的。使用AnnotatedBeanDefinitionReader來對註解的bean進行處理,使用ClassPathBeanDefinitionScanner來對類路徑下的bean進行掃描。

屬性

	@Nullable
	private BeanNameGenerator beanNameGenerator;

	@Nullable
	private ScopeMetadataResolver scopeMetadataResolver;

	private final Set<Class<?>> componentClasses = new LinkedHashSet<>();

	private final Set<String> basePackages = new LinkedHashSet<>();

loadBeanDefinitions

	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
        //獲取註解bean定義 reader
		AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
        //獲取classpath bean定義 scanner
		ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);

        //獲取bean name生成器
		BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
		if (beanNameGenerator != null) {
			reader.setBeanNameGenerator(beanNameGenerator);
			scanner.setBeanNameGenerator(beanNameGenerator);
			beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
		}

		ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
		if (scopeMetadataResolver != null) {
			reader.setScopeMetadataResolver(scopeMetadataResolver);
			scanner.setScopeMetadataResolver(scopeMetadataResolver);
		}

        //組件類,
		if (!this.componentClasses.isEmpty()) {
			if (logger.isDebugEnabled()) {
				logger.debug("Registering component classes: [" +
						StringUtils.collectionToCommaDelimitedString(this.componentClasses) + "]");
			}
            //註冊組件類
			reader.register(ClassUtils.toClassArray(this.componentClasses));
		}

		if (!this.basePackages.isEmpty()) {
			if (logger.isDebugEnabled()) {
				logger.debug("Scanning base packages: [" +
						StringUtils.collectionToCommaDelimitedString(this.basePackages) + "]");
			}
            //掃描包
			scanner.scan(StringUtils.toStringArray(this.basePackages));
		}

		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				try {
					Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
					if (logger.isTraceEnabled()) {
						logger.trace("Registering [" + configLocation + "]");
					}
                    //註冊 配置
					reader.register(clazz);
				}
				catch (ClassNotFoundException ex) {
					if (logger.isTraceEnabled()) {
						logger.trace("Could not load class for config location [" + configLocation +
								"] - trying package scan. " + ex);
					}
					int count = scanner.scan(configLocation);
					if (count == 0 && logger.isDebugEnabled()) {
						logger.debug("No component classes found for specified class/package [" + configLocation + "]");
					}
				}
			}
		}
	}

    

AnnotatedBeanDefinitionReader 參考:Spring BeanDefinition加載

ClassPathBeanDefinitionScanner 參考:Spring BeanDefinition加載​​​​​​​

XmlWebApplicationContext

使用XML作爲配置的Web應用。

public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {

	/** 默認 config location. */
	public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
	public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
	public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";


	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}

	/**
	 * 初始化bean definition reader,默認爲空
	 */
	protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
	}

	/**
	 * Load the bean definitions with the given XmlBeanDefinitionReader.
	 */
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				reader.loadBeanDefinitions(configLocation);
			}
		}
	}
	@Override
	protected String[] getDefaultConfigLocations() {
		if (getNamespace() != null) {
			return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
		}
		else {
			return new String[] {DEFAULT_CONFIG_LOCATION};
		}
	}

}

XmlBeanDefinitionReader 參考:

 

ServletContext

webApplicationContext初始化需要ServletContext,也就是說需要web容器前提下才能完成啓動工作  ,可以通過在web.xml中配置自啓動Servlet或Web容器監聽來實現web容器的啓動
Spring提供啓動WebApplicationContext的servlet和Web容器監聽器org.springframework.web.context.ContextLoaderListener 

Xml配置方式

!--從類路徑下加載Spring配置文件,classpath特指類路徑下加載-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:smart-context.xml
        </param-value>
    </context-param>
    <!--負責啓動spring容器的監聽器  還可以聲明自啓動的Servlet   ContextLoaderServlet-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

@Configuration配置方式

 <!--通過指定context參數,讓Spring使用AnnotationConfigWebApplicationContext啓動容器而非XmlWebApplicationContext   默認沒配置時是使用XmlWebApplicationContext-->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>
<!--指定標註了@Configuration的類,多個可以用逗號分隔-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.example.Car,com.example.Boss</param-value>
    </context-param>
    <!--監聽器將根據上面的配置使用AnnotationConfigWebApplicationContext
    根據contextConfigLocation
    指定的配置類啓動Spring容器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

 

發佈了174 篇原創文章 · 獲贊 21 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章