在《Spring源码学习:源码分析概述》的示代码中使如下式去加载Spring的配置件并
初始化容,
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationgContext.xml");
在web应中,配置件都是动加载的,示代码中的式就能满需求。在web应中
使Spring,需要在web.xml中添加如下配置。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml;
</param-value>
</context-param>
先解下ServletContext和ServletContextListener。ServletContext定义些法
Servlet和Servlet容进通讯,在个web应中所有的Servlet都公个ServletContext,
Spring在和web应结合使的时候,是将Spring的容存到ServletContext中的,通俗的说就是
将个ApplicationContext存储到ServletContext的个Map属性中;ServletContextListener
于监听ServletContext些事件。分析就从ContextLoaderListener开始。在web应启动读取
web.xml时,发现配置ContextLoaderListener,ContextLoaderListener实现
ServletContextListener接,因此会执ContextLoaderListener类中的contextInitialized法,
法的具体代码如下。
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
继续进initWebApplicationContext法,这个法在其类ContextLoader实现,根据法名
可以看出这个法是于初始化个WebApplicationContext,简单解就是初始化个Web应
下的Spring容。法的具体代码如下。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if
(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_AT
TRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context
present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
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_AT
TRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
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");
}
return this.context;
}catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_AT
TRIBUTE, ex);
throw ex;
}catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_AT
TRIBUTE, err);
throw err;
}
}
法的第就是检查servletContext是否已经存储个默认名称的WebApplicationContext,
因为在个应中Spring容只能有个,所以需要校验,于这个默认的名称为么是
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,看到后就会慢
慢明。直接关注重点代码,代码如下。
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
这的context是ContextLoader的个变,声明代码如下。
private WebApplicationContext context;
继续进createWebApplicationContext法,具体代码如下。
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);
}
这个法主要于创建个WebApplicationContext对象。因为WebApplicationContext只是个
接,能创建对象,所以需要找到个WebApplicationContext接的实现类,
determineContextClass法就是于寻找实现类,如果开发员在web.xml中配置个参数名
为contextClass,值为WebApplicationContext接实现类,那就会返回这个配置的实现类
Class;如果没有配置,则会返回Spring默认的实现类XmlWebApplicationContext。直接进
determineContextClass法体,代码如下。
protected Class<?> determineContextClass(ServletContext servletContext) {
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);
}
}
}
上的代码中使defaultStrategies,于获取Spring默认的WebApplicationContext接实现
类XmlWebApplicationContext.java,它的声明如下。
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
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());
}
}
也就是说在ContextLoader.java的径下,有个ContextLoader.properties件,查找并打开这
个件,件内容如下。
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.s
upport.XmlWebApplicationContext
这配置Spring默认的WebApplicationContext接实现类XmlWebApplicationContext.java。
回到createWebApplicationContext法,WebApplicationContext接实现类的Class已经找到,
然后就是使构造函数进初始化完成WebApplicationContext对象创建。
继续回到initWebApplicationContext法,此时这个context就指向刚刚创建的
WebApplicationContext对象。因为XmlWebApplicationContext间接实现
ConfigurableWebApplicationContext接,所以将会执如下代码。
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);
}
}
这关注重点代码configureAndRefreshWebApplicationContext(cwac, servletContext),
configureAndRefreshWebApplicationContext法具体代码如下。
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();
}
还是样,关注重点代码。看下CONFIG_LOCATION_PARAM这个常的值
是”contextConfigLocation”,OK,这个就是web.xml中配置applicationContext.xml的。这个参
数如果没有配置,在XmlWebApplicationContext中是有默认值的,具体的值如下。
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
也就是说,如果没有配置contextConfigLocation参数,将会使/WEB-
INF/applicationContext.xml。关注configureAndRefreshWebApplicationContext法的最后
代码wac.refresh(),是是有点眼熟,继续跟踪代码,refresh法的具体代码如下。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh
attempt", ex);
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}
这个refresh法就是《Spring源码学习四:BeanDefinition装载前奏曲》介绍的那个refresh
法,于完成Bean的解析、实化及注册。
继续分析,回到initWebApplicationContext法,将执如下代码。
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_AT
TRIBUTE, this.context);
可以看到,这将初始化后的context存到servletContext中,具体的就是存到个Map变
中,key值就是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这
个常。
使Spring的WebApplicationContextUtils具类获取这个WebApplicationContext式如下。
WebApplicationContext applicationContext =
WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
评论0
最新资源