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

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源码 中英文注释"为开发者提供了一个深入理解Spring框架内部工作原理的机会。通过阅读源码和注释,我们可以更清晰地了解Spring如何管理依赖注入、AOP(面向切面编程)、事务管理、上下文...

    Spring源代码解析

    在源代码中,`BeanFactory`和`ApplicationContext`是IoC容器的主要接口,它们负责加载配置,创建和管理bean。 Spring的AOP模块则提供了声明式事务管理、日志记录、性能监控等功能。在源代码中,`Advisor`、`...

    Spring源码深度解析第二版

    XmlBeanFactory是Spring框架的容器实现之一,主要用于加载和管理Bean。XmlBeanFactory的基础实现主要包括了加载Bean、实例化Bean和依赖注入等。 2.5.1 自己直文件封装 XmlBeanFactory的自己直文件封装主要包括了...

    spring 1.2源代码

    通过阅读这些源代码,开发者可以学习到Spring如何实现依赖注入(Dependency Injection,DI),这是Spring的核心特性之一。DI使得对象之间的耦合度降低,提高了代码的可测试性和可维护性。此外,你还能看到AOP(面向...

    精通spring 源代码

    10. **源码分析**:深入阅读Spring源码,可以帮助我们理解其内部设计思想,如事件驱动、设计模式的应用(如单例、工厂、装饰者等)、类加载机制以及线程安全等Java编程基础。 以上只是Spring框架中部分关键知识点的...

    spring源码(注释+测试版)

    这份"spring源码(注释+测试版)"提供了Spring框架的源代码,带有注释和测试用例,对于开发者深入理解Spring的工作原理非常有帮助。 1. **spring-core**:这是Spring框架的基础模块,包含了核心的工具类和资源处理...

    构建为eclipse项目的spring源码

    Spring框架是Java开发中最常用的轻量级开源框架之一,它为开发者提供了丰富的功能,包括依赖注入、面向切面编程、事务管理、数据访问等。在Eclipse中构建Spring源码项目,可以帮助我们深入理解Spring的工作原理,...

    spring源码分析(1-10)

    Spring 源代码分析系列涵盖了多个关键模块,包括事务处理、IoC容器、JDBC、MVC、AOP以及与...通过这些深入的源代码分析,我们可以理解Spring框架的内部工作机制,更好地利用它来设计和构建健壮、可维护的Java应用。

    spring源码包.zip

    《Spring框架核心模块深度解析》 Spring框架是Java开发领域中的一个重要组成部分,它以其强大的功能和灵活的设计赢得了广泛的赞誉。本篇文章将深入探讨Spring的核心模块,包括`spring-context`、`spring-webmvc`、`...

    Spring框架核心源代码的分析及其感受-6

    《Spring框架核心源代码分析及感悟》 Spring框架作为Java企业级应用开发的基石,其强大的功能和灵活性深受开发者喜爱。深入理解Spring的核心源代码,不仅有助于我们更好地使用这个框架,还能提升我们的编程技艺和对...

    Spring源码分析.pdf

    Spring 框架是 Java 语言中最流行的开源框架之一,它提供了一个强大且灵活的基础设施来构建企业级应用程序。在 Spring 框架中,IOC 容器扮演着核心角色,本文将深入分析 Spring 源码,了解 IOC 容器的实现机制和...

    Spring源代码解析(一):IOC容器.doc

    Spring框架的IOC(Inversion of Control,控制反转)容器是其核心组件,它负责管理和装配应用中的对象。在Spring源代码解析的第一部分,我们将聚焦于IOC容器,特别是BeanFactory接口,它是所有Spring容器的基础。 ...

    Spring boot 热加载 springloaded-1.2.4.RELEASE JAR包

    SpringLoaded是Spring框架的动态代理加载器,它实现了Java的类加载机制,可以在应用运行时自动检测源代码的变化,并实时重载修改后的类,从而避免了每次修改代码都需要重新构建和启动应用的繁琐步骤。SpringLoaded ...

    Spring Boot源码(spring-boot-2.6.2.zip)

    通过阅读这些核心组件的源码,我们可以深入理解Spring Boot是如何加载配置、启动应用、自动配置bean以及与其他Spring框架组件协作的。这将有助于提升我们的开发技能,解决潜在的问题,并且更好地优化Spring Boot应用...

    mini-spring是简化版的spring框架,能帮助你快速熟悉spring源码和掌握spring的核心原理.zip

    它旨在帮助开发者快速理解Spring框架的核心概念和工作原理,为深入研究Spring源码提供了良好的起点。在企业级应用开发中,Spring以其强大的功能、高度的灵活性和广泛的社区支持,成为了Java开发者的首选框架之一。 ...

    Spring框架源码

    通过阅读和运行这个示例,你可以更好地了解Spring框架如何在实际应用中工作。 总之,Spring框架是一个功能强大且灵活的Java应用开发框架,它的源码可以帮助我们深入学习和理解软件设计原则、DI和AOP等核心概念,...

    Spring框架基础压缩包

    在"Spring_Project_1"这个压缩包中,可能包含了Spring框架的基础示例项目,包括配置文件、源代码、测试用例等,可以帮助初学者理解并实践Spring的基本用法和特性。通过学习和研究这些内容,你可以深入掌握Spring框架...

    Spring框架源码的下载

    Spring框架是Java开发中最常用的轻量级开源框架之一,它为构建企业级应用程序提供了一种模块化、可扩展的方法。Spring框架的核心特性包括依赖注入、面向切面编程、AOP代理、事务管理以及一系列用于简化Web开发的组件...

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

    本资料包"spring加载restful(文档+程序源码)"将帮助我们深入理解Spring如何加载和处理RESTful服务,并通过实际的源代码进行学习和实践。 一、Spring与RESTful服务概述 REST(Representational State Transfer,...

    spring源码文件压缩

    这个压缩包"spring源码文件压缩"很可能包含了Spring框架的核心组件和模块的源代码。 在Spring源码中,主要涉及以下几个关键知识点: 1. **依赖注入(Dependency Injection, DI)**:这是Spring的核心特性,它通过...

Global site tag (gtag.js) - Google Analytics