Spring 是 Java 企业级应用的开源
开发框架。Spring 主要用来开发 Java 应用,但有些扩展是针对构建 J2EE 平台的 WEB 应用。Spring 框架目标是简化
Java 企业级应用开发,并通过 POJO 为基础的编程模型促进良好的编程习惯。
Spring 官网
Spring Framework 中文文档
2MB
。应用服务
和系统服务
分开。本地事务
下至全局事务
(JTA)。unchecked
异常。上图中包含了 Spring 框架的所有模块,这些模块可以满足一切企业级应用开发
的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。下面分别对这些模块的作用进行简单介绍。
数据访问/集成模块包括
JDBC
、ORM
、OXM
、JMS
和Transactions
模块,具体介绍如下:
Spring 的 Web 层包括
WebSocket
、Servlet
、Web
和Portlet
模块,具体介绍如下:
提供了面向切面编程实现,提供比如日志记录、权限控制、性能统计等通用功能和业务逻辑分离的技术,并且能动态的把这些功能添加到需要的代码中,这样各司其职,降低业务逻辑和通用功能的耦合。
提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
Spring 4.0 以后新增了消息(Spring-Messaging)模块,该模块提供了对消息传递体系结构和协议的支持。
Spring 的核心容器是
其他模块建立的基础
,由Beans
、Core
、Context 上下文
和SpEL
表达式语言模块组成,没有这些核心容器,也不可能有 AOP、Web 等上层的功能。具体介绍如下:
Spring 支持 Junit 和 TestNG 测试框架,而且还额外提供了一些基于 Spring 的测试功能,比如在测试 Web 框架时,模拟 Http 请求的功能。
控制反转
核心思想就是由 Spring 负责对象的创建
。在对象创建过程中,Spring 会自动根据依赖关系,将它依赖的对象注入到当前对象
中,这就是所谓的“依赖注入
”。
控制反转
的重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过依赖注入
来实现的!
IoC 是 Inversion of Control
的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。IoC 带来的最大改变不是代码层面的,而是从思想层面上发生了“主从换位”的改变。原本调用者是主动的一方,它想要使用什么资源就会主动出击,自己创建(new Obejct()
);但在 Spring 应用中,IoC 容器掌握着主动权,调用者则变成了被动的一方,被动的等待 IoC 容器创建它所需要的对象(getBean()
)。
IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下:
DI—Dependency Injection
,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:谁依赖谁?为什么需要依赖?谁注入谁?注入了什么?
Spring 容器启动时,查找并加载需要被管理的 Bean,对其进行实例化。先进行属性注入,设置相关属性和依赖。Bean 根据自身实现的接口或指定的初始化方法进行初始化,初始化完成后则可以使用该 Bean。Spring 停止容器时,Bean 通过 destory() 方法或者指定的销毁方法进行销毁。其具体流程如下图所示。
Bean 生命周期的整个执行过程描述如下:
BeanNameAware
接口,则 Spring 调用 Bean 的 setBeanName()
方法传入当前 Bean 的 id 值。BeanFactoryAware
接口,则 Spring 调用 setBeanFactory()
方法传入当前工厂实例的引用。ApplicationContextAware
接口,则 Spring 调用 setApplicationContext()
方法传入当前 ApplicationContext
实例的引用。BeanPostProcessor
接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation()
对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。InitializingBean
接口,则 Spring 将调用 afterPropertiesSet()
方法。init-method
属性指定了初始化方法,则调用该初始化方法。BeanPostProcessor
和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()
。此时,Bean 已经可以被应用系统使用了。DisposableBean
接口,则 Spring 会调用 destory()
方法销毁 Bean;如果在配置文件中通过 destory-method
属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 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 容器依据某种规则(自动装配的规则,有五种),为指定的 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 来自动装配。 |
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 进行定义。 |
事务(Transaction)是基于关系型数据库(RDBMS)的企业应用的重要组成部分。在软件开发领域,事务扮演者十分重要的角色,用来确保应用程序数据的完整性和一致性。
事务具有 4 个特性:原子性、一致性、隔离性和持久性,简称为
ACID
特性。
Spring 支持两种类型的事务管理:
选择编程式事务还是声明式事务,很大程度上就是在控制权细粒度和易用性之间进行权衡。
在实际应用中,经常会出现多个事务同时对同一数据执行不同操作,来实现各自的任务的情况。此时就有可能导致脏读、幻读以及不可重复读等问题的出现。事务的隔离级别定义了一个事务可能受其他并发事务影响的程度。
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 | 支持当前事务,如果不存在事务,则以非事务方式执行。 |