Spring 前世今生与编程思想

2020-03-29 2474点热度 2人点赞 0条评论
Spring 不仅是一个框架,Spring 已然成为一种生态。

Spring 诞生之前

在 Spring 诞生之前,Java 服务端的主要的技术栈还是 Servlet,JSP,EJB,Struts 。

在传统应用程序开发中,一个完整的应用是由一组相互协作的对象组成。所以开发一个应用除了要开发业务逻辑之外,更多的还需要关注如何使这些对象协作来完成所需功能,而且还要尽量满足低耦合、高聚合的设计。

可能有人说了,使用 “抽象工厂” 、 “工厂方法模式” 不也可以帮我们创建对象,使用 “生成器模式” 来处理对象间的依赖关系,不也能完成这些功能吗?可是这又需要我们创建另一些工厂类、生成器类,进而我们又需要额外管理这些类,增加了我们的负担。

如果能有种通过配置方式来创建对象、管理对象之间依赖关系的框架,使我们不需要通过工厂和生成器来创建及管理对象之间的依赖关系,这样就可以减少了我们的工作,加快简化了开发流程,就能使开发人员可以更加专注的解决业务问题。

2007 年,一个基于 Java 语言的开源框架正式发布,并且取了一个非常有活力的名字叫做 Spring 。它是一个开源的轻量级 Java SE/Java EE 开发应用框架,其目的是用于简化企业级应用程序开发。

一切从 Bean 开始

Bean 是 Spring 一切的起源。

早起的 Bean 的概念,起源于 1996 的 Java Applet Web 应用,其出现是为了数据传输的载体。但开发者们很快就发现这个新兴的语言还能做更多的事情。与之前的所有语言不同,Java 让模块化构建复杂的系统成为可能。

当时的软件行业虽然在业务上突飞猛进,但当时开发用的是传统的面向过程开发思想,软件的开发效率也一直踟蹰不前。伴随着业务复杂性的不断加深,开发也变得越发困难。

复杂的应用通常需要事务、安全、分布式等服务的支持,但当时的 JavaBean 并未直接提供。所以到了 1998 年 3 月, Sun 公司发布了 EJB 1.0 规范,把 Java 组件的设计理念延伸到了服务器端,并提供了许多必须的企业级服务。但它也不再像早期的 JavaBean 那么简单了。实际上,除了名字叫 EJB Bean 以外,它和我们现在看到的 JavaBean 关系不大了。

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

后来,随着 AOP 和 DI 等新的编程技术不断出现,为 JavaBean 提供了之前 EJB 才能拥有的强大功能,使得现在 Java 组件开发理念重新回归正轨。这些技术为 POJO 提供了类似 EJB 的声明式编程模型,同时避免引入任何 EJB 的复杂性。当简单的 JavaBean 足以胜任时,开发人员终于不用再编写笨重的 EJB 组件了。

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

Spring 的设计初衷

Spring 是为解决企业级应用开发的复杂性而设计,立志于全方面的简化 Java 开发。为此它主要采取了 4 个关键策略:

  1. 基于 POJO 的轻量级和最小侵入性编程;
  2. 通过依赖注入和面向接口松耦合;
  3. 基于切面和惯性进行声明式编程;
  4. 通过切面和模板减少样板式代码。

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

对以上策略稍作解释。

Spring 所提供的功能对比 JDK 中提供的实现,JDK 提供的实现是有条件的,例如:

  • JDK 中的动态代理,要求目标对象必须实现一个接口,从而使生成新的代理类来实现这个接口;
  • JDK 中实现原型模式,要求实现 Cloneable 接口;
  • JKD 中实现一个定时器,要求实现 Timer 。

以上,在 Spring 中一切都不需要,只需要你是一个标准的 POJO,实现尽可能的少侵入。

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

Spring 已经通过 IOC 容器帮我们管理了对象的生命周期。那么我们可以绕过 Spring 来手动创建对象吗?

例如当我们使用一个对象,发现是空的,这时我们想要手动创建这个对象:

// get null object
OrderDao orderDao;
assert orderDao == null;

// create object by ourself
OrderDao orderDao = new OrderDao();

这个做法是不可取的,这样假如这个对象中还有复合的的属性对象也为空:

class OrderDao {
    private Order order;
}

OrderDao orderDao = new OrderDao();
Order order = orderDao.getOeder();

// order is null
assert order == null;

// manual create again?
Order order = new Order();
orderDao.setOrder(order);

这样做会导致 Spring 事务不生效了,注入没有了,AOP 增强功能也没有了。正确的做法是 always 通过 Spring 来获取 Bean 对象:

OrderDao orderDao = application.getBean("order");

Spring 三大法宝

上述策略的实现,主要是通过三种方式来达成的:

  • 面向 Bean 编程(BOP):Service 是 Bean,Controller 是 Bean,配置文件也被加载为 ConfigurationDefinitionBean;
  • 依赖注入(DI)
  • 面向切面编程(AOP)

BOP 编程伊始

Spring 是面向 Bean 的编程 (Bean Oriented Programming, BOP),Bean 在 Spring 中才是真正的主角。 Bean 在 Spring 中作用就像 Object 对 0OP 的意义一样。 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 容器的基础,将处理事情的责任从应用程序代码转移到框架。

AOP 编程理念

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

AOP 和 IOC 是补充性的技术,它们都运用模块化方式解决企业应用程序开发中的复杂问题。在典型的面向对象开发方式中,可能要将日志记录语句放在所有方法和 Java 类中才能实现日志功能。在 AOP 方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要日志的组件上。

这样的优势就是 Java 类不需要知道日志服务的存在,也不需要关心相关的代码。所以,使用 Spring AOP 编写的应用程序代码是松散耦合的。

AOP 的功能完全集成到了 Spring 事务管理、日志和其他各种特性的上下文中。常见的应用场景有:

  • Authentication(权限认证)
  • Auto Caching(自动缓存处理)
  • Error Handling(统一错误处理)
  • Debugging(调试信息输出)
  • Logging(日志记录)
  • Transactions(事务处理)

Spring 中的编程思想总结

Spring 编程思想

Spring 思想 应用场景(特点) 归纳解释
OOP Object Oriented Programming(面向对象编程),用对象来抽象现实中的事物模型 封装、继承、多态
BOP Bean Oriented Programming(面向 Bean 编程),面向 Bean/POJO 类设计程序 一切从 Bean 开始
AOP Aspect Oriented Programming(面向切面编程),找出多个类中有一定规律的代码,开发时拆开,运行时再合并 解耦,专人做专事
IOC Inversion of Control(控制反转),将 new 对象的动作交给 Spring 管理,并由 IOC 容器保存已创建的对象 转交控制权
DI/DL Dependency Injection(依赖注入)或者 Dependency Lookup(依赖查找),Spring 不仅保存自己创建的对象,而且保存对象与对象之间的关系 注入即赋值,主要三种注入方式:构造方法、 set 方法、直接赋值

Spring 注解编程

Spring 的注解编程支持经历了以下过程:

  • V1.x:注解驱动启蒙时代
  • V2.0:注解驱动过渡时代
  • V2.5:引入了新的骨架式 Annotation
  • V3.x:注解驱动的黄金时代
  • V4.x:注解驱动的完善时代
  • V5.x:注解驱动的成熟时代(SpringBoot 2.0 实现完全注解化)

Spring 5 系统架构

spring_arch_overview

Spring 总共大约有 20 个模抉,由 1300 多个不同的文件构成。这些组件被分別整合在核心容器 (Core Container) 、 AOP(Aspect Oriented Programming) 、组装器 (Instrument) 、数据访问集成 (Data Access/Integeration) 、 Web 消息 (Messaging) 以及测试 (Test) 6 个模块中。组成 Spring 框架的每个模块集合或者模块都可以单独存在,也可以一个或多个模块联合实现。

模块依赖关系图如下:

spring_module_dependencies

SilverLining

也可能是只程序猿

文章评论