SpringMVC源码 2 WebApplicationContext
1.上文总结:
上一篇中讲了一些,关于SpringMVC 在Servlet容器启动过程中ServletContext的构建,以及Spring中ContextLoaderListener和ContextLoader在初始化过程中的初始化流程以及创建的一些内容。
1.在Servlet容器启动的时候,创建应用全局上下文ServletContext,读取应用配置文件web.xml。<context-param>和<listener>
2.Spring
ContextLoaderListener监听器被创建,调用contextInitialized方法。进入ContextLoader的初始化过程
3.在ContextLoader的初始化过程中,首先创建了一个WebApplicationContext,然后初始化IOC。调用AbstractApplicationContext的refresh()方法。
下面会主要讲解创建WebApplicationContext的过程,初始化IOC可以去Spring AbstractApplicationContext相关的笔记中。
2.WebApplicationContext的子类
WebApplicationContext说白了就是一个上下文,一个IOC容器。只是加入了Servlet的ServletContext。
默认使用XMLWebApplicationContext
在WebApplicationCotnext主要定义了一个 public ServletContext getServletContext()的抽象方法。可以说Web应用上下文与一般Spring程序上下文的主要区别就是对Servlet容器的ServletContext操作的区别。
其中主要的子类包含了
GeneticWebApplicationContext:
XmlWebApplicationContext: xml文件配置的。ContextLoad默认创建的WebApplicationContext。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config />
<!-- 自动扫描 -->
<context:component-scan base-package="com.maodq" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<bean id="captcha" class="com.
maodq
.web.Captcha" >
<property name="yunSuCaptcha" ref="yunSuCaptcha" />
<property name="juHe" ref="juHe" />
</bean>
<bean id="yunSuCaptcha" class="com.
maodq
.util.YunSuCaptcha" />
<bean id="juHe" class="com.
maodq
.util.JuHe" />
<context:property-placeholder location="classpath*:*.properties" />
</beans>
GroovyWebApplicationContext:groovy文件配置的。
AnnotationConfigWebApplicationContext : 注解形式的web应用上下文。
/**
* 配置spring,导入properties文件、xml文件,设置扫描范围
*/
@Configuration
@ComponentScan("com.myhexin")
@PropertySource({"classpath:redis.properties","classpath:dev.properties"
,"classpath:http.properties","classpath:dubbo.properties","classpath:captcha.properties"})
@ImportResource({"classpath:provider.xml","classpath:consumer.xml","classpath:shutdown.xml"})
public class SpringConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
1层 public interface
WebApplicationContext
extends ApplicationContext {
定义了一些常量,主要是String SCOPE_XXXXX和 String XXXX_XXXX_BEAN_BAME
定义了一个抽象方法,获取ServletContext
ServletContext getServletContext();
子接口ConfigurableWebApplicationContext
2层 public interface
ConfigurableWebApplicationContext
extends WebApplicationContext, ConfigurableApplicationContext
定义了两个常量: 上下文的前缀:"WebApplicationContext" 和 ServletConfig的bean名称
String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
String SERVLET_CONFIG_BEAN_NAME = "servletConfig";
定义了8个抽象方法:主要是对ServletContext Set操作。ServletConfig的Get/Set操作。 对ConfigLocation(配置文件的路径)的Get/Set操作
void setServletContext(ServletContext servletContext);
void setServletConfig(ServletConfig servletConfig);
ServletConfig getServletConfig();
void setNamespace(String namespace);
String getNamespace();
void setConfigLocation(String configLocation);
ConfigLocation来自于web.xml中的参数。可以参看笔记:SpringMVC高级 1 ContextLoaderListener和Servlet容器web.xml配置.当然一些具体的WebApplicationCOntext子类中有默认的配置文件路径。例如XmlWebApplicationContext配置文件默认路径是 /WEB-INF/applicationContext.xml
void setConfigLocations(String... configLocations);
String[] getConfigLocations();
3层 public class
GenericWebApplicationContext
extends GenericApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource
定义了2个变量
private ServletContext servletContext;
private ThemeSource themeSource;
还定义了一些对ServletConfig、ServletContext等的getter和setter
3层 public abstract class
AbstractRefreshableWebApplicationContext
extends AbstractRefreshableConfigApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource
定义了四个变量。这四个就是WebApplicationContext与Servlet容器交互的变量。也是与常规上下文最重要的区别。
private ServletContext servletContext;
private ServletConfig servletConfig;
private String namespace;
private ThemeSource themeSource;
还定义了一些对ServletConfig、ServletContext等的getter和setter
AbstractRefreshableWebApplicationContext(){
setDisplayName("Root WebApplicationContext") //displayName设置为 Root WebApplicationContext
}
4层 public
class
XmlWebApplicationContext
extends
AbstractRefreshableWebApplicationContext
{
定义3个常量。关于XML的。1.默认配置文件路径。2.默认配置文件前缀(文件目录) 3.默认配置文件后缀(文件类型xml)
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"
;
4层 public
class
AnnotationConfigWebApplicationContext
extends
AbstractRefreshableWebApplicationContext
implements
AnnotationConfigRegistry
AnnotationConfigWebApplicationContext和
AnnotationConfigApplicationContext都实现了这个接口
定义4个常量。关于注解的。1 2 3.注解类 4.扫描的包
private
BeanNameGenerator
beanNameGenerator
;
private
ScopeMetadataResolver
scopeMetadataResolver
;
private
final
Set
<
Class
<?>>
annotatedClasses
=
new
LinkedHashSet
<
Class
<?>>
()
;
private
final
Set
<
String
>
basePackages
=
new
LinkedHashSet
<
String
>
()
;
4层 public
class
GroovyWebApplicationContext
extends
AbstractRefreshableWebApplicationContext
implements
GroovyObject
定义3个常量。关于groovy的。性质和XML差不多
public
static
final
String
DEFAULT_CONFIG_LOCATION
=
"/WEB-INF/applicationContext.groovy"
;
public
static
final
String
DEFAULT_CONFIG_LOCATION_PREFIX
=
"/WEB-INF/"
;
public
static
final
String
DEFAULT_CONFIG_LOCATION_SUFFIX
=
".groovy"
;
3.WebApplicationContext的创建细节
this.context = createWebApplicationContext(servletContext); //开始创建WebApplicationcontext。
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//根据ServletContext中“contextClass”的参数去创建对应的WebApplication,如果没有这个参数,则通过去加载ContextLoader.properties文件中的默认参数。(XMLWebApplicationContext)
Class<?> contextClass =
determineContextClass
(sc);
//需要是ConfigurableWebApplicationContext的子类
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);
}
protected Class<?> determineContextClass(ServletContext servletContext) {
//CONTEXT_CLASS_PARAM="contextClass" 从ServletContext(web.xml)中获取“contextClass”的全类名param。
//如果这个参数不存在,会去defaultStrategies中加载数据。
//在ContextLoader中有一段代码,用来创建
defaultStrategies。通过
读取类同级目录下的ContextLoader.properties文件。文件内容如下
//org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
//这样通过获取了一个WebApplication 的全类名,通过反射的方式创建了实例
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}catch (ClassNotFoundException ex) {
throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", ex);
}
}else {
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}catch (ClassNotFoundException ex) {
throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
private static final Properties defaultStrategies;
static {
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
configureAndRefreshWebApplicationContext(cwac, servletContext); //配置和刷新web应用上下文,这才是启动sprigIoc的关键
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
//设置上下文的id。CONTEXT_ID_PARAM=“contextId”
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + //默认id
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
//对WebApplication设置Servlet容器的ServletContext。
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
//配置spring的配置文件路径 XmlWebApplicationContext默认为 “/WEB-INF/applicationContext.xml”
}
// 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(sc, null);
}
customizeContext(sc, wac);
wac.refresh(); 刷新上线文,具体上下文初始化操作,可以看AbstractApplicationContext.refresh()。
}
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextId</param-name>
<param-value>MMM-WebApplicationContext</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
参考
http://www.cnblogs.com/brolanda/p/4265597.html