使用spring,我们在web.xml都会配置ContextLoaderListener

  <listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

//由于他继承自ContextLoader,加载的时候当然会先加载他啦,那我们来看看这个类

}
ContextLoader这个类有一段静态代码块
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";//这个文件是在org.springframework.web.context.ContextLoader.properties他里面是

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext //这个后面再研究 以后就会把spring所有重要的东西都能看到了。

    private static final Properties defaultStrategies;

    static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);//根据上面的properties文件,就能得到一个默认的properties了。
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}

第一步:我们在看看ContextLoaderListener的这个方法,在容器启动的时候,他会扫描web.xml的listener配置,然后自动调用Listener的contextInitialized初始化方法。

public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}

第二步:initWebApplicationContext初始化web应用上下文

    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);//判断是否有WebApplicationContext,没有则创建,创建得到的是ConfigurableWebApplicationContext
}
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);//决定根Web应用程序环境是否存在父应用程序环境,一般是返回null
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);//在这里是初始化web应用上下文的入口 ,spring的依赖注入也从这里开始了。
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);//把创建的根Web应用程序环境保存到Servlet环境中,每个派遣器Servlet加载的子环境会应用这个环境作为父环境 ClassLoader ccl = Thread.currentThread().getContextClassLoader();//取得线程的类加载器
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;//如果线程和本类拥有相同的类加载器,则使用静态变量保存即可,因为同一类加载器加载同一份静态变量
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);//如果线程和本类拥有不同的类加载器,则使用线程的类加载器作为键值保存在一个映射对象里,保证析构时能拿到Web应用程序环境进行关闭操作
} 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);//如果产生任何异常,则保存异常对象到Servlet环境里
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);//如果产生任何错误,则保存错误对象到Servlet环境里
throw err;
}
}

第三步:得到具体的应用上下文

public static final String CONTEXT_CLASS_PARAM = "contextClass";

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

private static final Properties defaultStrategies;

//这个静态代码块,会去加载相同目录下的ContextLoader.properties,这个目录是org.springframework.web.context,这个配置文件的配置信息就只有org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
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());
}
} protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);//决定要得到哪个ContextClass
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); //实例化Web应用程序环境类
} protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);//获取web.xml配置的contextClass
if (contextClassName != null) {//配置了contextClass,返回具体的Class
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());//在上面的静态代码块中已经加载了XmlWebApplicationContext,返回的是XmlWebApplicationContext的全路径名:org.springframework.web.context.support.XmlWebApplicationContext
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());//没有配置contextClass,则通过配置文件,返回了XmlWebApplicationContext的class
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}

第四步:用于封装ApplicationContext数据并且初始化所有相关Bean对象。它会从web.xml中读取名为contextConfigLocation的配置,这就是spring xml数据源设置,然后放到ApplicationContext中,最后调用传说中的refresh方法执行所有Java对象的创建。

    public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";  在wen.xml中配置的位置

    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);
//设置Web应用程序环境的ID
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
//如果 Servlet规范 <= 2.4,则使用web.xml里定义的应用程序名字定义Web应用程序名
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getServletContextName()));
}
else {
// 如果Servlet规范是 2.5, 则使用配置的ContextPath定义Web应用程序名
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
}
//保存Servlet环境
wac.setServletContext(sc);
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);//这里得到"contextConfigLocation"配置的值:classpath*:spring-config.xml,
if (initParameter != null) {
wac.setConfigLocation(initParameter);//设置保存ConfigLocation,待后面加载的时候,扫描里面的配置
}
customizeContext(sc, wac);//提供子类可互换Web应用程序环境的机会 占位符方法
wac.refresh();//刷新Web应用程序环境以加载Bean定义,这里才是把我们XML里定义的bean放入容器的时候
}
"contextConfigLocation"在web.xml的配置
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:spring-config.xml,
</param-value>
</context-param>

第五步:bean的注入

    public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//告诉子类刷新内部bean工厂。 // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);//准备bean工厂给上下文使用 try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);//允许在上下文后处理bean工厂的子类 // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);//调用处理器工厂注册的bean上下文 // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);注册bean拦截器拦截bean的创建 // Initialize message source for this context.
initMessageSource();//给上下文初始化消息来源 // Initialize event multicaster for this context.
initApplicationEventMulticaster();//给上下文初始化事件多播 // Initialize other special beans in specific context subclasses.//在特定上下文初始化其他特殊bean子类
onRefresh(); // Check for listener beans and register them.
registerListeners();//检查监听器bean并注册他们 // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);//实例化所有剩余(non-lazy-init)单例对象 // Last step: publish corresponding event.
finishRefresh();//发布相应的事件
} catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();//销毁已经创建的单例,避免悬挂的资源 // Reset 'active' flag.
cancelRefresh(ex);//重置“活跃”标志 // Propagate exception to caller.
throw ex;
}
}
}

spring容器启动的加载过程(一)的更多相关文章

  1. spring容器启动的加载过程(三)

    第十步: public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { /** * Load bean def ...

  2. spring容器启动的加载过程(二)

    第六步: public abstract class AbstractApplicationContext extends DefaultResourceLoader implements Confi ...

  3. 微服务架构 | *2.3 Spring Cloud 启动及加载配置文件源码分析(以 Nacos 为例)

    目录 前言 1. Spring Cloud 什么时候加载配置文件 2. 准备 Environment 配置环境 2.1 配置 Environment 环境 SpringApplication.prep ...

  4. 1. spring5源码 -- Spring整体脉络 IOC加载过程 Bean的生命周期

    可以学习到什么? 0. spring整体脉络 1. 描述BeanFactory 2. BeanFactory和ApplicationContext的区别 3. 简述SpringIoC的加载过程 4. ...

  5. Tomcat源码分析三:Tomcat启动加载过程(一)的源码解析

    Tomcat启动加载过程(一)的源码解析 今天,我将分享用源码的方式讲解Tomcat启动的加载过程,关于Tomcat的架构请参阅<Tomcat源码分析二:先看看Tomcat的整体架构>一文 ...

  6. 深入理解 spring 容器,源码分析加载过程

    Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC ...

  7. spring启动component-scan类扫描加载过程(转)

    文章转自 http://www.it165.net/pro/html/201406/15205.html 有朋友最近问到了 spring 加载类的过程,尤其是基于 annotation 注解的加载过程 ...

  8. 【Spring源码分析系列】启动component-scan类扫描加载过程

    原文地址:http://blog.csdn.net/xieyuooo/article/details/9089441/ 在spring 3.0以上大家都一般会配置一个Servelet,如下所示: &l ...

  9. Spring源码:Spring IoC容器加载过程(1)

    Spring源码版本:4.3.23.RELEASE 一.加载过程概览 Spring容器加载过程可以在org.springframework.context.support.AbstractApplic ...

随机推荐

  1. macos上安装wget命令

    首先从Apple Store下载Xcode,然后安装Xcode,接着安装Homebrew包管理,类似于Ubuntu下的apt-get: 终端下输入ruby -e "$(curl -fsSL ...

  2. js判断数组里面的值是否相等。

    var zhi=[]; var zhiT=[]; //var arr=["a","b","a","a"]; var ar ...

  3. Android 6.0之权限管理

    安卓6.0的权限体系分为非敏感权限和敏感权限,非敏感权限默认获取,可以手动关闭. 敏感权限必须由app在运行时动态申请.而存储读写空间权限是一个敏感权限,不是一个“很正常的必须权限”. 安卓并不是想要 ...

  4. linux upstart启动配置

    程序名.conf放在/etcc/init/目录下# 注释 description "your-server" author "xxx" start on run ...

  5. DrawerLayout,ToolBar 和 TabHost 的使用

    ActionBar 定义起来不方便 toolbar: 最重要的特性,显示menu菜单,右上角三个点的菜单按钮,使用灵活 使用:1,布局文件,包裹LinearLayout 放imageView, 或者I ...

  6. jsp/servlet中的编码问题

    首先声明以下只是我个人的看法,有部分观点与网上人云亦云的观点不一样,不过凡事必恭亲,我还是相信自己测试的结果 推荐一个很好地URL编码详解http://www.ruanyifeng.com/blog/ ...

  7. Tyvj-超级书架

    描述 Farmer John最近为奶牛们的图书馆添置了一个巨大的书架,尽管它是如此的大,但它还是几乎瞬间就被各种各样的书塞满了.现在,只有书架的顶上还留有一点空间. 所有N(1 <= N < ...

  8. $.ajax()方法详解 jquery中的ajax方法

    jquery中的ajax方法参数总是记不住,这里记录一下. 1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为String类型的参数,请求方式(p ...

  9. 关于单选按钮在提交时获取所选择的选项得value值问题

    在此使用jQuery,别忘记引用. radio在使用时若要判断选中的是哪一个一定要注意区分input的name值.以此来判断你所获取的单选按钮的value值.直接上代码: <body> & ...

  10. SuperSocket源码解析之开篇 (转)

    一 简介 官方介绍:SuperSocket 是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架.你无须了解如何使用 Socket, 如何维护 Socket 连接和 S ...