主题

之前Spring相关的一些类,比如Enviromnent,BenFactory都接触了一些.有一些收获.但是直接看代码很多方法都不知为什么这样写.哪里会用到.因为太底层了.不好理解..现在从高层次的调用开始再研究下.应该会有新的理解.

所以从一个Web应用初始化开始学习.看看它经历了哪些步骤.做了哪些事情.

之前对spring的dispatcherServlet有一点点研究(http://www.cnblogs.com/abcwt112/p/5283674.html).

ContextLoaderListener

1个最普通的WEB项目如果要在servlet环境中用Spring.肯定是在web.xml里配置1个listener.这个linstener是1个入口,在内部肯定会创建Spring相关的applicationcontext并配置它.

 /*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package org.springframework.web.context; import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener; /**
* Bootstrap listener to start up and shut down Spring's root {@link WebApplicationContext}.
* Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
*
* <p>This listener should be registered after {@link org.springframework.web.util.Log4jConfigListener}
* in {@code web.xml}, if the latter is used.
*
* <p>As of Spring 3.1, {@code ContextLoaderListener} supports injecting the root web
* application context via the {@link #ContextLoaderListener(WebApplicationContext)}
* constructor, allowing for programmatic configuration in Servlet 3.0+ environments.
* See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 17.02.2003
* @see org.springframework.web.WebApplicationInitializer
* @see org.springframework.web.util.Log4jConfigListener
*/
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { /**
* Create a new {@code ContextLoaderListener} that will create a web application
* context based on the "contextClass" and "contextConfigLocation" servlet
* context-params. See {@link ContextLoader} superclass documentation for details on
* default values for each.
* <p>This constructor is typically used when declaring {@code ContextLoaderListener}
* as a {@code <listener>} within {@code web.xml}, where a no-arg constructor is
* required.
* <p>The created application context will be registered into the ServletContext under
* the attribute name {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
* and the Spring application context will be closed when the {@link #contextDestroyed}
* lifecycle method is invoked on this listener.
* @see ContextLoader
* @see #ContextLoaderListener(WebApplicationContext)
* @see #contextInitialized(ServletContextEvent)
* @see #contextDestroyed(ServletContextEvent)
*/
public ContextLoaderListener() {
} /**
* Create a new {@code ContextLoaderListener} with the given application context. This
* constructor is useful in Servlet 3.0+ environments where instance-based
* registration of listeners is possible through the {@link javax.servlet.ServletContext#addListener}
* API.
* <p>The context may or may not yet be {@linkplain
* org.springframework.context.ConfigurableApplicationContext#refresh() refreshed}. If it
* (a) is an implementation of {@link ConfigurableWebApplicationContext} and
* (b) has <strong>not</strong> already been refreshed (the recommended approach),
* then the following will occur:
* <ul>
* <li>If the given context has not already been assigned an {@linkplain
* org.springframework.context.ConfigurableApplicationContext#setId id}, one will be assigned to it</li>
* <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to
* the application context</li>
* <li>{@link #customizeContext} will be called</li>
* <li>Any {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}s
* specified through the "contextInitializerClasses" init-param will be applied.</li>
* <li>{@link org.springframework.context.ConfigurableApplicationContext#refresh refresh()} will be called</li>
* </ul>
* If the context has already been refreshed or does not implement
* {@code ConfigurableWebApplicationContext}, none of the above will occur under the
* assumption that the user has performed these actions (or not) per his or her
* specific needs.
* <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
* <p>In any case, the given application context will be registered into the
* ServletContext under the attribute name {@link
* WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and the Spring
* application context will be closed when the {@link #contextDestroyed} lifecycle
* method is invoked on this listener.
* @param context the application context to manage
* @see #contextInitialized(ServletContextEvent)
* @see #contextDestroyed(ServletContextEvent)
*/
public ContextLoaderListener(WebApplicationContext context) {
super(context);
} /**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
} /**
* Close the root web application context.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
} }

既然是1个listener.spring相关步骤肯定写在listener的contextInitialized方法里.内部很简单的调用了父类的initWebApplicationContext方法,并传入了servletContext对象作为参数.看方法名就知道这个方法肯定是要初始化WebApplicationContext.

 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(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 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 {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == 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);
}
}
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);
} 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_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}

几个比较重要的步骤是:

1. 19行this.context = createWebApplicationContext(servletContext);

初始化1个WebApplicationContext,默认是XmlWebApplicationContext通过BeanUtils.instantiateClass创建的,XmlWebApplicationContext这个类名是写在org\springframework\web\context\ContextLoader.properties里的.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

不喜欢的话自己也可以通过servletContext里去配置contextClass这个参数指定class来配置WebApplicationContext.但是我想一般没人这么做吧.

2. 32行configureAndRefreshWebApplicationContext(cwac, servletContext);

初始化wac.大概是最重要的入口了.后面再仔细看.

3. 35行servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

如果前面初始化成功的话就把wac绑定到servletContext的attr中(org.springframework.web.context.WebApplicationContext.ROOT),.所以其实我们在servlet环境中如果要获取wac,要么通过ApplicationContextAware要么通过这个servletContext的attr...都是可以的.

configureAndRefreshWebApplicationContext方法

     protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
} else {
// Generate default id...
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);
} // 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) {
// 一开始env里的propertySources里面的servletContextInitParams和servletContextConfigParams都是空的,需要用相应的servlet的值去替换
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
} customizeContext(sc, wac);
wac.refresh();
}

然后我们来看看这个最重要的初始化wac的方法.

L15 wac.setServletContext(sc); wac要初始化需要servlet环境相关的数据.

L16.获取servletContext里配置的contextConfigLocation的值.这个变量大家肯定不陌生.这个就是需要加载的spring配置文件的路径地址.

L18 contextConfigLocation值设置给wac.所以wac初始化需要spring的配置文件(废话).

L24-28 让wac的env去加载servlet环境相关的数据.因为之前wac是用beanUtils创建的.创建的时候并不知道你当前的环境有什么变量.所以这里需要加载一下servlet环境的properties.

initPropertySources有2个参数.第一个servletContext.第二个是servletConfig.很明显.这里是在listener而不是springMVC的dispatcherServlet里,所以这里servletConfig是null.

L30  customizeContext(sc, wac);在wac启动之前允许你定制一下.

     /**
* Customize the {@link ConfigurableWebApplicationContext} created by this
* ContextLoader after config locations have been supplied to the context
* but before the context is <em>refreshed</em>.
* <p>The default implementation {@linkplain #determineContextInitializerClasses(ServletContext)
* determines} what (if any) context initializer classes have been specified through
* {@linkplain #CONTEXT_INITIALIZER_CLASSES_PARAM context init parameters} and
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
* given web application context.
* <p>Any {@code ApplicationContextInitializers} implementing
* {@link org.springframework.core.Ordered Ordered} or marked with @{@link
* org.springframework.core.annotation.Order Order} will be sorted appropriately.
*
* @param sc the current servlet context
* @param wac the newly created application context
* @see #CONTEXT_INITIALIZER_CLASSES_PARAM
* @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
*/
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
// 找到 web.xml里配置的 globalInitializerClasses 和 contextInitializerClasses 对应的class
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
// 一般情况下没人配置,所以是empty.
if (initializerClasses.isEmpty()) {
// no ApplicationContextInitializers have been declared -> nothing to do
return;
} // 如果这些ApplicationContextInitializer存在的话就调用他们的initialize方法.
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>(); for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if (initializerContextClass != null) {
Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
"Could not add context initializer [%s] since its generic parameter [%s] " +
"is not assignable from the type of application context used by this " +
"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
wac.getClass().getName()));
}
initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
} AnnotationAwareOrderComparator.sort(initializerInstances);
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
initializer.initialize(wac);
}
}

可以参考我写的注释.就是允许你在servletContext中配置2个参数 globalInitializerClasses 和 contextInitializerClasses.他们对应的类都是ApplicationContextInitializer的实现.如果你配置了.那这里会调用

initializer.initialize(wac);

这个方法执行你的回调函数.

我记得我第一家公司这里好像加载了一些properties文件..(虽然我现在想想不明白为什么要在这里加载而不是直接配置在spring的配置文件中....或者直接使用@PropertySource或者@ConfigurationProperties)..算是1个spring的扩展点吧.

L31 前面的配置也做完了..那就真正开始初始化wac了.执行wac.refresh();刷新wac.

小结

主要学习了XmlWebApplicationContext刷新前的ContextLoaderListener做的一些预备工作..比如:

1.默认加载的是哪个wac.你也可以自己定制.

2.env读取servletContext与Config的参数

3.自定义的customer,ApplicationContextInitializer

4.初始化wac成功以后绑定到servletContext的attr中

等等...

后续准备研究wac是怎么refresh的.

Spring 学习记录7 初识XmlWebApplicationContext的更多相关文章

  1. Spring 学习记录8 初识XmlWebApplicationContext(2)

    主题 接上文Spring 学习记录7 初识XmlWebApplicationContext refresh方法 refresh方法是定义在父类AbstractApplicationContext中的. ...

  2. 我的Spring学习记录(二)

    本篇就简单的说一下Bean的装配和AOP 本篇的项目是在上一篇我的Spring学习记录(一) 中项目的基础上进行开发的 1. 使用setter方法和构造方法装配Bean 1.1 前期准备 使用sett ...

  3. 我的Spring学习记录(四)

    虽然Spring管理这我们的Bean很方便,但是,我们需要使用xml配置大量的Bean信息,告诉Spring我们要干嘛,这还是挺烦的,毕竟当我们的Bean随之增多的话,xml的各种配置会让人很头疼. ...

  4. 我的Spring学习记录(五)

    在我的Spring学习记录(四)中使用了注解的方式对前面三篇做了总结.而这次,使用了用户登录及注册来对于本人前面四篇做一个应用案例,希望通过这个来对于我们的Spring的使用有一定的了解. 1. 程序 ...

  5. Spring 学习记录3 ConversionService

    ConversionService与Environment的关系 通过之前的学习(Spring 学习记录2 Environment),我已经Environment主要是负责解析properties和p ...

  6. Spring 学习记录6 BeanFactory(2)

    主题 除了Spring 学习记录5 BeanFactory 里写的几个接口外,BeanFactory的实现类还实现了一些其他接口,这篇文章主要介绍这些接口和实现类. 结构 DefaultListabl ...

  7. Spring学习记录(九)---通过工厂方法配置bean

    1. 使用静态工厂方法创建Bean,用到一个工厂类 例子:一个Car类,有brand和price属性. package com.guigu.spring.factory; public class C ...

  8. Spring学习记录(七)---表达式语言-SpEL

    SpEL---Spring Expression Language:是一个支持运行时查询和操作对象图表达式语言.使用#{...}作为定界符,为bean属性动态赋值提供了便利. ①对于普通的赋值,用Sp ...

  9. 【Android】学习记录<1> -- 初识ffmpeg

    工作需要用到ffmpeg来进行Android的软编码,对这玩意儿一点都不了解,做个学习记录先. FFmpeg:http://www.ffmpeg.org Fmpeg is the leading mu ...

随机推荐

  1. BZOJ1510 POI2006 Kra-The Disks 【模拟】

    BZOJ1510 POI2006 Kra-The Disks LINK 还是粘题面吧,但是图就算了 Description Johnny 在生日时收到了一件特殊的礼物,这件礼物由一个奇形怪状的管子和一 ...

  2. BZOJ1096 ZJOI2007 仓库建设 【斜率优化DP】

    BZOJ1096 ZJOI2007 仓库建设 Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原内陆地区(干燥少雨),L公司一般 ...

  3. Navicat工具导出mySQL数据库某个视图结构的.sql脚本

    用Navicat工具怎么都导不出来mySQL数据库的某个视图.sql脚本,即使导出来也只是包含视图记录,不包含视图结构.经过一番研究,终于克服,操作如下: 1.在某个数据库中,新建备份,如下图 2.选 ...

  4. 《DSP using MATLAB》示例Example 6.20

  5. flask第十九篇——模板【3】

    请关注微信公众号:自动化测试实战 今天我们继续模板的知识,现在我们增加字典的复杂度,这个时候在render_template第二个参数可以传**title,以后我们会用**context代替原来的ti ...

  6. hdu4261 Estimation[暴力dp+对顶堆]

    https://vjudge.net/problem/HDU-4261 对于一个长2000的数列划分最多25个块,每块代价为块内每个数与块内中位数差的绝对值之和,求最小总代价. 套路化地,设$f[i] ...

  7. node 升级

    npm install -g n   npm update –g    

  8. MySQL5.6版本性能调优my.cnf详解

    [client] port = 3306 socket = /tmp/mysql.sock [mysqld] port = 3306 socket = /tmp/mysql.sock basedir  ...

  9. oracle的时间

    今天发现之前从oracle导出的数据时间格式很奇怪: "ACTIVETIME","ACTIVITYID" "2018-08-10 06:08:43&q ...

  10. SVN checkout 出的项目在PHPstorm中打开没有subversion(SVN)选项按钮怎么办?

    对于svn add 命令的批量操作,为了操作简便还是习惯在IDE中完成,有时候新checkout出的项目,在PHPstorm中右键菜单中没有 Subversion 按钮,操作如下: 点击VCS按钮,然 ...