JYM大家好,这里是布洛妮娅, 鸭鸭的更文频道😎,本篇文章是对
Spring
的一个学习记录和总结!!
一、Spring的优点?
Spring 是一个轻量级应用框架,它提供了 IOC 和 AOP 这两个核心的功能.
它的核心目的是为了简化企业级应用程序的开发,使得开发者只需要关心业务需求,不需要关心 Bean 的管理, 以及通过切面增强功能减少代码的侵入性.
从 Spring 本身的特性来看:
轻量
:Spring 是轻量的,基本的版本大约 2MB.IOC/DI
:Spring 通过 IOC 容器实现了 Bean 的生命周期的管理,以及通过 DI 实现依赖注入,从而实现了对象依赖的松耦合管理.AOP
:Spring 支持面向切面的编程,从而把应用业务逻辑和系统服务分开.MVC框架
:Spring MVC 提供了功能更加强大且更加灵活的 Web 框架支持.事务管理
:Spring 通过 AOP 实现了事务的统一管理,对应用开发中的事务处理提供了非常灵活的支持.
二、Spring中事务的传播行为有哪些?
所谓的事务传播行为,就是多个声明了事务的方法相互调用的时候,这个事务应该如何传播. 比如说,methodA()调用 methodB(),两个方法都显示的开启了事务。那么 methodB()是开启一个新事务,还是继续在 methodA()这个事务中执行?就取决于事务的传播行为.
在 Spring 中,定义了 7 种事务传播行为:
REQUIRED
:默认的 Spring 事物传播级别,如果当前存在事务,则加入这个事务,如果不存在事务,就新建一个事务.REQUIRE_NEW
:不管是否存在事务,都会新开一个事务,新老事务相互独立.外部事务抛出异常回滚不会影响内部事务的正常提交.NESTED
:如果当前存在事务,则嵌套在当前事务中执行.如果当前没有事务,则新建一个事务,类似REQUIRE_NEW.SUPPORTS
:表示支持当前事务,如果当前不存在事务,以非事务的方式执行.NOT_SUPPORTED
:表示以非事务的方式来运行,如果当前存在事务,则把当前事务挂起.MANDATORY
:强制事务执行,若当前不存在事务,则抛出异常.NEVER
:以非事务的方式执行,如果当前存在事务,则抛出异常. Spring 事务传播级别一般不需要定义,默认就是 PROPAGATION_REQUIRED,除非在嵌套事务的情况下需要重点了解.
三、Spring Bean的作用域?
Spring的作用域分为:
singleton
:唯一 Bean 实例,Spring 中的 Bean 默认都是单例的。prototype
:每次请求都会创建一个新的 Bean 实例。request
:每一次 HTTP 请求都会产生一个新的 Bean,该 Bean 仅在当前 HTTP request 内有效。session
:每一次 HTTP 请求都会产生一个新的 Bean,该 Bean 仅在当前 HTTP session 内有效。global-session
:全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring 5 已经没有了。Portlet 是能够生成语义代码(如 HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。
在开发过程中,对有状态的 Bean 建议使用 Prototype
,对无状态建议使用 Singleton
。
四、Spring中什么样的Bean会存在线程安全问题?
Spring 容器中的 Bean 其实都是根据我们自己写的类来创建的实例。因此,Spring 中的 Bean 是否线程安全, 跟 Spring 容器无关,只是交由 Spring 容器托管而已.
从第三点我们可以知道:
prototype(多例) Bean 每次都会新创建新实例,也就是说线程之间不存在 Bean 共享的问题。因此,多例 Bean 是不存在线程安全问题的.
而单例 Bean 是所有线程共享一个实例,因此,就可能会存在线程安全问题。但是单例 Bean 又分为无状态 Bean 和有状态 Bean。在多线程操作中只会对 Bean 的成员变量进行查询操作,不会修改成员变量的值,这样的 Bean 称之为无状态 Bean。所以,可想而知,无状态的单例 Bean 是不存在线程安全问题的。但是,在多线程操作中如果需要对 Bean 中的成员变量进行数据更新操作,这样的 Bean 称之为有状态Bean,所以,有状态的单例 Bean 就可能存在线程安全问题.
五、如何处理Spring Bean的线程安全的问题?
- 将Bean的作用域由Singleton改为Prototype.
- 在类中定义ThreadLocal的成员变量,并将需要的可变成员变量保存到ThreadLocal中.
ThreadLocal本身就具备线程隔离的特性,相当于为每个线程提供一个独立的变量副本,每个线程只需要操作自己线程副本变量,从而解决线程安全的问题.
PS:在使用完ThreadLocal后我们应该手动调用它的remove方法,防止发生内存泄露.