【Spring学习笔记 一】Sping基本概念及理论基础

简介: 【Spring学习笔记 一】Sping基本概念及理论基础

花费了8篇Blog终于将MyBatis学习完了,对这个半自动化的ORM框架也有了较为深入的理解。今天这篇Blog是Spring系列的第一篇Blog,主要了解下Spring的结构以及Spring的核心思想IOC以及核心思想的实现方式DI

Spring基本概念

什么是Spring呢,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架),我对Spring的理解是它已经不仅仅是一个框架了,它更像是一个生态,整个J2EE企业级开发的一个集大成者。Spring的核心概念就是IOC和AOP,搞懂了这两个概念,我们也就搞懂了Spring存在的意义,一个技术的产生必然有其便捷性或者具备历史技术不具备的能力,也就是我们需要回答为什么要用Spring这个问题。

IOC和DI的概念

IOC,Inversion of Control的缩写,也即控制反转,在Java开发中,IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。理解好IOC的关键是要明确:谁控制谁,控制什么,为何是反转,哪些方面反转了,我们还是使用在MyBatis中使用的Person和BankAccount的例子来说明这个问题:

IOC的基本概念

Person对象,包含一系列的银行账户信息,对象熟悉赋值时依赖银行账户信息的创建

package com.example.MyBatis.dao.model;
import lombok.Data;
import java.util.List;
/*
 * person表对应对象,包含一系列的银行账户信息
 * */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private int id;
    private String username;
    private String password;
    private int age;
    private int phone;
    private String email;
    private String interests;
    private List<BankAccount> bankAccounts;
}

BankAccount对象

package com.example.MyBatis.dao.model;
import lombok.Data;
/*
 * bank_account表对应对象
 * */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BankAccount {
    private int id;
    private String bankName;
    private String accountName;
    private int personId;
}

这种情况下我们想要获取一个Person就只能通过依次创建这些依赖对象,这种就是主动控制对象的创建过程。

//构造一个Person对象
    public Person getPerson(){
        Person person=new Person();
        ArrayList<BankAccount> bankAccounts=new ArrayList<>();
        BankAccount bankAccount1=new BankAccount();
        BankAccount bankAccount2=new BankAccount();
        bankAccounts.add(bankAccount1);
        bankAccounts.add(bankAccount2);
        person.setBankAccounts(bankAccounts);
        return person;
    }

Spring的IOC创建对象实现方式我们下一篇Blog再进行介绍,我们回到刚刚的问题:

  • 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IOC是有专门一个容器来创建这些对象,即由IOC容器来控制对象的创建以及外部资源获取(不只是对象,还有文件等)。
  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象:由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转,依赖对象的获取被反转了

说白了,IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。

为什么使用IOC思想

ok我们回过头来看为什么要使用IOC这种方式去获取对象呢?好处是什么,之前传统方式的弊端又是什么。应该还记得我之前画的MVC架构图:

1 传统方式重复对象创建资源浪费

业界普遍按MVC这种分层方式组织代码,其核心思想是职责分离。层次越低复用程度越高,比如一个 DAO 对象往往会被多个 Service 对象使用,一个 Service 对象往往也会被多个 Controller 对象使用,理想情况是这种倒三角。

而实际情况则是,我们只做到了逻辑复用,并没有做到资源复用。上层调用下一层时,必然会持有下一层的对象引用,即成员变量。目前我们每一个成员变量都会实例化一个对象

每一个链路都创建了同样的对象,造成了极大的资源浪费。本应多个 Controller 复用同一个 Service,多个 Service 复用同一个 DAO。现在变成了一个 Controller创建多个重复的 Service,多个 Service 又创建了多个重复的 DAO,从倒三角变成了正三角,造成了资源的浪费。虽然可以将类设置成单例模式,但是那也比较麻烦,需要程序员考虑的东西太多了。

对象交由容器管理后,默认是单例的,这就解决了多例模式造成的资源浪费问题

2 传统方式配置组件复杂

如果依赖对象或组件的创建交给对象本身去管理,那么会非常痛苦,例如Service在使用Dao的时候,实例化的是 UserDaoImpl,那么对这个依赖对象的初始化设置是比较麻烦的。组件的调用方参与了组件的创建和配置工作,而这些工作又比较庞杂,例如A对象依赖B对象,B对象依赖C对象,C对象依赖D对象,我们使用一次A对象,就要依次把BCD都创建好才可以,如果把这些都交给调用方做,那需要考虑的也太多了。

对象交由容器管理后,不需要调用方每次考虑依赖关系的复杂创建过程,容器直接把创建好的A返回给调用方

3 传统方式更换实现类复杂

假设有 10 个 UserService依赖了 UserDaoImpl,最开始实例化的是 UserDaoImpl,后面需要换一个实现类 OtherUserDaoImpl,我就得逐个修改那 10 个 UserService重新构造OtherUserDaoImpl,而如果每个UserService中的OtherUserDaoImpl配置比较复杂的情况下那么会非常麻烦,需要逐个去进行修改:

public class OtherUserDaoImpl implements UserDao{
    private MyDataSource dataSource;
    public OtherUserDaoImpl() {
        // 构造数据源
        dataSource = new MyDataSource("jdbc:mysql://localhost:3306/test", "root", "password");
        // 进行一些其他配置
        dataSource.setInitiaSize(10);
        dataSource.setMaxActive(100);
        // ...省略更多配置项
    }
}

该数据源组件要想真正生效需要对其进行许多配置,这个创建和配置过程是非常麻烦的。而且配置可能会随着业务需求的变化经常更改,这时候你就需要修改每一个依赖该组件的地方,牵一发而动全身。当然你可以引入一个util类来统一处理,但是那也比较麻烦,需要程序员考虑的东西太多了。

对象交由容器管理后,依赖对象实现方式的修改只需要在容器中进行替换即可全局生效

IOC思想的演进

IOC的演进过程是什么样的呢?没有IOC之前我们是怎么操作的呢,用一个制造斧头的形象的例子来说明:

  1. 最原始的方式,不仅需要知道接口,还需要知道接口具体的每个实现类(自己做一把斧子,需要了解所有关于斧子的东西
  2. 进步的工厂方式,只需要知道接口和存放所有实现类的工厂就行了(找工厂定制一把斧子,生命周期,特征完全是你的要求,重用性低)
  3. 最先进的按需分配、控制反转,只需要知道接口,工厂会自动赋予你需要的资源,控制反转,资源注入(告诉工厂要斧子,工厂会根据你的需求给你,但不是完全属于你,只是借用,使用完后控制权还在工厂)

所谓IOC,对于Spring框架来说,就是由Spring来负责控制对象的生命周期和对象间的关系。举个简单的例子,我们是如何找女朋友的?

  • 常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号等,想办法认识她们,投其所好送其所要,这个过程是复杂深奥的,我们必须自己设计和面对每个环节。
  • 传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类耦合起来

那么IOC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。

  • 婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个相亲对象,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。
  • Spring所倡导的开发方式就是如此,所有的类都会在Spring容器中登记,告诉Spring你是个什么东西,你需要什么东西,然后Spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 Spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。对于某个具体的对象而言,以前是它控制它依赖对象的,现在是所有对象都被Spring控制,所以这叫控制反转

所以也可以理解为IOC是一个中介机构,直接送上你需要的东西,并且集中管理和注册资源,降低对象间的耦合,复合软件设计原则,低耦合高内聚。

依赖注入DI的概念

IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过**DI(Dependency Injection,依赖注入)**来实现的。比如对象A需要操作数据库,

  • 以前我们总是要在A中自己编写代码来获得一个Connection对象
  • 有了 Spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,Spring会在适当的时候制造一个Connection,然后像打针一样,注入到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由Spring注入到A中的,依赖注入的名字就这么来的。

DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,Spring就是通过反射来实现依赖注入的,所以DI并不是一个思想模式,而是IOC思想的一个具体的实现方式

AOP的概念

什么是AOP,AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面

想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠复制粘贴吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决,这有点儿像我们之前学习Servlet时的过滤器。Spring的AOP解决方式我们后续在专门讨论下。

Sping 组成结构

了解完了Spring的核心思想以及为什么使用它之后,我们再来看看Spring的组成结构

Spring分层组成

数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下:

  • JDBC 模块,提供了 JDBC 抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
  • ORM 模块,提供了对流行的对象关系映射 API 的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring的其它功能整合,比如前面提及的事务管理。
  • OXM 模块,提供了对 OXM 实现的支持,比如 JAXB、Castor、XML Beans、JiBX、XStream 等。
  • JMS 模块,包含生产(produce)和消费(consume)消息的功能。从 Spring 4.1 开始,集成了 spring-messaging 模块。
  • 事务模块,为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写 beginTransaction()、commit()、rollback() 等事务管理方法,声明式事务是通过注解或配置由 spring 自动处理,编程式事务粒度更细)

Spring的Web层包括 Web、Servlet、WebSocket和 Portlet 组件,具体介绍如下。

  • Web 模块,提供面向 web 的基本功能和面向 web 的应用上下文,比如多部分(multipart)文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。
  • Web-MVC 模块,为 web 应用提供了模型视图控制(MVC)和 REST Web服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成。
  • Web-Socket 模块,为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
  • Web-Portlet 模块,提供了用于 Portlet 环境的 MVC 实现,并反映了 spring-webmvc 模块的功能。

Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 SPEL 表达式语言模块组成,具体介绍如下。

  • Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
  • Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IOC和 DI 功能。
  • Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
  • SPEL 表达式模块:是运行时查询和操作对象图的强大的表达式语言

Spring的其他模块还有 AOP、Aspects、Instrumentation 、Messaging,具体介绍如下。

  • AOP 模块,提供了面向方面(切面)的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。
  • Aspects 模块,提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。
  • Instrumentation 模块,在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。
  • Messaging 模块,为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。

Test模块,测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试

Sping核心容器

核心容器由 spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring 表达式语言,Spring Expression Language)等模块组成,它们的细节如下:

  • spring-core 模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。
  • spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
  • spring-context,模块建立在由 core和 beans 模块的基础上建立起来的,它以一种类似于 JNDI 注册的方式访问对象。Context 模块继承自 Bean 模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过 Servelet 容器)等功能。Context 模块也支持 Java EE 的功能,比如 EJB、JMX 和远程调用等。ApplicationContext 接口是 Context 模块的焦点。
  • spring-context-support 提供了对第三方集成到 Spring 上下文的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
  • spring-expression 模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是 JSP2.1 规范中定义的统一表达式语言的扩展,支持 set 和 get 属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从 Spring IoC 容器检索对象,还支持列表的投影、选择以及聚合等。

它们的完整依赖关系如下图所示:

所以我们使用时一般引入spring-context一般就够了。

Spring 框架的优势

依据组成结构其实我们也可以看的出,Spring的一些优势:

  • 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API,在更换或升级Spring框架时,不需要修改原来的业务逻辑代码
  • 控制反转:IOC——Inversion of Control,指的是将对象的创建权交给 Spring 去创建。使用 Spring 之前,对象的创建都是由我们自在代码中new创建。而使用 Spring 之后。对象的创建都是给了 Spring 框架。
  • 依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用 setXX 方法去设置,而是通过配置赋值。
  • 面向切面编程:Aspect Oriented Programming——AOP
  • 容器:Spring 是一个容器,因为它包含并且管理应用对象的生命周期
  • 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
  • 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上 Spring 自身也提供了表现层的 SpringMVC 和持久层的 Spring JDBC)

正因为有这么多优点,当今Java web开发才是Spring生态的天下

总结一下

其实所有的框架或者所有的软件模式的发展都是朝着高内聚,低耦合这一目标前行的,例如前面学习的MyBatis就是把代码和SQL分离、Spring的IOC思想就是把对象间的依赖关系分离进行统一管理,这么做有很大的好处,我们引入了一个中介,虽然增加了一些系统复杂度,但是这个中介却将系统给我们打理的简单明了,清晰规范,资源集中管理,统一调度。这对于企业级的软件开发是非常有必要的,我认为这才是框架存在的意义。

相关文章
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
5月前
|
Java Maven 数据安全/隐私保护
详解 Java AOP:面向方面编程的核心概念与 Spring 实现
详解 Java AOP:面向方面编程的核心概念与 Spring 实现
85 1
|
3月前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
5月前
|
Java 数据库连接 Spring
Spring底层架构核心概念总结
Spring底层架构核心概念总结
|
5月前
|
消息中间件 Java Maven
深入理解Spring Boot Starter:概念、特点、场景、原理及自定义starter
深入理解Spring Boot Starter:概念、特点、场景、原理及自定义starter
|
5月前
|
前端开发 安全 Java
Spring EL表达式:概念、特性与应用深入解析
Spring EL表达式:概念、特性与应用深入解析
|
5月前
|
XML 负载均衡 Java
Spring Boot 中实现负载均衡:概念、功能与实现
【6月更文挑战第28天】在分布式系统中,负载均衡(Load Balancing)是指将工作负载和流量分配到多个服务器或服务实例上,以提高系统可用性和响应速度。负载均衡器可以是硬件设备,也可以是软件解决方案。
283 0
|
5月前
|
XML Java 数据库
Spring5系列学习文章分享---第五篇(事务概念+特性+案例+注解声明式事务管理+参数详解 )
Spring5系列学习文章分享---第五篇(事务概念+特性+案例+注解声明式事务管理+参数详解 )
32 0
|
5月前
|
SQL Java 数据库连接
Spring5系列学习文章分享---第四篇(JdbcTemplate+概念配置+增删改查数据+批量操作 )
Spring5系列学习文章分享---第四篇(JdbcTemplate+概念配置+增删改查数据+批量操作 )
35 0
|
5月前
|
XML Java 数据格式
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))
52 0