入口DispatcherServlet
启动类ContextLoaderListener
当Web容器启动时,触发监听
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
这个方法在基类中实现,建立IoC容器和生成WebApplicationContext
public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
先看这个WebApplicationContext接口
public interface WebApplicationContext extends ApplicationContext {
/**
* Context attribute to bind root WebApplicationContext to on successful startup.
* <p>Note: If the startup of the root context fails, this attribute can contain
* an exception or error as value. Use WebApplicationContextUtils for convenient
* lookup of the root WebApplicationContext.
* @see org.springframework.web.context.support.WebApplicationContextUtils#getWebApplicationContext
* @see org.springframework.web.context.support.WebApplicationContextUtils#getRequiredWebApplicationContext
*/
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
/**
* Scope identifier for request scope: "request".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_REQUEST = "request";
/**
* Scope identifier for session scope: "session".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_SESSION = "session";
/**
* Scope identifier for global session scope: "globalSession".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
String SCOPE_GLOBAL_SESSION = "globalSession";
/**
* Scope identifier for the global web application scope: "application".
* Supported in addition to the standard scopes "singleton" and "prototype".
*/
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/PortletContext init-params environment bean in the factory.
* <p>Note: Possibly merged with ServletConfig/PortletConfig 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/PortletContext attributes environment bean in the factory.
* @see javax.servlet.ServletContext#getAttributeNames()
* @see javax.servlet.ServletContext#getAttribute(String)
*/
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
/**
* Return the standard Servlet API ServletContext for this application.
* <p>Also available for a Portlet application, in addition to the PortletContext.
*/
ServletContext getServletContext();
}
这个接口就将servlet上下文引入了
在上面的创建WebApplicationContext时的代码
this.context = createWebApplicationContext(servletContext);
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
...
}
/**
* Return the WebApplicationContext implementation class to use, either the
* default XmlWebApplicationContext or a custom context class if specified.
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
...
}
默认情况下都是使用XmlWebApplicationContext,所以看下这个实现类吧.
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** Default config location for the root context */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/** Default prefix for building a config location for a namespace */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
/** Default suffix for building a config location for a namespace */
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@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);
}
/**
* Initialize the bean definition reader used for loading the bean
* definitions of this context. Default implementation is empty.
* <p>Can be overridden in subclasses, e.g. for turning off XML validation
* or using a different XmlBeanDefinitionParser implementation.
* @param beanDefinitionReader the bean definition reader used by this context
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
*/
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
* therefore this method is just supposed to load and/or register bean definitions.
* <p>Delegates to a ResourcePatternResolver for resolving location patterns
* into Resource instances.
* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
/**
* The default location for the root context is "/WEB-INF/applicationContext.xml",
* and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"
* (like for a DispatcherServlet instance with the servlet-name "test").
*/
@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};
}
}
}
这里配置了一些web上才用得到的属性,比如默认的xml名字和目录等.除了获取xml信息外,其他的IoC容器功能都在基类中实现了
开始之前的initWebApplicationContext()
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
...
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {//设置根上下文的双亲上下文
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
return this.context;
...
}
获取ServletContext然后读取一些信息,创建初始化WebApplicationContext,并将其作为根上下文,并绑定在ServletContext上.可以通过WebApplicationContextUtils.getWebApplicationContext()来获取
创建根上下文方法
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
这方法和以前不一样了.这里并没有设置什么属性,也没有调用refresh()只是简简单单的实例化了这个对象,然而这些动作都在外面一层的下一句.优雅~~~
configureAndRefreshWebApplicationContext(cwac, servletContext);
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
wac.refresh();
}