为什么我们需要Spring Framework ?

一、为什么我们需要Spring Framework ?

1. 从J2EE说起

  1. 1998 年Sun 公司发表JDK 1.2 时,分别发表了标准版-J2SE、企业版-J2EE、微型版-J2ME。J2EE 诞生。
  2. 2005 年Java One 大会上Sun 公布了JDK 1.6,J2XX 全部改名为JavaXX,J2EE 改名为JavaEE。
  3. 2018 年3月,Eclipse 基金会将JavaEE 更名为Jakarta EE。(2017 年Oracle 将JavaEE 移交给开源组织Eclipse 基金会,但不允许其继续使用Java一词)

Java EE 是一个技术体系的统称,它包含了:

  • EJB - Enterprise Java Beans.
  • JNDI - Java Naming and Directory Interface.
  • JDBC - Java Database Connectivity.
  • JMS - Java Message Service.
  • Servlet - Java Servlet API.
  • JSP - Java Server Pages.

2. EJB - Enterprise Java Bean

EJB 是J2EE 规范的核心内容,也与我们要说的Spring 的诞生密切相关。EJB 2.0 在2001 年8月发布,EJB 的工作方式如下:

为什么我们需要Spring Framework ?_第1张图片

EJB 提供了一种组件模式,使得开发人员可以仅关注系统业务方面的开发,而忽略中间件需求,比如:组件、远程调用、事务管理、持久化等等。在需要的时候开发人员可以随意将需求的中间件服务添加到系统中。至少从表面上看,这一切非常完美和有前途。但是你知道的,事实上并非如此。

EJB 存在的问题?

  1. 业务类需要与EJB 框架紧耦合,必须编写多个接口才能创建业务组件

    EJB 2.X 要求组件接口interface和业务逻辑实现类class必须从EJB 框架包中扩展接口,这使得开发人员编写的代码和EJB 框架的接口类之间产生了紧耦合。顺带的,我们还必须实现几个不必要的回调方法,例如ejbCreate(), ejbPassivate(), ejbActivate(), ...

    为了开发一个EJB 组件,开发人员至少需要写三个不同的类,分别针对主程序、远程接口和业务对象:

    /**
     * 远程接口,使客户端可以远程调用EJB 组件的业务功能
     */
    public interface PetService extends EJBObject {
        void saveOwner(Owner owner) throws RemoteException;
    }
    /**
     * 主接口,使客户端可以获取EJB 组件的句柄
     */
    public interface PetServiceHome extends EJBHome {
        PetService create() throws RemoteException, CreateException;
    }
    /**
     * 无状态会话Bean
     */
    public class PetServiceBean implements SessionBean {
        private SessionContext sessionContext;
    
        // 以下为EJB 要求实现的方法
    
        public void ejbCreate(){}
    
        public void ejbRemote(){}
    
        public void ejbActivate(){}
    
        public void ejbPassivate(){}
    
        public void setSessionContext(SessionContext sessionContext){
            this.sessionContext = sessionContext;
        }
    
        /**
         * 业务方法
         */
        public void saveOwner() throws java.rmi.RemoteException {
            // 业务代码
        }
    }
    
  2. RMI 带来不必要的性能开销。一个J2EE 服务器中同时有Servlet 容器和EJB 容器,不可接受的是Servlet 容器必须通过RMI 来调用EJB。为了避免RMI 这个问题,EJB 最终又引入了本地接口(还记得1里说的“至少”吗?)。

  3. 部署时需要些冗长的XML部署描述符,这很不直观且易出错。

  4. 难以在容器之外进行单元测试:JNDI 的依赖查找,使得对组件进行单元测试很困难,因为对JDNI 上下文的依赖。

  5. 面向过程:EJB 编程模型将开发人员引向了面向过程程序设计风格,数据和行为被分离而不是以内聚的形式在一起。在这里不辩论编程风格,只不过我们使用的Java 是面向对象的编程语言,因此我们肯定想充分利用Java 的优点,不是吗?

3. POJO 编程模型

正是由于上述的种种问题,EJB 2.X 逐渐被人们厌恶。而与此同时,与EJB 编程模型完全不同的POJO 编程模型发展起来了。

POJO - Plain Old Java Object 一词被发明出来,是为了描述那些:不需要实现任何特定接口或者扩展自某一特定框架类的最简单的类。它也使得我们可以专注于从面向对象的角度来编写代码

我们的主角来了:

基于POJO 编程模型产生了很多框架,在这其中Spring Framework 是最成功的一个框架,它已经成为事实上的开发JavaEE 应用程序的标准框架。

需要知道的一点是:在EJB 3.X 中,上述问题大部分都已经得到了解决,不过在此之前Spring Framework 已经发展壮大了。另外,在EJB 所有改进中最重要的一点就是EJB 规范引入了POJO 编程模型。显而易见,这在很大程度上要归功于Spring Framework 产生的影响。另外,当前的Spring Framework 已经兼容了很多EJB 规范。例如:@Resource, @PostConstruct, @PreDestroy

4. 总结:我们为什么需要Spring Framework ?

在EJB 2.X 这个历史时间阶段上,由于其本身的设计问题,导致使用EJB 模型来开发会提高复杂度,这其中最重要的问题是:业务类需要与EJB 框架紧耦合

而在这个时间节点上诞生的Spring Framework ,给我们提供了更简单易用的一个选择。业务代码纯粹,我们业务代码不必依赖框架中服务的API,且这些服务是可插拔的。(业务代码纯粹、服务可插拔)


二、Spring Framework 为我们带来了什么?

2002 年Rod Johnson 在 expert one-on-one J2EE Design and Development 一书中发表了Spring Framework 的第一个版本。

为什么我们需要Spring Framework ?_第2张图片

后来他又陆续发布了 expert one-on-one J2EE Development without EJB

为什么我们需要Spring Framework ?_第3张图片

以及Professional Java Development with the Spring Framework

为什么我们需要Spring Framework ?_第4张图片


Spring Framework 为我们带来了一个轻量级容器,使我们可以在没有被“侵入”的情况下写业务代码。

1. 轻量级容器

  1. Java EE 的容器

    在Java EE 中有一个“容器”的概念,它是指这样一个环境:在该环境中所有组件都被创建和装配,并且提供了所需要要的中间件服务。

    Java EE 提供了多个容器:

    • Servlet 容器负责创建和管理Web 层组件

      比如Servlet、JSP、Filter。

    • EJB 容器专注于业务层

      管理EJB 组件。

  2. 轻量级容器

    任何容器都应该向它所管理的组件提供一些基本服务。根据expert one-on-one J2EE Development without EJB 一书,可以列出如下所述的一些预期服务:

    • 生命周期管理
    • 依赖解析
    • 组件查找
    • 应用程序配置

    如果能够再进一步提供一些中间件服务,那就更好了:

    • 事务管理
    • 线程管理
    • 对象和资源池
    • 对组件的远程访问
    • 容器的扩展和定制

    一个轻量级容器(lightweight container)包含上述所有功能,但并不需要依赖那些API 来编写代码。也就是说轻量级容器没有侵入特性,在企业级Java 世界中,Spring Framework 就是最著名的轻量级容器

2. 控制反转

容器及其管理的组件所提供的最重要的好处是:可插拔的体系结构。组件需要实现一些接口,并且可以通过类似的接口访问其他组件所提供的服务。组件不需要知道这些服务的具体实现类,因此实现是很容易替换的。容器的工作是:创建这些组件以及所依赖的服务,并将这些组件装配在一起

控制反转:在组件类中,组件不需要实例化其所依赖的其它组件,而是在运行时由容器将被依赖的组件注入到这个组件中。这种模式被称为控制反转(Inversion of Controll),简称IoC,即依赖的控制权由组件自己反转到了容器

IoC 是任何容器都要提供的基本功能,它主要有两种实现方式:依赖查找(dependency lookup)和依赖注入(dependency injection):

  • 依赖查找

    容器向其管理的组件提供了回调方法,组件通过回调方法来显式地获取它所需要的依赖。通常使用一个“查找上下文”来访问依赖组件。

  • 依赖注入

    组件必须提供合适的构造函数或者Setter 方法,以便容器可以为组件注入其依赖的其他组件。

在最初的J2EE 中,所使用的主要方法是依赖查找,上面提到的“查找上下文”在这里就是JNDI。随着Spring Framework 等很多轻量级容器的出现,依赖注入变得流行起来。如今,当开发人员再提及IoC 时,通常会被理解为依赖注入。

3. 依赖注入

依赖注入的基本原则是:应用程序对象不应该负责查找它们所依赖的资源,而是由IoC 容器处理对象的创建和依赖注入。

一个好的容器应该同时支持构造函数注入和Setter 注入。

  1. Setter 注入

    当一个对象被实例化后其Setter 方法就会被马上调用。

    • 优点

      在组件被配置后,可以在运行时重新配置,这是因为Setter 方法本身是JavaBean 规范的内容,所以外部世界也可以在之后修改其依赖值。

    • 缺点

      有可能并不是所有的依赖项都可以在使用前被注入,从而使组件处于一种部分配置状态(Setter 循环依赖)。由于Setter 的顺序无法在组件中得到约定,可能会产生状态不一致的情况。

  2. 构造函数注入

    • 优点

      每一个被管理的组件都处于一致状态(循环依赖会直接报错),且在创建后可以马上使用。

    • 缺点

      无法在组件创建完毕后再对组件进行重新配置。

实际使用中,一般我们会混合使用这两种注入方式。


以上,我们知道了“为何使用Spring Framework?”,以及Spring Framework 为我们带来了什么。

你可能感兴趣的:(JavaEE,Spring,Java)