一、初识Spring

为什么要使用Spring

可以看官网的这段话:

Why Spring?
Spring makes programming Java quicker, easier, and safer for everybody. Spring’s focus on speed,simplicity, and productivity has made it the world’s most popular Java framework.

对于 Java 编程来说,使用 Spring 能完成的更加快速,更容易并更安全。Spring 专注于速度,便捷与开
发效率,也正是如此,让Spring成为了全世界最流行的 Java 框架。

Spring官网链接


什么是Spring

  Spring 是一个轻量级Java开发框架,最根本的使命是解决企业级应用开发的复杂性,即简化Java开发目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。 它是一个分层的 JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring 负责基础架构,因此 Java 开发者可以专注于应用程序的开发。
  Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)。


Spring框架的核心

IoC容器AOP模块。通过IoC容器管理POJO对象以及他们之间的耦合关系;通过AOP以动态非侵入的方式增强服务。IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。

  POJO = “Plain Old Java Object”,是MartinFowler等发明的一个术语,用来表示普通的Java对象,不是JavaBean, EntityBean 或者 SessionBean。POJO不担当任何特殊的角色,也不实现任何特殊的Java框架的接口如,EJB,JDBC等等。
  即POJO是一个简单的普通的Java对象,它不包含业务逻辑或持久逻辑等,但不是JavaBean、EntityBean等,不具有任何特殊角色和不继承或不实现任何其它Java框架的类或接口。


由哪些模块组成

   Spring Framework 属于其中最基础,最核心的部分,Spring下的其他大部分框架都依赖 Spring
Framework 。
其中大约有 20 个模块, 由 1300 多个不同的文件构成。 而这些组件被分别整合在核心容器(Core Container)AOP(Aspect Oriented Programming)和设备支持(Instrmentation)数据访问与集成(Data Access/Integeration)Web消息(Messaging)Test等 6 个模块中。 以下是 Spring 5.x 版本的模块结构图:

在这里插入图片描述

  • spring core:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。
  • spring beans:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为Bean。
  • spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框架式的对象访问方法。
  • spring jdbc:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析, 用于简化JDBC。
  • spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。
  • spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。
  • spring test:主要为测试提供支持的,支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。

官网对于各模块的详细介绍资料链接


二、Core Container(核心容器)

  本模块由spring-corespring-beansspring-contextspring-context-supportand spring-expression (Spring Expression Language) 4 个模块组成。
  spring-corespring-beans模块,这两个模块提供了整个 Spring 框架最基础的设施:IoC (Inversion of Control,控制反转) 和 DI (Dependency Injection,依赖注入)。 这部分功能相当于所有 Spring 框架运行的基础,以前我们操作对象都需要手动的 new 对象,由对象的作用域决定对象的生命周期。使用 Spring 后,由框架提供了统一的容器来实例化、管理这些对象,并自动组织对象与对象间的关系。这种容器称为 IoC 容器,有些地方也叫 Spring Bean 容器、Spring 容器。对象之间的复杂关系(体现在代码中就是对象中成员变量,引用了另一个对象),也交给了容器来进行设置。


IoC(控制反转)

什么是 IoC

  控制反转即 IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
  Spring IoC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。


IoC 的作用

  • 管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的。
  • 解耦,由容器去维护具体的对象
  • 托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的。

IoC 的优点

  • IoC 或 依赖注入把应用的代码量降到最低。
  • 它使应用容易测试,单元测试不再需要单例和 JNDI 查找机制。
  • 最小的代价和最小的侵入性使松散耦合得以实现。
  • IoC 容器支持加载服务时的饿汉式初始化和懒加载。

IoC 的缺点

  • 软件系统引入第三方IoC 容器,使得生成对象变得复杂,本来是两者之间的事情,凭空又增加了一条手续。在一定程度上,引入一个新的框架,增加了团队成员学习和认知的培训成本。
  • IoC 容器在生成对象上是通过反射的方式,在运行效率上一定的损耗。
  • 具体到IoC 框架项目来说,需要进行大量的配置,比较繁琐,对于小的项目来说,一定程度上加大了工作成本。
  • 需要关注于IoC 框架的成熟度,如果引入了一个不成熟的IoC 框架,对项目整体来说也会产生一定的隐患。

DI(依赖注入)

什么是DI

相对于IoC而言,依赖注入更加准确地描述了IoC的设计理念。所谓依赖注入(Dependency Injection)就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中,即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。


依赖注入的基本原则

  应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。容器全权负责组件的装配,它会把符合依赖关系的对象通过属性(JavaBean中的setter)或者是构造器传递给需要的对象。


依赖注入的优势

  依赖注入之所以更流行是因为它是一种更可取的方式:让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造器或者接口,使容器可以在初始化时组装对象的依赖关系。其与依赖查找方式相比,主要优势为:

  • 查找定位操作与应用代码完全无关。
  • 不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
  • 不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。

不同类型的依赖注入实现方式

依赖注入是时下最流行的IoC实现方式,依赖注入分为接口注入(Interface Injection)Setter方法注入(Setter Injection)构造器注入(Constructor Injection) 三种方式。其中接口注入由于在灵活性和易用性比较差,从Spring4开始已被废弃。

构造器注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。


构造器依赖注入和 Setter方法注入的区别

构造函数注入setter 注入
没有部分注入有部分注入
不会覆盖 setter 属性会覆盖 setter 属性
任意修改都会创建一个新实例任意修改不会创建一个新实例
适用于设置很多属性适用于设置少量属性

两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。


顶级接口

BeanFactoryApplicationContext是 Spring 的两大核心接口,都可以当做 Spring 的容器。其中ApplicationContext 是 BeanFactory 的子接口。

BeanFactory 和 ApplicationContext 的区别

1、依赖关系

BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。

ApplicationContext:作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:

  • 继承MessageSource,因此支持国际化。
  • 统一的资源文件访问方式。
  • 提供在监听器中注册bean的事件。
  • 同时加载多个配置文件。
  • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

2、加载方式

BeanFactroy,采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。

ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

3、创建方式

BeanFactory通常以编程的方式被创建;
ApplicationContext还能以声明的方式创建,如使用ContextLoader。

4、注册方式

BeanFactory 和 ApplicationContext 都支持 BeanPostProcessor、BeanFactoryPostProcessor 的使用,但两者之间的区别是:BeanFactory 需要手动注册,而 ApplicationContext 则是自动注册。


BeanFactory和ApplicationContext的关系详解

BeanFactory简单粗暴,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”

ApplicationContext可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。

除了这两个大接口,还有其他的辅助接口,这里就不介绍他们了。


ApplicationContext通常的实现

FileSystemXmlApplicationContext :此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。

ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。

WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。


Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐