`
snake_hand
  • 浏览: 574182 次
社区版块
存档分类
最新评论

Spring 源码阅读 之 Spring框架加载

阅读更多

欢迎大家访问我的个人网站 萌萌的IT人

说起第一次阅读Spring Framework源码,大概还是2010年吧,那个时候还不懂技巧和方法,一头扎在代码的汪洋大海里,出不来了。后面几年偶尔断断续续的也看过几次,都是不得要领,最后都是无疾而终。所以觉得阅读这种大型的代码项目,很吃力,也很艰难,需要不断的坚持。 

  最近项目不是很忙,下班早,就又把Spring的源码翻出来看看,也看了一段时间了,这次算是小有收获吧,于是打算学博客记录下来。 

  在这之前,并没有打算在继续写博客,因为这里的这种讨厌的限制,而且也越来越不喜欢这里的风格。但是有觉得,学过的东西,既然有价值,就记录下来吧,好记性不如烂笔头,不记得的时候可以打开看看。在这之前,我的一些学习笔记一直是使用为知笔记来记录的,现在把记录在为知笔记里的内容重新翻出来整理一下,帖子博客上吧。

  好了,开始进入正题吧:

----------------------------------------------------------------------------------------------------

  在使用Spring Framework的时候,各种教程都是介绍首先要在 web.xml 里面配置一个 listener,该 listener 会在web容器启动的时候依次加载,从而来加载Spring Framework。那就从这个 listener 开始阅读吧。

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

 

ContextLoaderListener 实现了 ServletContextListener 接口,因此 ContextLoaderListener 监听了当前Web Application的生命周期,在 Web 容器启动时会调用其实现的 contextInitialized() 方法来加载Spring Framework框架,当容器终止时会调用 其 contextDestroyed() 方法来销毁资源。 ContextLoaderListener类的contextInitialized()方法是这样实现的:

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

 

这个方法很简单,但是我实在不明白框架的设计者为什么要先判断 contextLoader 是否为 null,然后再通过 contextLoader 来调用 initWebApplicationContext() 方法,而为什么不在 contextInitialized()方法里直接调用 initWebApplicationContext() 方法,一行代码就搞定的事,为什么还这么麻烦? 

  为什么会有这样的疑问呢,因为initWebApplicationContext()方法是ContextLoader类的成员方法,而ContextLoaderListener 又继承自 ContextLoader,initWebApplicationContext()方法是用public进行修饰的,因此该方法也得到 ContextLoaderListener 继承,也就是说ContextLoaderListener也拥有此方法。那么作者为什么还要在该类里增加一个ContextLoader类型的成员变量contextLoader,用 contextLoader 来调用 initWebApplicationContext()呢? 看了一下整个ContextLoaderListener类,contextLoader 都是调用其自身的方法,而这些方法也被ContextLoaderListener继承了,实在是想不通。

  进入 initWebApplicationContext() 方法,该方法实现了整个Spring的加载。该方法除去log和try/catch代码,真正的代码也不是很多。外表看似简单,但是里面的逻辑实现,完全超乎我的想象,直接看代码吧: 

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) {
            configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, 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;
    }
}

 

 代码第二行,从 servletContext取出以 org.springframework.web.context.WebApplicationContext.ROOT 为key的对象,如果该对象不等于null,说明当前servletContext已经加载成功了Spring,则直接抛出异常。为什么要做此判断呢?难道是当前Web容器启动了多个Web应用,每个应用都使用了Spring框架框架吗?如果是这样,则每个Web应用都对应着一个单独的ServletContext,是不会出现这种问题的,貌似也就只又在 web.xml里面配置多个 类似的加载 Spring Framework的listener才会触发此问题。好了,那 org.springframework.web.context.WebApplicationContext.ROOT 又是在何时放进 servletContext 的呢? 在第24行的地方,当代码执行到第24行的地方时,说明整个Spring框架已经加载并初始化完毕。   

    代码第29行,调用 createWebApplicationContext() 方法创建Spring Web应用上下文。Spring Web应用上下文是怎么创建的呢?下文在继续。。。

5
6
分享到:
评论
1 楼 jackie_yk 2014-01-13  
引用
这个方法很简单,但是我实在不明白框架的设计者为什么要先判断 contextLoader 是否为 null,然后再通过 contextLoader 来调用 initWebApplicationContext() 方法,而为什么不在 contextInitialized()方法里直接调用 initWebApplicationContext() 方法,一行代码就搞定的事,为什么还这么麻烦?


开发者可以继承此类,然后覆写createContextLoader()生成自己的loader,这样的话,就使用用户自己的loader。这里是留了一个扩展点。

相关推荐

    spring源代码解析

    对于一个Spring激活的web应用程序,可以通过使用Spring代码声明式的指定在web应用程序启动时载入应用程序上下文(WebApplicationContext),Spring的ContextLoader是提供这样性能的类,我们可以使用 ...

    Spring.net框架

    ConfigHandler类将根据该结点下的内容处理并创建一ConfigInfo对象(关于ConfigInfo、 ObjectInfo以及PropertyInfo的代码可自行查看源代码,这里就不再赘述)。ConfigHandler类的代码实现如下: using System; using ...

    spring-framework-5.0.x.zip

    该资源为Spring5.0x原版源码,仅提供学习Spring框架源码使用,编译遇到问题请搜索 “Spring源码学习之路——第二天~加载Spring源码”

    spring加载restful(文档+程序源码)

    spring加载restful(文档+程序源码) 通过REST风格体系架构,请求和响应都是基于资源表示的传输来构建的。资源是通过全局ID来标识的,这些ID一般使用的是一个统一资源标识符(URI)。客户端应用使用HTTP方法(如,...

    Spring高级之注解驱动开发视频教程

    Spring框架是一系列应用框架的核心,也可以说是整合其他应用框架的基座。同时还是SpringBoot的基础。在当下的市场开发环境中,Spring占据的地位是非常高的,基本已经成为了开发者绕不过去的框架了。它里面包含了...

    Spring-Data-JPA快速使用

     《Spring源码深度解析》不仅介绍了使用Spring框架开发项目必须掌握的核心概念,还指导读者如何使用Spring框架编写企业级应用,并针对在编写代码的过程中如何优化代码、如何使得代码高效给出切实可行的建议,从而...

    深入解析Spring IoC:源码与实践指南

    本文深入探讨了Spring IoC容器的加载过程及其源码实现,揭示了Spring中最为根本的概念之一。这包括从AnnotationConfigApplicationContext的实例化开始,到DefaultListableBeanFactory工厂的建立,再到...

    mini-spring:mini-spring是简化版的spring框架,能帮助您快速熟悉spring原型和掌握spring的核心原理。取下spring的核心逻辑,代码极度简化,保留spring的核心功能,如IoC和AOP,资源加载器,事件监听器,类型转换,容器扩展点,bean生命周期和作用域,应用多维等核心功能

    迷你弹簧关于迷你弹簧是Spring框架的简化版本,可帮助您快速熟悉Spring源代码并掌握Spring的核心原理。 提取了Spring的核心逻辑,极大地简化了代码,并完善了Spring的核心功能,例如IoC和AOP,资源加载器,事件侦听...

    基于SSM框架的健康项目管理源码,整合Dubbo分布式与SpringSecurity权限认证

    项目概述:这是一款基于SSM(Spring、SpringMVC、MyBatis)框架开发的健康项目管理源码。项目整合了Dubbo分布式服务框架,以及SpringSecurity进行权限认证,确保系统的安全性和高效性。技术栈多元,主要使用...

    使用Struts+Spring+Hibernate整合开发例子

    3. 添加spring框架包。注意spring的配置文件要放到WEB-INF下。 4. 添加hibernate框架包。 注意:在添加hibernate框架时,选择生成spring configuration file (applicationContext.xml)。 这里是使用spring配置...

    spring2.5.6源码

    源代码分析,是一件既痛苦又快乐的事情,看别人写的代码是通过的,但当你能够看明白的时候,相信快乐也会随之而来,为了减少痛苦,更快的带来快乐,在这里希望通过这篇文章对觉得困难的朋友有一个帮助。 本文以...

    Spring开发参考手册.chm

    Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。 [2] Spring是一个开源容器框架...

    Spring中文帮助文档

    6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点运算 7.2.3. AspectJ切入点表达式 7.2.4. 便利的切入...

    Maven、Struts2、Spring3、Hibernate4、jetcd、spring动态加载properties

    项目框架是借鉴了使用Maven搭建Struts2+Spring3+Hibernate4的整合开发环境 这篇博文中的项目结构;...③spring数据源配置文件远程获取并加载启动; 代码在你本地环境并不一定能顺利运行需要稍作简单改动;

    Spring攻略(第二版 中文高清版).part1

    3.10 Spring中的AspectJ加载时织入aspect 140 3.10.1 问题 140 3.10.2 解决方案 141 3.10.3 工作原理 141 3.11 在Spring中配置AspectJ aspect 146 3.11.1 问题 146 3.11.2 解决方案 146 3.11.3 工作...

    Spring API

    6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7. Spring AOP APIs 7.1. 简介 7.2. Spring中的切入点API 7.2.1. 概念 7.2.2. 切入点运算 7.2.3. AspectJ切入点表达式 7.2.4. 便利的切入...

    Extjs4.0通用后台管理系统源码完整大型项目(ExtJS 4.2+Hibernate 4.1.7+Spring MVC 3.2.8)

    1、采用Spring MVC的静态加载缓存功能,在首页将Javascript文件、CSS文件和图片等静态资源文件加载进来放进内存,极大提高ExtJS的加载速度。 2、三种皮肤主题:经典、灰色和海王星,支持多浏览器和多分辨率。 3、...

    ssh(structs,spring,hibernate)框架中的上传下载

    Struts+Spring+Hibernate实现上传下载    本文将围绕SSH文件上传下载的主题,向您... 代码 8 业务接口实现类之save() 1. … 2. public class FileServiceImpl 3. implements FileService 4. { 5. private TfileDAO ...

    Spring攻略(第二版 中文高清版).part2

    3.10 Spring中的AspectJ加载时织入aspect 140 3.10.1 问题 140 3.10.2 解决方案 141 3.10.3 工作原理 141 3.11 在Spring中配置AspectJ aspect 146 3.11.1 问题 146 3.11.2 解决方案 146 3.11.3 工作...

    JAVA上百实例源码以及开源项目源代码

     Java绘制图片火焰效果,源代码相关注释:前景和背景Image对象、Applet和绘制火焰的效果的Image对象、Applet和绘制火焰的效果的Graphics对象、火焰效果的线程、Applet的高度,图片到图片装载器、绘制火焰效果的X坐标...

Global site tag (gtag.js) - Google Analytics