spring之:XmlWebApplicationContext作为SpringWeb应⽤。。。
它既是 DispatcherServlet 的 (WebApplicationContext)默认策略,⼜是 ContextLoaderListener 创建 root WebApplicationContext(根容器,同时也是 DispatcherServlet 的 WebApplicationContext 的⽗容器)的默认策略。
继承体系
⼀、XmlWebApplicationContext实例化过程
spring-web-4.3.14.RELEASE.jar中的org.t.ContextLoader.java类,通过ContextLoader初始化和销毁Spring Web上下⽂的过程。
1、ContextLoader类中有⼀个静态代码块,这个静态代码块就是从配置中读取到“XmlWebApplicationContext”类
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
//DEFAULT_STRATEGIES_PATH = "ContextLoader.properties",即加载的是contextLoader.properties的配置⽂件了
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());
}
}
"contextLoader.properties"⽂件就在ContextLoader.class的相同⽬录中,
contextLoader.properties:(配置中配置的就是XmlWebApplicationContext)
org.t.WebApplicationContext=org.t.support.XmlWebApplicationContext
2、上⾯只是将配置读取到ContextLoader中,下⾯看看XmlWebApplicationContext怎么初始化的,ContextLoader.initWebApplicationContext⽅法:
org.t.ContextLoader.java
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//检查是否已经创建了Application context,如果已经存在,抛异常退出
if (Attribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in l!");
}
Log logger = Log(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
/
/ it is available on ServletContext shutdown.
if (t == null) {
//调⽤createWebApplicationContext,创建XmlWebApplicationContext,
}
// 如果当前的应⽤上下⽂对象是 ConfigurableWebApplicationContext
if (t instanceof ConfigurableWebApplicationContext) {
//强制类型转换getattribute方法返回类型
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) t;
// 如果应⽤上下⽂没有⽣效
if (!cwac.isActive()) {
/
/ The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
//// 如果该上下⽂对象为nul
if (Parent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
//加载⽗上下⽂
ApplicationContext parent = loadParentContext(servletContext);
// 设置⽗上下⽂
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
//将该上下⽂对象放⼊servlet上下⽂参数中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, t);
//获取当前线程的类加载器
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
// 如果ContextLoader的类加载器和当前线程的类加载器⼀样,则应⽤上下⽂对象赋值给currentContext
if (ccl == ClassLoader()) {
currentContext = t;
}
//否则,就将ContextLoader的类加载器放⼊到Map中,Map的value是应⽤上下⽂对象
else if (ccl != null) {
currentContextPerThread.put(ccl, t);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
/
/最后返回应⽤上下⽂对象
t;
}
catch (RuntimeException ex) {
<("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
<("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
在ateWebApplicationContext⽅法中
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//获取上下⽂类
Class<?> contextClass = determineContextClass(sc);
//如果该上下⽂类没有实现ConfigurableWebApplicationContext接⼝则抛出异常
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + Name() +
"] is not of type [" + Name() + "]");
}
// 返回该上下⽂类的实例,调⽤BeanUtils.instantiateClass(contextClass),通过反射,调⽤XmlWebApplicationContext的⽆参构造函数实例化XmlWebApplicationContext对象return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
----------------------------BeanUtils.instantiateClass()-----------------------------------------------------------------------------------------------------------------------
这⾥插⼊BeanUtils.instantiateClass(),BeanUtils使⽤instantiateClass初始化对象注意:必须保证初始化类必须有public默认⽆参数构造器,注意初始化内部类时,内部类必须是静态的,否则报错!
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
return DeclaredConstructor());
}
catch (NoSuchMethodException ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
public static <T> T instantiateClass(Constructor<T> ctor, args) throws BeanInstantiationException {
try {
ReflectionUtils.makeAccessible(ctor);
wInstance(args);
}
//...
}
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = CallerClass();
checkAccess(caller, clazz, null, modifiers);
}
if ((Modifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor;  // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
----------------------------BeanUtils.instantiateClass()-----------------------------------------------------------------------------------------------------------------------ContextLoader.java中的determineContextClass()⽅法:
/**
* 返回上下⽂类型
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
//从servlet上下⽂中获取初始化配置参数contextClass的值
String contextClassName = InitParameter(CONTEXT_CLASS_PARAM);
// 如果contextClassName不为null则放回配置的Class对象
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, DefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
/
/ 如果没有配置则使⽤XmlWebApplicationContext,这个代码就是从contextLoad.properties配置中加载进来的,配置中的就是XmlWebApplicationContext
contextClassName = Property(Name());
try {
return ClassUtils.forName(contextClassName, ClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
⾸先在l中我们可以看到如下配置:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/spring/*.xml</param-value>
</context-param>
<listener>
<listener-class>org.t.ContextLoaderListener</listener-class>
</listener>
ContextLoaderListener继承Spring的ContextLoader上下⽂加载器类,同时实现ServletContextListener接⼝(Servlet上下⽂),监听Web服务器上下⽂的启动和停⽌事件,管理Web环境中Spring的启动和销毁过程,
⾸先我们看看这个的源码。初始化的⼊⼝是contextInitialized⽅法,它只是简单地将初始化功能委
托为了ContextLoader进⾏处理。
org.t.ContextLoaderListener.java
/**
* Initialize the root web application context.初始化根WEB应⽤上下⽂
*/
@Override
public void contextInitialized(ServletContextEvent event) {
ServletContext()); //调⽤ContextLoader的initWebApplicationContext()
}
通过对ContextLoaderListener的源码分析,我们看到ContextLoaderListener继承ContextLoader,所以ContextLoaderListener本⾝也是Spring的上下⽂加载器。
ContextLoaderListener实现了ServletContextListener接⼝,当Web应⽤在Web服务器中被被启动和停⽌时,Web服务器启动和停⽌事件会分别触发ContextLoaderListener的contextInitialized和contextDestroyed⽅法来初始化和销毁Spring上下⽂。我们通过上述对ContextLoaderListener的源码分析看到真正实现Spring上下⽂的初始化和销毁功能的是ContextLoader类,分析ContextLoader初始化和销毁Spring Web上下⽂的过程见上⾯。
ContextLoader的initWebApplicationContext()的源码见上⾯的分析。
1、springmvc加载配置⽂件
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>l</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
org.springframework.web.servlet.DispatcherServlet是通过这个servlet,加载配置⽂件
FrameworkServlet中有⼀个属性
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
接着看FrameworkServlet的initWebApplicationContext()⽅法:
protected WebApplicationContext initWebApplicationContext() {
.
....
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
}
⽅法中有⼀个ateWebApplicationContext(rootContext)⽅法
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//这个⽅法就是创建XmlWebApplicationContext实例的Class
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
}
//如果该上下⽂类没有实现ConfigurableWebApplicationContext接⼝则抛出异常
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + Name() +
"] is not of type ConfigurableWebApplicationContext");
}
/
/调⽤BeanUtils.instantiateClass(contextClass),通过反射,调⽤XmlWebApplicationContext的⽆参构造函数实例化XmlWebApplicationContext对象  ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
configureAndRefreshWebApplicationContext(wac);
return wac;
}
接⼝看getContextClass()⽅法:
Class<?> contextClass = getContextClass();这个⽅法就是创建XmlWebApplicationContext实例的Class
public Class<?> getContextClass() {
tClass;
}
⾄此,实例化XmlWebApplicationContext的步骤基本相同:
1、通过读取配置⽂件⽅式,读取到org.t.WebApplicationContext的类型
为“org.t.support.XmlWebApplicationContext”;
2、检查上下⽂类没有实现ConfigurableWebApplicationContext接⼝则抛出异常;
3、调⽤BeanUtils.instantiateClass(contextClass),通过反射,调⽤XmlWebApplicationContext的⽆参构造函数实例化XmlWebApplicationContext对象;
⼆、XmlWebApplicationContext源码
ContextLoader初始化Spring Web上下⽂的determineContextClass⽅法中,我们知道Spring⾸先通过Servlet上下⽂从l⽂件中获取⽤户⾃定义配置的contextClass参数值,如果没有获取到,则默认使⽤Spring的XmlWebApplicationContext作为Spring Web应⽤的IoC容
器,XmlWebApplicationContext是WebApplicationContext的实现类ConfigurableWebApplicationContext的⼦类
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
//Web应⽤中Spring配置⽂件的默认位置和名称,如果没有特别指定,则Spring会根据
//此位置定义Spring Bean定义资源
public static final String DEFAULT_CONFIG_LOCATION = "/l";
//Spring Bean定义资源默认前缀
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
//Spring Bean定义资源默认后置
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
//在分析Spring IoC初始化过程中我们已经分析过,加载Spring Bean定义资源的⽅法,
//通过Spring容器刷新的refresh()⽅法触发
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//为Spring容器创建XML Bean定义读取器,加载Spring Bean定义资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// resource loading environment.
beanDefinitionReader.setEnvironment(getEnvironment());
//设置Bean定义读取器,因为XmlWebApplicationContext是DefaultResourceLoader的⼦类,所以使⽤默认资源加载器来定义Bean定义资源
beanDefinitionReader.setResourceLoader(this);
//为Bean定义读取器设置SAX实体解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//在加载Bean定义之前,调⽤⼦类提供的⼀些⽤户⾃定义初始化Bean定义读取器的⽅法
initBeanDefinitionReader(beanDefinitionReader);
//使⽤Bean定义读取器加载Bean定义资源
loadBeanDefinitions(beanDefinitionReader);
}
//⽤户⾃定义初始化Bean定义读取器的⽅法
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
//加载Bean定义资源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
//获取定位的Bean定义资源路径
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//遍历加载所有定义的Bean定义资源
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
//获取默认Bean定义资源
protected String[] getDefaultConfigLocations() {
//获取l中的命名空间,如命名空间不为null,则返回 “/WEB-INF/命名空间.xml”
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
//如果命名空间为null,则返回"/l"
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
}
XmlWebApplicationContext将Web应⽤中配置的Spring Bean定义资源⽂件载⼊到Spring IoC容器中后,接下来的Spring IoC容器初始化和依赖注⼊的过程后⾯再分析。