【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思想就是把对象间的依赖关系分离进行统一管理,这么做有很大的好处,我们引入了一个中介,虽然增加了一些系统复杂度,但是这个中介却将系统给我们打理的简单明了,清晰规范,资源集中管理,统一调度。这对于企业级的软件开发是非常有必要的,我认为这才是框架存在的意义。

相关文章
|
30天前
|
安全 Java 测试技术
Spring Boot集成支付宝支付:概念与实战
【4月更文挑战第29天】在电子商务和在线业务应用中,集成有效且安全的支付解决方案是至关重要的。支付宝作为中国领先的支付服务提供商,其支付功能的集成可以显著提升用户体验。本篇博客将详细介绍如何在Spring Boot应用中集成支付宝支付功能,并提供一个实战示例。
77 2
|
30天前
|
Java 关系型数据库 数据库
Spring Boot多数据源及事务管理:概念与实战
【4月更文挑战第29天】在复杂的企业级应用中,经常需要访问和管理多个数据源。Spring Boot通过灵活的配置和强大的框架支持,可以轻松实现多数据源的整合及事务管理。本篇博客将探讨如何在Spring Boot中配置多数据源,并详细介绍事务管理的策略和实践。
65 3
|
21天前
|
Java Spring 容器
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
37 0
|
30天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
44 2
|
30天前
|
XML Java API
Spring Boot 整合 LiteFlow 规则引擎:概念与实战
【4月更文挑战第30天】在现代软件开发中,规则引擎允许我们以声明式的方式定义业务逻辑和决策路径。LiteFlow 是一个轻量级、易于使用的组件式规则引擎,它可以与 Spring Boot 应用无缝整合。本文将介绍如何在 Spring Boot 项目中引入 LiteFlow,实现灵活的业务流程管理。
70 0
|
30天前
|
安全 Java 测试技术
利用Java反射机制提高Spring Boot的代码质量:概念与实战
【4月更文挑战第29天】Java反射机制提供了一种强大的方法来在运行时检查或修改类和对象的行为。在Spring Boot应用中,合理利用反射可以提高代码的灵活性和可维护性。本篇博客将探讨Java反射的核心概念,并展示如何通过反射提高Spring Boot项目的代码质量。
37 0
|
30天前
|
监控 Java 测试技术
Spring Boot与事务钩子函数:概念与实战
【4月更文挑战第29天】在复杂的业务逻辑中,事务管理是确保数据一致性和完整性的关键。Spring Boot提供了强大的事务管理机制,其中事务钩子函数(Transaction Hooks)允许开发者在事务的不同阶段插入自定义逻辑。本篇博客将详细探讨事务钩子函数的概念及其在Spring Boot中的应用。
40 1
|
30天前
|
安全 Java 数据安全/隐私保护
Spring Boot优雅实现多租户架构:概念与实战
【4月更文挑战第29天】在多租户系统中,一个应用实例服务于多个租户,每个租户享有独立的数据视图,而应用的基础设施被共享。这样的架构不仅优化了资源使用,还能降低维护和运营成本。本文将详细介绍如何在Spring Boot中实现多租户架构,并提供具体的实战案例。
101 2
|
30天前
|
前端开发 Java 数据安全/隐私保护
Spring Boot使用拦截器:概念与实战
【4月更文挑战第29天】拦截器(Interceptors)在Spring Boot应用中常用于在请求处理的前后执行特定的代码,如日志记录、认证校验、权限控制等。本篇博客将详细介绍Spring Boot中拦截器的概念及其实战应用,帮助开发者理解和利用拦截器来增强应用的功能。
36 0
|
30天前
|
监控 Java Sentinel
Spring Cloud Sentinel:概念与实战应用
【4月更文挑战第28天】在分布式微服务架构中,确保系统的稳定性和可靠性至关重要。Spring Cloud Sentinel 为微服务提供流量控制、熔断降级和系统负载保护,有效预防服务雪崩。本篇博客深入探讨 Spring Cloud Sentinel 的核心概念,并通过实际案例展示其在项目中的应用。
38 0