写在最前
Spring 是 Java 企业级应用的开源
开发框架。Spring 主要用来开发 Java 应用,但有些扩展是针对构建 J2EE 平台的 WEB 应用。Spring 框架目标是简化
Java 企业级应用开发,并通过 POJO 为基础的编程模型促进良好的编程习惯。
Spring 框架特性
- 轻量:Spring 是轻量的,基本的版本大约
2MB
。 - 控制反转:Spring 通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
- 面向切面的编程(AOP):Spring 支持面向切面的编程,并且把
应用服务
和系统服务
分开。 - 容器:Spring 包含并管理应用中对象的生命周期和配置。
- MVC 框架:Spring 的 WEB 框架是个精心设计的框架,是 Web 框架的一个很好的替代品。
- 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至
本地事务
下至全局事务
(JTA)。 - 异常处理:Spring 提供方便的 API 把具体技术相关的异常(比如由 JDBC,Hibernate or JDO 抛出的)转化为一致的
unchecked
异常。
Spring 组成模块
上图中包含了 Spring 框架的所有模块,这些模块可以满足一切企业级应用开发
的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。下面分别对这些模块的作用进行简单介绍。
Data Access/Integration(数据访问/集成)模块
数据访问/集成模块包括
JDBC
、ORM
、OXM
、JMS
和Transactions
模块,具体介绍如下:
- JDBC 模块:提供了一个 JBDC 的样例模板,使用这些模板能消除传统冗长的 JDBC 编码还有必须的事务控制,而且能享受到 Spring 管理事务的好处。
- ORM 模块:提供与流行的“对象-关系”映射框架无缝集成的 API,包括 JPA、JDO、Hibernate 和 MyBatis 等。而且还可以使用 Spring 事务管理,无需额外控制事务。
- OXM 模块:提供了一个支持 Object/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。将 Java 对象映射成 XML 数据或者将 XML 数据映射成 Java 对象。
- JMS 模块:指 Java 消息服务,提供一套“消息生产者、消息消费者”模板用于更加简单的使用 JMS,JMS 用于在两个应用程序之间或分布式系统中发送消息,进行异步通信。
- Transactions 事务模块:支持编程和声明式事务管理。
Web 模块
Spring 的 Web 层包括
WebSocket
、Servlet
、Web
和Portlet
模块,具体介绍如下:
- WebSocket 模块:提供了简单的接口,用户只要实现响应的接口就可以快速的搭建 WebSocket Server,从而实现双向通讯。
- Servlet 模块:提供了一个 Spring MVC Web 框架实现。Spring MVC 框架提供了基于注解的请求资源注入、更简单的数据绑定、数据验证等及一套非常易用的 JSP 标签,完全无缝与 Spring 其他技术协作。
- Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IOC 容器初始化以及 Web 应用上下文。
- Portlet 模块:提供了在 Portlet 环境中使用 MVC 实现,类似 Web-Servlet 模块的功能。
AOP 模块
提供了面向切面编程实现,提供比如日志记录、权限控制、性能统计等通用功能和业务逻辑分离的技术,并且能动态的把这些功能添加到需要的代码中,这样各司其职,降低业务逻辑和通用功能的耦合。
Aspects 模块
提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
Instrumentation 模块
提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
Messaging 模块
Spring 4.0 以后新增了消息(Spring-Messaging)模块,该模块提供了对消息传递体系结构和协议的支持。
Core Container(Spring 的核心容器)模块
Spring 的核心容器是
其他模块建立的基础
,由Beans
、Core
、Context 上下文
和SpEL
表达式语言模块组成,没有这些核心容器,也不可能有 AOP、Web 等上层的功能。具体介绍如下:
- Beans 模块:提供了框架的基础部分,包括控制反转和依赖注入。
- Core 核心模块:封装了 Spring 框架的底层部分,包括资源访问、类型转换及一些常用工具类。
- Context 上下文模块:建立在 Core 和 Beans 模块的基础之上,集成 Beans 模块功能并添加资源绑定、数据验证、国际化、Java EE 支持、容器生命周期、事件传播等。ApplicationContext 接口是上下文模块的焦点。
- SpEL 模块:提供了强大的表达式语言支持,支持访问和修改属性值,方法调用,支持访问及修改数组、容器和索引器,命名变量,支持算数和逻辑运算,支持从 Spring 容器获取 Bean,它也支持列表投影、选择和一般的列表聚合等。
Test 模块
Spring 支持 Junit 和 TestNG 测试框架,而且还额外提供了一些基于 Spring 的测试功能,比如在测试 Web 框架时,模拟 Http 请求的功能。
Spring IoC / DI
控制反转
核心思想就是由 Spring 负责对象的创建
。在对象创建过程中,Spring 会自动根据依赖关系,将它依赖的对象注入到当前对象
中,这就是所谓的“依赖注入
”。
控制反转
的重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过依赖注入
来实现的!
控制反转(IoC)
IoC 是 Inversion of Control
的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。IoC 带来的最大改变不是代码层面的,而是从思想层面上发生了“主从换位”的改变。原本调用者是主动的一方,它想要使用什么资源就会主动出击,自己创建(new Obejct()
);但在 Spring 应用中,IoC 容器掌握着主动权,调用者则变成了被动的一方,被动的等待 IoC 容器创建它所需要的对象(getBean()
)。
IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下:
- 开发人员通过 XML 配置文件、注解、Java 配置类等方式,对各个对象以及它们之间的依赖关系进行配置;
- Spring 启动时,IoC 容器会自动根据对象定义,将这些对象创建并管理起来。这些被 IoC 容器创建并管理的对象被称为 Spring Bean。
- 当我们想要使用某个 Bean 时,可以直接从 IoC 容器中获取(例如通过 ApplicationContext 的 getBean() 方法),而不需要手动通过代码(例如 new Obejct() 的方式)创建。
依赖注入(DI)
DI—Dependency Injection
,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:谁依赖谁?为什么需要依赖?谁注入谁?注入了什么?
- 谁依赖谁:应用程序依赖于 IoC 容器;
- 为什么需要依赖:应用程序需要 IoC 容器来提供对象需要的外部资源;
- 谁注入谁:IoC 容器注入应用程序某个对象,应用程序依赖的对象;
- 注入了什么:注入某个对象所需要的外部资源(包括对象、资源、常量数据);
Spring Bean 的生命周期
Spring 容器启动时,查找并加载需要被管理的 Bean,对其进行实例化。先进行属性注入,设置相关属性和依赖。Bean 根据自身实现的接口或指定的初始化方法进行初始化,初始化完成后则可以使用该 Bean。Spring 停止容器时,Bean 通过 destory() 方法或者指定的销毁方法进行销毁。其具体流程如下图所示。
Bean 生命周期的整个执行过程描述如下:
- Spring 启动,查找并加载需要被 Spring 管理的 Bean,对 Bean 进行实例化。
- 对 Bean 进行属性注入。
- 如果 Bean 实现了
BeanNameAware
接口,则 Spring 调用 Bean 的setBeanName()
方法传入当前 Bean 的 id 值。 - 如果 Bean 实现了
BeanFactoryAware
接口,则 Spring 调用setBeanFactory()
方法传入当前工厂实例的引用。 - 如果 Bean 实现了
ApplicationContextAware
接口,则 Spring 调用setApplicationContext()
方法传入当前ApplicationContext
实例的引用。 - 如果 Bean 实现了
BeanPostProcessor
接口,则 Spring 调用该接口的预初始化方法postProcessBeforeInitialzation()
对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。 - 如果 Bean 实现了
InitializingBean
接口,则 Spring 将调用afterPropertiesSet()
方法。 - 如果在配置文件中通过
init-method
属性指定了初始化方法,则调用该初始化方法。 - 如果
BeanPostProcessor
和 Bean 关联,则 Spring 将调用该接口的初始化方法postProcessAfterInitialization()
。此时,Bean 已经可以被应用系统使用了。 - 如果在 中指定了该 Bean 的作用域为 singleton,则将该 Bean 放入 Spring IoC 的缓存池中,触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用域为 prototype,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
- 如果 Bean 实现了
DisposableBean
接口,则 Spring 会调用destory()
方法销毁 Bean;如果在配置文件中通过destory-method
属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
Bean 作用域
作用域 | 描述 |
---|---|
singleton | 在 Spring IoC 容器仅存在一个 Bean 实例,Bean 以单例方式存在,默认值 |
prototype | 每次从容器中调用 Bean 时,都返回一个新的实例,即每次调用 getBean() 时,相当于执行 newXxxBean() |
request | 每次 HTTP 请求都会创建一个新的 Bean,该作用域仅适用于 WebApplicationContext 环境 |
session | 同一个 HTTP Session 共享一个 Bean,不同 Session 使用不同的 Bean,仅适用于 WebApplicationContext 环境 |
global-session | 一般用于 Portlet 应用环境,该作用域仅适用于 WebApplicationContext 环境 |
Spring 自动装配模式
Spring 的自动装配功能可以让 Spring 容器依据某种规则(自动装配的规则,有五种),为指定的 Bean 从应用的上下文(AppplicationContext 容器)中查找它所依赖的 Bean,并自动建立 Bean 之间的依赖关系。
Spring 共提供了 5 中自动装配规则,它们分别与 Autowire 属性的 5 个取值对应,具体说明如下表:
模式 | 描述 |
---|---|
no | 这是默认的设置,它意味着没有自动装配,你应该使用显式的 bean 引用来连线。 |
byName | 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 |
byType | 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 |
Spring 首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
Spring AOP
AOP 的全称是“Aspect Oriented Programming
”,译为“面向切面编程”,和 OOP(面向对象编程)类似,它也是一种编程思想
。
Spring 框架的一个关键组件是面向切面的编程(AOP)框架。面向切面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。在软件开发过程中有各种各样的很好的切面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
相关术语
名称 | 说明 |
---|---|
Joinpoint(连接点) | AOP 的核心概念,指的是程序执行期间明确定义的一个点,例如方法的调用、类初始化、对象实例化等。 在 Spring 中,连接点则指可以被动态代理拦截目标类的方法。 |
Pointcut(切入点) | 又称切点,指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。 |
Advice(通知) | 指拦截到 Joinpoint 之后要执行的代码,即对切入点增强的内容。 |
Target(目标) | 指代理的目标对象,通常也被称为被通知(advised)对象。 |
Weaving(织入) | 指把增强代码应用到目标对象上,生成代理对象的过程。 |
Proxy(代理) | 指生成的代理对象。 |
Aspect(切面) | 切面是切入点(Pointcut)和通知(Advice)的结合。 |
代理机制
Spring 在运行期会为目标对象生成一个动态代理对象,并在代理对象中实现对目标对象的增强(当执行一个方法时,你可以在方法执行之前或之后添加额外的功能)。
Spring AOP 的底层是通过以下 2 种动态代理机制,为目标对象(Target Bean)执行横向织入的。
代理技术 | 描述 |
---|---|
JDK 动态代理 | Spring AOP 默认的动态代理方式,若目标对象实现了若干接口,Spring 使用 JDK 的 java.lang.reflect.Proxy 类进行代理。 |
CGLIB 动态代理 | 若目标对象没有实现任何接口,Spring 则使用 CGLIB 库生成目标对象的子类,以实现对目标对象的代理。 |
通知的类型
通知 | 描述 |
---|---|
前置通知(before advice) | 在一个方法执行之前,执行通知。 |
后置通知(after (finally) advice) | 在一个方法执行之后,不论是正常返回还是异常退出,执行通知。 |
返回后通知(after returning advice) | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知(after throwing advice) | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知(around advice) | 在建议方法调用之前和之后,执行通知。 |
正常情况下执行顺序
异常情况下执行顺序
切面类型
切面类型 | 接口 | 描述 |
---|---|---|
一般切面 | org.springframework.aop.Advisor | Spring AOP 默认的切面类型。 由于 Advisor 接口仅包含一个 Advice(通知)类型的属性,而没有定义 PointCut(切入点),因此它表示一个不带切点的简单切面。这样的切面会对目标对象(Target)中的所有方法进行拦截并织入增强代码。由于这个切面太过宽泛,因此我们一般不会直接使用。 |
切点切面 | org.springframework.aop.PointcutAdvisor | Advisor 的子接口,用来表示带切点的切面,该接口在 Advisor 的基础上还维护了一个 PointCut(切点)类型的属性。使用它,我们可以通过包名、类名、方法名等信息更加灵活的定义切面中的切入点,提供更具有适用性的切面。 |
引介切面 | org.springframework.aop.IntroductionAdvisor | Advisor 的子接口,用来代表引介切面,引介切面是对应引介增强的特殊的切面,它应用于类层面上,所以引介切面适用 ClassFilter 进行定义。 |
Spring 事务
事务(Transaction)是基于关系型数据库(RDBMS)的企业应用的重要组成部分。在软件开发领域,事务扮演者十分重要的角色,用来确保应用程序数据的完整性和一致性。
事务具有 4 个特性:原子性、一致性、隔离性和持久性,简称为
ACID
特性。
- 原子性(Atomicity):一个事务是一个不可分割的工作单位,事务中包括的动作要么都做要么都不做。
- 一致性(Consistency):事务必须保证数据库从一个一致性状态变到另一个一致性状态,一致性和原子性是密切相关的。
- 隔离性(Isolation):一个事务的执行不能被其它事务干扰,即一个事务内部的操作及使用的数据对并发的其它事务是隔离的,并发执行的各个事务之间不能互相打扰。
- 持久性(Durability):持久性也称为永久性,指一个事务一旦提交,它对数据库中数据的改变就是永久性的,后面的其它操作和故障都不应该对其有任何影响。
事务管理方式
Spring 支持两种类型的事务管理:
- 编程式事务管理:这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。
- 声明式事务管理:这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。
选择编程式事务还是声明式事务,很大程度上就是在控制权细粒度和易用性之间进行权衡。
- 编程式对事物控制的细粒度更高,我们能够精确的控制事务的边界,事务的开始和结束完全取决于我们的需求,但这种方式存在一个致命的缺点,那就是事务规则与业务代码耦合度高,难以维护,因此我们很少使用这种方式对事务进行管理。
- 声明式事务易用性更高,对业务代码没有侵入性,耦合度低,易于维护,因此这种方式也是我们最常用的事务管理方式。
事务的隔离级别
在实际应用中,经常会出现多个事务同时对同一数据执行不同操作,来实现各自的任务的情况。此时就有可能导致脏读、幻读以及不可重复读等问题的出现。事务的隔离级别定义了一个事务可能受其他并发事务影响的程度。
Spring 中提供了以下隔离级别:
方法 | 说明 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED | 允许读取尚未提交的更改,可能导致脏读、幻读和不可重复读 |
ISOLATION_READ_COMMITTED | Oracle 默认级别,允许读取已提交的并发事务,防止脏读,可能出现幻读和不可重复读 |
ISOLATION_REPEATABLE_READ | MySQL 默认级别,多次读取相同字段的结果是一致的,防止脏读和不可重复读,可能出现幻读 |
ISOLATION_SERIALIZABLE | 完全服从 ACID 的隔离级别,防止脏读、不可重复读和幻读 |
事务的传播行为
Spring 事务的传播行为说的是,当多个事务同时存在的时候,Spring 如何处理这些事务的行为。
Spring 提供了以下 7 种不同的事务传播行为:
名称 | 说明 |
---|---|
PROPAGATION_MANDATORY | 支持当前事务,如果不存在当前事务,则引发异常。 |
PROPAGATION_NESTED | 如果当前事务存在,则在嵌套事务中执行。 |
PROPAGATION_NEVER | 不支持当前事务,如果当前事务存在,则引发异常。 |
PROPAGATION_NOT_SUPPORTED | 不支持当前事务,始终以非事务方式执行。 |
PROPAGATION_REQUIRED | 默认传播行为,如果存在当前事务,则当前方法就在当前事务中运行,如果不存在,则创建一个新的事务,并在这个新建的事务中运行。 |
PROPAGATION_REQUIRES_NEW | 创建新事务,如果已经存在事务则暂停当前事务。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果不存在事务,则以非事务方式执行。 |
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/78364.html