目录
Spring AOP和AspectJ AOP
在Spring AOP 中,关注点和横切关注的区别
Spring 框架中的单例 Bean 是线程安全的吗
Spring 是怎么解决循环依赖的?
事务隔离级别
事务的传播级别
Spring 事务实现方式
Spring框架的事务管理有哪些优点
事务注解的本质
Spring AOP和AspectJ AOP
Spring AOP是属于运行时增强,而AspectJ是编译时增强。Spring AOP基于代理(Proxying),而
AspectJ基于字节码操作(Bytecode Manipulation)。
Spring AOP已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。
AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。
如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比
SpringAOP快很多。
在Spring AOP 中,关注点和横切关注的区别
关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的一个功能。 横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。
那什么是连接点呢?连接点代表一个应用程序的某个位置,在这个位置我们可以插入一个AOP切面,它实际上是个应用程序执行Spring AOP的位置。切入点是什么?切入点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点。
通知
通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码段。
Spring切面可以应用五种类型的通知:
before:前置通知,在一个方法执行前被调用。
after: 在方法执行之后调用的通知,无论方法执行是否成功。
after-returning: 仅当方法成功完成后执行的通知。
after-throwing: 在方法抛出异常退出时执行的通知。
around: 在方法执行之前和之后调用的通知。
Spring的IOC
(1)IOC就是控制反转,是指创建对象的控制权的转移。以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系。对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。
(2)最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
(3)Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来
形成可重用的功能组件。
Spring 框架中的单例 Bean 是线程安全的吗
Spring 框架并没有对单例 Bean 进行任何多线程的封装处理。
关于单例 Bean 的线程安全和并发问题,需要开发者自行去搞定。
单例的线程安全问题,并不是 Spring 应该去关心的。Spring 应该做的是,提供根据配置,创建单例 Bean 或多例 Bean 的功能。
当然,但实际上,大部分的 Spring Bean 并没有可变的状态,所以在某种程度上说 Spring 的单例Bean 是线程安全的。如果你的 Bean 有多种状态的话,就需要自行保证线程安全。最浅显的解决办法,就是将多态 Bean 的作用域(Scope)由 Singleton 变更为 Prototype。
Spring 是怎么解决循环依赖的?
整个流程大致如下:
- 首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在
初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建
出来; - 然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来;
- 这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A)。这
个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories),通过
ObjectFactory 提前曝光,所以可以通过 ObjectFactory#getObject() 方法来拿到 A 对象。C 拿
到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中; - 回到 B,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路
就已经完成了初始化过程了。
关键字:三级缓存,提前曝光
事务隔离级别
事务隔离级别是数据库系统中的一个重要概念,它定义了并发事务之间数据可见性的规则,以确保数据的一致性和完整性。以下是数据库系统中常见的四种事务隔离级别:
- 读未提交(Read Uncommitted):这是最低的隔离级别,允许一个事务读取到另一个未提交事务修改的数据,可能导致脏读、不可重复读和幻读等问题。
- 读已提交(Read Committed):事务只能读取已经提交的数据,这样可以避免脏读问题,但仍然可能存在不可重复读和幻读问题。
- 可重复读(Repeatable Read):在可重复读隔离级别下,一个事务在执行过程中多次读取相同的数据时,能够保证数据不会被其他事务修改,从而避免脏读和不可重复读,但仍然可能存在幻读问题。
- 串行化(Serializable):这是最高的隔离级别,事务会按顺序一个接一个地执行,不允许并发执行,可以避免脏读、不可重复读和幻读等并发问题,但会降低系统的并发性能。
不同的数据库管理系统可能会在这些标准隔离级别上进行微调或提供额外的隔离级别。在高并发环境下,选择合适的隔离级别是确保数据完整性和一致性的关键决策之一。不可重复读可以导致事务无法依赖于之前读取的数据,从而影响一致性。
事务的传播级别
Spring 框架支持多种事务传播级别,这些级别定义了在调用方法时如何处理当前作用域内的事务。以下是Spring支持的主要事务传播级别:
- PROPAGATION_REQUIRED:这是默认的事务传播级别,如果存在事务则加入该事务,如果不存在则创建一个新的事务。这个级别适用于大多数业务场景。
- PROPAGATION_SUPPORTS:如果上下文已经存在事务,则支持当前事务的加入;如果没有事务,则以非事务方式执行。这个级别的应用场景较少。
- PROPAGATION_MANDATORY:要求上下文中必须存在事务,否则会抛出异常。这个级别用于确保某些代码段在有事务的环境下执行。
- PROPAGATION_REQUIRES_NEW:每次都会创建一个新的事务,并挂起上下文中的事务,直到新事务执行完毕后才恢复上下文的事务。这个级别适用于那些需要在一个新事务中执行的操作。
- PROPAGATION_NOT_SUPPORTED:如果上下文已经存在事务,则挂起当前事务,并以非事务方式执行;如果上下文没有事务,则不执行任何操作。这个级别用于确保某些代码段在无事务的环境下执行。
- PROPAGATION_NEVER:上下文中不能存在事务,如果有事务则抛出异常,强制停止执行。这个级别用于防止在非事务环境下执行的事务操作。
- PROPAGATION_NESTED:如果上下文中已经存在事务,则嵌套事务执行;如果上下文没有事务,则创建一个新的事务并执行嵌套事务。嵌套事务在开始时会创建一个保存点,如果嵌套事务失败,可以回滚到此保存点。
这些传播级别可以根据业务需求和系统设计来选择,以确保事务的正确性和一致性。
Spring 事务实现方式
编程式事务管理:这意味着你可以通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
声明式事务管理:这种方式意味着你可以将事务管理和业务代码分离。你只需要通过注解或者XML配置管理事务。
Spring框架的事务管理有哪些优点
- 它为不同的事务API(如JTA, JDBC, Hibernate, JPA, 和JDO)提供了统一的编程模型。
- 它为编程式事务管理提供了一个简单的API而非一系列复杂的事务API(如JTA)
- 它支持声明式事务管理。
- 它可以和 Spring 的多种数据访问技术很好的融合。
事务三要素
数据源:表示具体的事务性资源,是事务的真正处理者,如MySQL等。
事务管理器:像一个大管家,从整体上管理事务的处理过程,如打开、提交、回滚等。
事务应用和属性配置:像一个标识符,表明哪些方法要参与事务,如何参与事务,以及一些相关属性如隔离级别、超时时间等
事务注解的本质
@Transactional 这个注解仅仅是一些(和事务相关的)元数据,在运行时被事务基础设施读取消费,并使用这些元数据来配置bean的事务行为。 大致来说具有两方面功能,一是表明该方法要参与事务,二是配置相关属性来定制事务的参与方式和运行行为
声明式事务主要是得益于Spring AOP。使用一个事务拦截器,在方法调用的前后/周围进行事务性增强(advice),来驱动事务完成。
@Transactional注解既可以标注在类上,也可以标注在方法上。当在类上时,默认应用到类里的所有方法。如果此时方法上也标注了,则方法上的优先级高。 另外注意方法一定要是public的。
原文链接:Spring-3 · 语雀