Spring 源码分析 (一)——迈向 Spring 之路

一切都是从 Bean 开始的

在 1996 年,Java 还只是一个新兴的、初出茅庐的编程语言。人们之所以关注她仅仅是因为,可以使用 Java 的 Applet 来开发 Web 应用。但这些开发者很快就发现这个新兴的语言还能做更多的事情。与之前的所有语言不同,Java 让模块化构建复杂的系统成为可能(当时的软件行业虽然在业务上突飞猛进,但当时开发用的是传统的面向过程开发思想,软件的开发效率一直踟蹰不前。伴随着业务复杂性的不断加深,开发也变得越发困难。其实,当时也是面向对象思想飞速发展的时期,她在 80 年代末被提出,成熟于 90 年代,现今大多数编程语言都是面向对象的,当然这是后话了)。他们为 Applet 而来,为组件化而留。这便是最早的 Java。

同样在这一年的 12 月,Sun 公司发布了当时还名不见经传但后来人尽皆知的 JavaBean 1.00-A 规范。早期的 JavaBean 规范针对于 Java,她定义了软件组件模型。这个规范规定了一整套编码策略,使简单的 Java 对象不仅可以被重用,而且还可以轻松地构建更为复杂的应用。尽管 JavaBean 最初是为重用应用组件而设计的,但当时他们却是主要用作构建窗体控件,毕竟在 PC 时代那才是主流。但相比于当时正如日中天的 Delphi、VB 和 C++,他看起来还是太简易了,以至于无法胜任任何 "实际的" 工作。

复杂的应用通常需要诸如事物、安全、分布式等服务的支持,但 JavaBean 并未直接提供。所以到了 1998 年 3 月,Sun 发布了 EJB 1.0 规范,该规范把 Java 组件的设计理念延伸到了服务器端,并提供了许多必须的企业级服务,但他也不再像早期的 JavaBean 那么简单了。实际上,除了名字,EJB Bean 已经和 JavaBean 没有任何关系了。

尽管现实中有很多系统是基于 EJB 构建的,但 EJB 从来没有实现它最初的设想:简化开发。EJB 的声明式编程模型的确简化了很多基础架构层面的开发,例如事务和安全;但另一方面 EJB 在部署描述符和配套代码实现等方面变得异常复杂。随着时间的推移,很多开发者对 EJB 已经不再抱有幻想,开始寻求更简洁的方法。

现在 Java 组件开发理念重新回归正轨。新的编程技术 AOP 和 DI 的不断出现,他们为 JavaBean 提供了之前 EJB 才能拥有的强大功能。这些技术为 POJO 提供了类似 EJB 的声明式编程模型,而没有引入任何 EJB 的复杂性。当简单的 JavaBean 足以胜任时,人们便不愿编写笨重的 EJB 组件了。

客观地讲,EJB 的发展甚至促进了基于 POJO 的编程模型。引入新的理念,最新的 EJB 规范相比之前的规范有了前所未有的简化,但对很多开发者而言,这一切的一切都来得太迟了。到了 EJB 3 规范发布时,其他基于 POJO 的开发架构已经成为事实的标准了,而 Spring 框架也是在这样的大环境下出现的。

代码简洁之路

Rod Johnson 的思考

Rod Johnson 这位澳大利亚的 Java 大师,很难想象,他除了在悉尼大学获得计算机学士学位以外还获得了音乐的博士学位,或许正是他音乐的细胞成就了 Spring 独特简约和优雅。他在 1996 年就踏足 Java 开发,并且在 C++ 方面也已经有了很深厚的造诣。他常年从事保险、银行等企业的开发,是一个一切从现实出发的设计者以及勇敢的创新者。

Rod Johnson 在 2002 年编著的《Expert One-to-One J2EE Design and Development》一书中,对 EJB 框架的臃肿、低效、脱离现实等种种现状提出了质疑,并积极寻求革新之道。以此书为指导思想,他编写了 interface21 框架,这是一个力图冲破 Java EE 传统开发困境,从实际需求出发,着眼于轻便、灵巧,易于开发、测试和部署的轻量级开发框架。Spring 框架即以 interface21 框架为基础,经过重新设计,并不断丰富其内涵,于 2004 年 3 月 24 日,发布了 Spring 1.0 正式版。同年他又推出了另一部堪称经典的力作《Expert One-to-One J2EE Development without EJB》,该书在 Java 世界掀起了轩然大波,不断地改变着 Java 开发者程序设计和开发的思考方式。在该书中,作者根据自己多年丰富的实践经验,对 EJB 的各项笨重臃肿的结构进行了逐一的分析和否定,并分别以简洁实用的方法代替。可以说,这本书中的代码诞生了 Spring Framework。借此,Rod Johnson 也奠定了他在 Java 世界中大师级人物的地位。

设计伊始

Spring 是为解决企业级应用开发的复杂性而设计,她可以做很多事。但归根到底支撑 Spring 的仅仅是少许的基本理念,而所有地这些的基本理念都能可以追溯到一个最根本的使命:简化开发。这是一个郑重的承诺,其实许多框架都声称在某些方面做了简化。

而 Spring 则立志于全方面的简化 Java 开发。对此,她主要采取了 4 个关键策略:

1,基于 POJO 的轻量级和最小侵入性编程;

2,通过依赖注入和面向接口松耦合;

3,基于切面和惯性进行声明式编程;

4,通过切面和模板减少样板式代码;

而他主要是通过:面向 Bean、依赖注入以及面向切面这三种方式来达成的。

面向 Bean

Spring 是面向 Bean 的编程(Bean Oriented Programming, BOP),Bean 在 Spring 中才是真正的主角。Bean 在 Spring 中作用就像 Object 对 OOP 的意义一样,Spring 中没有 Bean 也就没有 Spring 存在的意义。Spring 提供了 IoC 容器通过配置文件或者注解的方式来管理对象之间的依赖关系。

控制反转 ( 其中最常见的方式叫做依赖注入(Dependency Injection,DI),还有一种方式叫 “依赖查找”(Dependency Lookup,DL),她在 C++、Java、PHP 以及.NET 中都运用。在最早的 Spring 中是包含有依赖注入方法和依赖查询的,但因为依赖查询使用频率过低,不久就被 Spring 移除了,所以在 Spring 中控制反转也被称作依赖注入),她的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器 (在 Spring 框架中是 IoC 容器) 负责将这些联系在一起。

在典型的 IoC 场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法。

依赖注入

Spring 设计的核心 org.springframework.beans 包(架构核心是 org.springframework.core 包),它的设计目标是与 JavaBean 组件一起使用。这个包通常不是由用户直接使用,而是由服务器将其用作其他多数功能的底层中介。下一个最高级抽象是 BeanFactory 接口,它是工厂设计模式的实现,允许通过名称创建和检索对象。BeanFactory 也可以管理对象之间的关系。

BeanFactory 支持两个对象模型。

1,单例:模型提供了具有特定名称的对象的共享实例,可以在查询时对其进行检索。Singleton 是默认的也是最常用的对象模型。对于无状态服务对象很理想。

2,原型:模型确保每次检索都会创建单独的对象。在每个用户都需要自己的对象时,原型模型最适合。

bean 工厂的概念是 Spring 作为 IoC 容器的基础。IoC 则将处理事情的责任从应用程序代码转移到框架。

面向切面

面向切面编程,即 Aop,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。Aop 的核心构造是方面,它将那些影响多个类的行为封装到可重用的模块中。

Aop 和 IoC 是补充性的技术,它们都运用模块化方式解决企业应用程序开发中的复杂问题。在典型的面向对象开发方式中,可能要将日志记录语句放在所有方法和 Java 类中才能实现日志功能。在 Aop 方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要日志的组件上。当然,优势就是 Java 类不需要知道日志服务的存在,也不需要考虑相关的代码。所以,用 Spring Aop 编写的应用程序代码是松散耦合的。

Aop 的功能完全集成到了 Spring 事务管理、日志和其他各种特性的上下文中。

俯瞰 Spring 源码架构

系统架构

Spring 总共大约有 20 个模块,由 1300 多个不同的文件构成。而这些组件被分别整合在核心容器(Core Container)、Aop(Aspect Oriented Programming)和设备支持(Instrmentation)、数据访问及集成(Data Access/Integeration)、Web、报文发送(Messaging)、Test,6 个模块集合中。

以下是 Spring 4 的系统架构图。

Spring 源码分析 (一)——迈向 Spring 之路_第1张图片

组成 Spring 框架的每个模块集合或者模块都可以单独存在,也可以一个或多个模块联合实现。每个模块的组成和功能如下:

1. 核心容器:由 spring-beans、spring-core、spring-context 和 spring-expression(Spring Expression Language, SpEL) 4 个模块组成。

spring-beans 和 spring-core 模块是 Spring 框架的核心模块,包含了控制反转(Inversion of Control, IoC)和依赖注入(Dependency Injection, DI)。BeanFactory 接口是 Spring 框架中的核心接口,它是工厂模式的具体实现。BeanFactory 使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码进行了分离。但 BeanFactory 容器实例化后并不会自动实例化 Bean,只有当 Bean 被使用时 BeanFactory 容器才会对该 Bean 进行实例化与依赖关系的装配。

spring-contest 模块构架于核心模块之上,他扩展了 BeanFactory,为她添加了 Bean 生命周期控制、框架事件体系以及资源加载透明化等功能。此外该模块还提供了许多企业级支持,如邮件访问、远程访问、任务调度等,ApplicationContext 是该模块的核心接口,她是 BeanFactory 的超类,与 BeanFactory 不同,ApplicationContext 容器实例化后会自动对所有的单实例 Bean 进行实例化与依赖关系的装配,使之处于待用状态。

spring-expression 模块是统一表达式语言(unified EL)的扩展模块,可以查询、管理运行中的对象,同时也方便的可以调用对象方法、操作数组、集合等。它的语法类似于传统 EL,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数。这种语言的特性是基于 Spring 产品的需求而设计,他可以非常方便地同 Spring IoC 进行交互。

2.Aop 和设备支持:由 spring-aop、spring-aspects 和 spring-instrumentation 3 个模块组成。

spring-aop 是 Spring 的另一个核心模块,是 Aop 主要的实现模块。作为继 OOP 后,对程序员影响最大的编程思想之一,Aop 极大地开拓了人们对于编程的思路。在 Spring 中,他是以 JVM 的动态代理技术为基础,然后设计出了一系列的 Aop 横切实现,比如前置通知、返回通知、异常通知等,同时,Pointcut 接口来匹配切入点,可以使用现有的切入点来设计横切面,也可以扩展相关方法根据需求进行切入。

spring-aspects 模块集成自 AspectJ 框架,主要是为 Spring Aop 提供多种 Aop 实现方法。

spring-instrumentation 模块是基于 JAVA SE 中的 "java.lang.instrument" 进行设计的,应该算是 Aop 的一个支援模块,主要作用是在 JVM 启用时,生成一个代理类,程序员通过代理类在运行时修改类的字节,从而改变一个类的功能,实现 Aop 的功能。在分类里,我把他分在了 Aop 模块下,在 Spring 官方文档里对这个地方也有点含糊不清,这里是纯个人观点。

3. 数据访问及集成:由 spring-jdbc、spring-tx、spring-orm、spring-jms 和 spring-oxm 5 个模块组成。

spring-jdbc 模块是 Spring 提供的 JDBC 抽象框架的主要实现模块,用于简化 Spring JDBC。主要是提供 JDBC 模板方式、关系数据库对象化方式、SimpleJdbc 方式、事务管理来简化 JDBC 编程,主要实现类是 JdbcTemplate、SimpleJdbcTemplate 以及 NamedParameterJdbcTemplate。

spring-tx 模块是 Spring JDBC 事务控制实现模块。使用 Spring 框架,它对事务做了很好的封装,通过它的 Aop 配置,可以灵活的配置在任何一层;但是在很多的需求和应用,直接使用 JDBC 事务控制还是有其优势的。其实,事务是以业务逻辑为基础的;一个完整的业务应该对应业务层里的一个方法;如果业务操作失败,则整个事务回滚;所以,事务控制是绝对应该放在业务层的;但是,持久层的设计则应该遵循一个很重要的原则:保证操作的原子性,即持久层里的每个方法都应该是不可以分割的。所以,在使用 Spring JDBC 事务控制时,应该注意其特殊性。

spring-orm 模块是 ORM 框架支持模块,主要集成 Hibernate, Java Persistence API (JPA) 和 Java Data Objects (JDO) 用于资源管理、数据访问对象 (DAO) 的实现和事务策略。

spring-jms 模块(Java Messaging Service)能够发送和接受信息,自 Spring Framework 4.1 以后,他还提供了对 spring-messaging 模块的支撑。

spring-oxm 模块主要提供一个抽象层以支撑 OXM(OXM 是 Object-to-XML-Mapping 的缩写,它是一个 O/M-mapper,将 java 对象映射成 XML 数据,或者将 XML 数据映射成 java 对象),例如:JAXB, Castor, XMLBeans, JiBX 和 XStream 等。

4.Web:由 spring-web、spring-webmvc、spring-websocket 和 spring-webmvc-portlet 4 个模块组成。

spring-web 模块为 Spring 提供了最基础 Web 支持,主要建立于核心容器之上,通过 Servlet 或者 Listeners 来初始化 IoC 容器,也包含一些与 Web 相关的支持。

spring-webmvc 模块众所周知是一个的 Web-Servlet 模块,实现了 Spring MVC(model-view-controller)的 Web 应用。

spring-websocket 模块主要是与 Web 前端的全双工通讯的协议。(资料缺乏,这是个人理解

spring-webmvc-portlet 模块是知名的 Web-Portlets 模块(Portlets 在 Web 门户上管理和显示的可插拔的用户界面组件。Portlet 产生可以聚合到门户页面中的标记语言代码的片段,如 HTML,XML 等),主要是为 SpringMVC 提供 Portlets 组件支持。

5. 报文发送:即 spring-messaging 模块。

spring-messaging 是 Spring4 新加入的一个模块,主要职责是为 Spring 框架集成一些基础的报文传送应用。

6.Test:即 spring-test 模块。

spring-test 模块主要为测试提供支持的,毕竟在不需要发布(程序)到你的应用服务器或者连接到其他企业设施的情况下能够执行一些集成测试或者其他测试对于任何企业都是非常重要的。

依赖关系

该图是 SPRING 3.2.X 的包结构,可以从中清楚看出 Spring 各个模块之间的依赖关系。

Spring 源码分析 (一)——迈向 Spring 之路_第2张图片

从图中可以看出,IoC 的实现包 spring-beans 和 AOP 的实现包 spring-aop 是整个框架的基础,而 spring-core 则是整个框架的核心,基础的功能都在这三个包里。

在此基础之上,spring-context 提供上下文环境,为各个模块提供粘合作用。

在 spring-context 基础之上提供了 spring-tx 和 spring-orm 包,而 web 部分的功能,都是要依赖 spring-web 来实现的。

由于 struts 框架自身的 Bug 一直没有修复,以及 Spring MVC 已经足够强大,所以在最新的 spring 4 中已经移除了 spring-struts 模块。

如果你想加入 spring 源码的学习,笔者的建议是从 spring-core 入手,其次是 spring-beans 和 spring-aop,随后是 spring-context,再其次是 spring-tx 和 spring-orm,最后是 spring-web 和其他部分。

闲言小叙

框架小结

笔者将 Spring 的内部设计与操作系统 kernel(Linux 内核)的设计方法进行类比,希望借此能更形象的说明 Spring 体系。

Spring 体系的核心是 IoC 和 Aop 模块。对于 kernel 而言,进程调度器就是其关键部位,kernel 通过 “进程” 这个概念来抽象物理的计算资源,同时通过调度算法的设计来实现对计算资源的高效使用。而对于 Spring 来说,也是一样的,一方面通过 IoC 容器来进行 POJO 对象管理,以及对他们进行松耦合处理,同时也让信息资源可以用最简单的 Java 语言来抽象和描述;另一方面,可以通过 Aop 来增强服务的功能。

另外,在 Spring 体系中,Spring 简化了 Java EE 所进行的开发,这种简化是指我们能够在不 EJB 这么厚重的环境中使用 Java EE 的基本服务 —— 为应用开发服务提供了许多即开即用的系统组件合服务,这些服务涵盖了 Java EE 各个基本服务,对于其他的服务,也可以根据使用情况动态扩展到 Spring 体系中。基本来说,Spring 体系中已经涵盖了 Java EE 中经常用到的许多服务,比如事务处理、Web MVC、JDBC、ORM、远程调用,这些服务的价值是不可忽视的,就像 kernel 如果没有实现许多驱动,那 Linux 对用户而言也是没有任何价值的。Spring 通过自己的努力,提供了这些看起来不起眼,但对推广起着关键作用的部分,从而构建起了一个丰富的生态圈。其实,这也是 interface21 和 Spring 之间的区别。

和 Linux 一样,作为一个开源项目。其开源的特性也深深影响了 Spring 体系的设计,在发展的过程中,其自身也吸收了不少好的社区项目,比如 Spring 的 Security 框架就是来源于社区 Acegi, 这个框架的原意是为 Spring 设计一个安全框架,让 Spring 应用更方便地处理一些安全性的问题,但慢慢的被 Spring 吸收,成为 Spring 的一个子项目。

太史公曰

笔者坦言,文中的观点建立于大量地书籍、博客、英文资料以及笔者亲身经历之上,其实中间有不少是他人观点和创意,甚至有很多就是原文或者间接的翻译。但笔者认为借鉴他人观点和创意并不可耻,毕加索说过,拙工偷,巧匠盗,但如何将他人的创意有机的结合为一个整体,这就完全看个人的品味了。

本篇是带你走入 Spring 世界,主要是着眼于架构设计和源码角度编写,其实这也是笔者的 Spring 源码分析的写作计划,笔者将她分为了四个部分总共八篇,第一部分是本文主要介绍 Spring 源码框架,其次是第二部分源码分析第一阶段主要分两篇主要介绍 Spring 的核心容器和 Aop,再其次是源码分析第二阶段分为四篇主要介绍数据访问及集成、Web、报文发送以及 Test,最后是一篇总纲。慢工出细活,笔者预计会在一至三个月内编写完一篇,全部完成,可能会花费半年到二年时间。很长对吧,但对于独具匠心的 Spring 框架,笔者认为这一切都是值得的。当然笔者也希望,自己能从中得出启示,从而开发出新的东西,毕竟创造的乐趣远远大于使用的乐趣。

最后笔者声明,笔者的初衷是编写笔者对 Spring 源码分析的总结,平时的兴趣也仅仅在于源码分析、后台以及系统架构。

Spring 源码分析 (一)——迈向 Spring 之路_第3张图片

学习更多JAVA知识与技巧,关注与私信博主(学习)学习JAVA 课件,源码,安装包,还有最新大厂面试资料等等等

Spring 源码分析 (一)——迈向 Spring 之路_第4张图片

你可能感兴趣的:(spring,java,后端)