spring-面试题

1.spring是什么



轻量级开发框架,为java提供了基础架构支持,简化了企业级应用开发,让开发者只需要关注业务逻辑。

2.spring的设计核心是什么
IOC和AOP

ioc: Spring 的核心就是一个大的工厂容器,可以维护所有对象的创建和依赖关系,Spring 工厂用于生成 Bean,并且管理 Bean 的生命周期,实现高内聚低耦合的设计理念。

aop:Spring 提供了面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等切面功能。

3.IOC和AOP面试题
谈谈你对IOC和AOP的理解

4.spring的优点和缺点


优点:
①Ioc的支持:spring容器维护对象的创建和依赖关系管理。
②AOP的支持:面向切面编程,实现日志和权限拦截等功能。
③声明事务的支持:通过配置进行事务管理,不需要手动编写。
④方便集成各种框架。

缺点:
①使用大量反射机制,占内存,不如直接调用效率高。
②没有做到依赖管理。
③集成的框架耦合度高,不易拆分。

5.spring中bean的作用域


①singleton(单例模式): 默认作用域,在spring容器中一个bean只创建一个实例,所有对bean的请求和引用都会返回这个实例。适用都是无状态的bean。
②prototype(原型模式): 每次请求都会为bean创建一个实例。适用都是有状态的bean。
③request(请求作用域): 为每一个HTTP请求创建一个实例,在请求完成以后,bean会失效,会被垃圾回收器回收。
④session(会话作用域): 为每一个HTTP会话创建一个实例,不同会话使用不同实例,session销毁,bean失效。
⑤global-session(全局作用域): Spring5 已经没有了。

有状态: 有数据存储功能。
无状态: 不会保存数据。

6.spring中bean的注入方式


①构造器注入
②Setter注入
③接口注入(灵活性和易用性差,Spring4已经废弃)

7.BeanFactory 和 ApplicationContext有什么区别?


是spring的核心接口,都可以作为容器,ApplicationContext是BeanFactory的子接口。
BeanFactory: 是spring最底层的接口,包含各种Bean的定义和Bean的管理。
ApplicationContext: 作为BeanFactory的派生,除了有BeanFactory的功能以外,还提供了更多的功能。

区别:
① BeanFactroy采用的是延迟加载形式来注入Bean的,使用到bean才会加载。ApplicationContext一次性加载所有bean。
② BeanFactory需要手动注册,而ApplicationContext则是自动注册。
③ BeanFactory不支持国际化,ApplicationContext支持国际化(实现MessageSource接口)。

8.循环依赖的情况,怎么解决?


循环依赖:A依赖B,B依赖C,C依赖A,形成了闭环。

①构造器的循环依赖: 这种依赖spring是处理不了的,直接抛出异常。
②单例模式下的setter循环依赖: 通过"三级缓存"处理循环依赖,能处理。
③多例模式下的setter循环依赖: 不能处理,会一直产生新的Bean,导致OOM。

OOM(Out Of Memory)是计算机科学中的一个术语,指的是“内存溢出”或“内存耗尽”的情况。当程序或系统尝试使用超过其可用内存量的资源时,就会发生OOM错误

9.spring中单例Bean是线程安全的吗?


不是,因为所有线程共享一个单例Bean,存在资源的竞争所以是线程不安全的,实际上大部分时间Bean是无状态的,所以说在某种程度上来说Bean其实是安全的。如果是有状态,那就需要开发人员修改bean的作用域。singleton单例模式改为原型模式prototype。

有状态: 有数据存储功能。
无状态: 不会保存数据。

10.spring如何处理线程的并发问题?


①把成员变量写在方法内。
②使用ThreadLocal,ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
③修改bean的作用域,singleton单例模式改为原型模式prototype。(@Scope(“prototype”))
④使用synchronized修饰。

11.spring中bean的生命周期


bean: 是由 Spring IoC 容器实例化、组装和管理的对象。
正常情况: 当bean不在被使用,就会被回收。
单例模式: spring中bean的生命周期分为:实例化Bean->Bean属性填充->初始化Bean->销毁Bean。
多例模式: spring无法进行管理,所以将生命周期交给用户控制。


13.在 Spring中如何注入一个java集合?


Spring提供以下几种集合的配置元素:
想要注入java集合,就是注入集合类。
list: 类型用于注入一列值,允许有相同的值。
set: 类型用于注入一组值,不允许有相同的值。
map: 类型用于注入一组键值对,键和值都可以为任意类型。
props: 类型用于注入一组键值对,键和值都只能为String类型。

14.bean的自动装配


spring会在上下文中自动寻找,并自动给bean装配属性。之前属性需要我们手动注入。

15.spring用到了那些设计模式


工厂模式: beanFactory就用到了简单工厂模式。
单例模式: Bean默认为单例模式。
代理模式: AOP用到了JDK的动态代理模式。
模板模式: 减少代码冗余,Jdbc模板等。
观察者模式: 定义对象间的一对多的关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。spring监听器的实现就用了观察者模式。

16.spring的常用注解


@Component(任何层)、@Controller(表现层)、@Service(逻辑层)、@Repository(持久层): 被修饰的类,会被spring扫描到并注入到bean容器中(实例化对象)。
@Scope: 设置spring的作用域。
@Bean: 用于将方法返回值对象放入容器。
@Import: 在一个配置类中导入其它配置类的内容。
@AutoWired: 按照类型匹配注入。
@Qualifier: 和AutoWired联合使用,在按照类型匹配的基础上,在按照名称匹配。
@Resource: 按照名称匹配依赖注入。
@Configuration: 被此注解标注的类,会被 Spring 认为是配置类。
@ComponentScan: 用于对组件(Component)进行扫描。
@Transactional: 可以用于类和方法上,具有事务管理的功能
@Value: 将外部的值动态注入到 Bean 中。
       @Value(“${}”):可以获取配置文件的值。
        @Value(“#{}”):表示SpEl(Spring Expression Language是Spring表达式语言,可以在运行时查询和操作数据。)表达式通常用来获取 bean 的属性,或者调用 bean 的某个方法。

17.spring 事务实现方式有哪些?


编程式: beginTransaction()、commit()、rollback()等事务管理相关的方法,灵活度高,但是维护性差。
声明式: 利用注解或者xml配置,将业务和事务分离出来。

18.spring事务的实现方式和实现原理


spring事务就是对数据库事务的支持,没有数据库的事务支持,spring是无法提供事务
功能的。

19.说一下 spring 的事务隔离?


五大隔离级别。
ISOLATION_DEFAULT: 默认值,使用数据库的隔离级别。
ISOLATION_READ_UNCOMMITTED: 读未提交。
ISOLATION_READ_COMMITTED: 读已提交。
ISOLATION_REPEATABLE_READ: 可重复读。
ISOLATION_SERIALIZABLE: 序列化。

20.spring事务的传播行为


REQUIRED(默认):默认事务传播行为,存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
REQUIRE_NEW:它不管是否存在事务,它都会新开启一个事务来执行,新老事务相互独立的,外部事务抛出异常,并不会影响内部事务的一个正常提交。
NESTED:如果当前存在事务,就嵌套当前事务中去执行,如果当前没有事务,那么就新建一个事务,类似 REQUIRE_NEW这个样一个传播行为。
SUPPORTS:表示支持当前当前的事务,如果当前不存在事务,就以非事务的方式去执行。
NOT_SUPPORT: 总是非事务地执行,并挂起任何存在的事务。
MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
NEVER:就是以非事务的方式来执行,如果存在事务则抛出一个异常。

2.Spring 有哪些模块呢?

最主要的七大模块:

  1. Spring Core:Spring 核心,它是框架最基础的部分,提供 IoC 和依赖注入 DI 特性。
  2. Spring Context:Spring 上下文容器,它是 BeanFactory 功能加强的一个子接口。
  3. Spring Web:它提供 Web 应用开发的支持。
  4. Spring MVC:它针对 Web 应用中 MVC 思想的实现。
  5. Spring DAO:提供对 JDBC 抽象层,简化了 JDBC 编码,同时,编码更具有健壮性。
  6. Spring ORM:它支持用于流行的 ORM 框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO 的整合等。
  7. Spring AOP:即面向切面编程,它提供了与 AOP 联盟兼容的编程实现。

3.Spring 有哪些常用注解呢?

①、@Controller:用于标注控制层组件。

②、@RestController:是@Controller 和 @ResponseBody 的结合体,返回 JSON 数据时使用。

③、@RequestMapping:用于映射请求 URL 到具体的方法上,还可以细分为:

  • @GetMapping:只能用于处理 GET 请求
  • @PostMapping:只能用于处理 POST 请求
  • @DeleteMapping:只能用于处理 DELETE 请求

④、@ResponseBody:直接将返回的数据放入 HTTP 响应正文中,一般用于返回 JSON 数据。

⑤、@RequestBody:表示一个方法参数应该绑定到 Web 请求体。

⑥、@PathVariable:用于接收路径参数,比如 @RequestMapping(“/hello/{name}”),这里的 name 就是路径参数。

⑦、@RequestParam:用于接收请求参数。比如 @RequestParam(name = "key") String key,这里的 key 就是请求参数。

容器类注解有哪些呢?
  • @Component:标识一个类为 Spring 组件,使其能够被 Spring 容器自动扫描和管理。
  • @Service:标识一个业务逻辑组件(服务层)。比如 @Service("userService"),这里的 userService 就是 Bean 的名称。
  • @Repository:标识一个数据访问组件(持久层)。
  • @Autowired:按类型自动注入依赖。
  • @Configuration:用于定义配置类,可替换 XML 配置文件。
  • @Value:用于将 Spring Boot 中 application.properties 配置的属性值赋值给变量。
AOP 方面有哪些注解呢?

@Aspect 用于声明一个切面,可以配合其他注解一起使用,比如:

  • @After:在方法执行之后执行。
  • @Before:在方法执行之前执行。
  • @Around:方法前后均执行。
  • @PointCut:定义切点,指定需要拦截的方法。

4.Spring 中应用了哪些设计模式呢?

①、工厂模式:IoC 容器本身可以看作是一个巨大的工厂,负责创建和管理 Bean 的生命周期和依赖关系。

像 BeanFactory 和 ApplicationContext 接口都提供了工厂模式的实现,负责实例化、配置和组装 Bean。

②、代理模式:AOP 的实现就是基于代理模式的,如果配置了事务管理,Spring 会使用代理模式创建一个连接数据库的代理对象,来进行事务管理。

③、单例模式:Spring 容器中的 Bean 默认都是单例的,这样可以保证 Bean 的唯一性,减少系统开销。

④、模板模式:Spring 中的 JdbcTemplate,HibernateTemplate 等以 Template 结尾的类,都使用了模板方法模式。

比如,我们使用 JdbcTemplate,只需要提供 SQL 语句和需要的参数就可以了,至于如何创建连接、执行 SQL、处理结果集等都由 JdbcTemplate 这个模板方法来完成。

④、观察者模式:Spring 事件驱动模型就是观察者模式很经典的一个应用,Spring 中的 ApplicationListener 就是观察者,当有事件(ApplicationEvent)被发布,ApplicationListener 就能接收到信息。

⑤、适配器模式:Spring MVC 中的 HandlerAdapter 就用了适配器模式。它允许 DispatcherServlet 通过统一的适配器接口与多种类型的请求处理器进行交互。

⑥、策略模式:Spring 中有一个 Resource 接口,它的不同实现类,会根据不同的策略去访问资源。

5.说一说什么是 IoC?什么是 DI?

所谓的IoC(控制反转,Inversion of Control),就是由容器来控制对象的生命周期和对象之间的关系。以前是我们想要什么就自己创建什么,现在是我们需要什么容器就帮我们送来什么。

引入IoC之前和引入IoC之后

引入IoC之前和引入IoC之后

也就是说,控制对象生命周期的不再是引用它的对象,而是容器,这就叫控制反转

DI(依赖注入,Dependency Injection)

就好像 A 类需要 B 类,以前是 A 类自己 new 一个 B 类,现在是有人把 B 类注入到 A 类里。

为什么要使用 IoC 呢?

在平时的 Java 开发中,如果我们要实现某一个功能,可能至少需要两个以上的对象来协助完成,在没有 Spring 之前,每个对象在需要它的合作对象时,需要自己 new 一个,比如说 A 要使用 B,A 就对 B 产生了依赖,也就是 A 和 B 之间存在了一种耦合关系。

有了 Spring 之后,就不一样了,创建 B 的工作交给了 Spring 来完成,Spring 创建好了 B 对象后就放到容器中,A 告诉 Spring 我需要 B,Spring 就从容器中取出 B 交给 A 来使用。

至于 B 是怎么来的,A 就不再关心了

8.你知道 Spring 容器启动阶段会干什么吗?

Spring 的 IoC 容器工作的过程,其实可以划分为两个阶段:容器启动阶段Bean 实例化阶段

其中容器启动阶段主要做的工作是加载和解析配置文件,保存到对应的 Bean 定义中。

容器启动和Bean实例化阶段

9.能说一下 Spring Bean 生命周期吗?

Spring 中 Bean 的生命周期大致分为四个阶段:实例化(Instantiation)、属性赋值(Populate)、初始化(Initialization)、销毁(Destruction)。

  • 实例化:Spring 容器根据 Bean 的定义创建 Bean 的实例,相当于执行构造方法,也就是 new 一个对象。
  • 属性赋值:相当于执行 setter 方法为字段赋值。
  • 初始化:初始化阶段允许执行自定义的逻辑,比如设置某些必要的属性值、开启资源、执行预加载操作等,以确保 Bean 在使用之前是完全配置好的。
  • 销毁:相当于执行 = null,释放资源。

10.Bean 定义和依赖定义有哪些方式?

有三种方式:直接编码方式配置文件方式注解方式

Bean依赖配置方式

Bean依赖配置方式

  • 直接编码方式:我们一般接触不到直接编码的方式,但其实其它的方式最终都要通过直接编码来实现。
  • 配置文件方式:通过 xml、propreties 类型的配置文件,配置相应的依赖关系,Spring 读取配置文件,完成依赖关系的注入。
  • 注解方式:注解方式应该是我们用的最多的一种方式了,在相应的地方使用注解修饰,Spring 会扫描注解,完成依赖关系的注入。

11.有哪些依赖注入的方法?

构造方法注入

通过调用类的构造方法,将接口实现类通过构造方法变量传入

属性注入

通过 Setter 方法完成调用类所需依赖的注入

工厂方法注入

  • 静态工厂注入

    静态工厂顾名思义,就是通过调用静态工厂的方法来获取自己需要的对象,为了让 Spring 管理所有对象,我们不能直接通过"工程类.静态方法()"来获取对象,而是依然通过 Spring 注入的形式获取:

非静态工厂注入

非静态工厂,也叫实例工厂,意思是工厂方法不是静态的,所以我们需要首先 new 一个工厂实例,再调用普通的实例方法。

14.Spring 中的单例 Bean 会存在线程安全问题吗?

Spring Bean 的默认作用域是单例(Singleton),这意味着 Spring 容器中只会存在一个 Bean 实例,并且该实例会被多个线程共享。

如果单例 Bean 是无状态的,也就是没有成员变量,那么这个单例 Bean 是线程安全的。比如 Spring MVC 中的 Controller、Service、Dao 等,基本上都是无状态的。

但如果 Bean 的内部状态是可变的,且没有进行适当的同步处理,就可能出现线程安全问题

单例 Bean 线程安全问题怎么解决呢?

第一,使用局部变量。局部变量是线程安全的,因为每个线程都有自己的局部变量副本。尽量使用局部变量而不是共享的成员变量。

第二,尽量使用无状态的 Bean,即不在 Bean 中保存任何可变的状态信息。

第三,同步访问。如果 Bean 中确实需要保存可变状态,可以通过 synchronized 关键字或者 Lock 接口来保证线程安全。

或者将 Bean 中的成员变量保存到 ThreadLocal 中,ThreadLocal 可以保证多线程环境下变量的隔离。

再或者使用线程安全的工具类,比如说 AtomicIntegerConcurrentHashMapCopyOnWriteArrayList 等。

第四,将 Bean 定义为原型作用域(Prototype)。原型作用域的 Bean 每次请求都会创建一个新的实例,因此不存在线程安全问题。

@Component
@Scope("prototype")
public class MyService {
    // 实例变量
}

16.那 Spring 怎么解决循环依赖的呢?

Spring 通过三级缓存(Three-Level Cache)机制来解决循环依赖。

  1. 一级缓存:用于存放完全初始化好的单例 Bean。
  2. 二级缓存:用于存放正在创建但未完全初始化的 Bean 实例。
  3. 三级缓存:用于存放 Bean 工厂对象,用于提前暴露 Bean。

17.为什么要三级缓存?⼆级不⾏吗?

不行,主要是为了**⽣成代理对象**。如果是没有代理的情况下,使用二级缓存解决循环依赖也是 OK 的。但是如果存在代理,三级没有问题,二级就不行了。

因为三级缓存中放的是⽣成具体对象的匿名内部类,获取 Object 的时候,它可以⽣成代理对象,也可以返回普通对象。使⽤三级缓存主要是为了保证不管什么时候使⽤的都是⼀个对象。

假设只有⼆级缓存的情况,往⼆级缓存中放的显示⼀个普通的 Bean 对象,Bean 初始化过程中,通过 BeanPostProcessor 去⽣成代理对象之后,覆盖掉⼆级缓存中的普通 Bean 对象,那么可能就导致取到的 Bean 对象不一致了。

18.@Autowired 的实现原理?

实现@Autowired 的关键是:AutowiredAnnotationBeanPostProcessor

在 Bean 的初始化阶段,会通过 Bean 后置处理器来进行一些前置和后置的处理。

实现@Autowired 的功能,也是通过后置处理器来完成的。这个后置处理器就是 AutowiredAnnotationBeanPostProcessor。

  • Spring 在创建 bean 的过程中,最终会调用到 doCreateBean()方法,在 doCreateBean()方法中会调用 populateBean()方法,来为 bean 进行属性填充,完成自动装配等工作。

  • 在 populateBean()方法中一共调用了两次后置处理器,第一次是为了判断是否需要属性填充,如果不需要进行属性填充,那么就会直接进行 return,如果需要进行属性填充,那么方法就会继续向下执行,后面会进行第二次后置处理器的调用,这个时候,就会调用到 AutowiredAnnotationBeanPostProcessor 的 postProcessPropertyValues()方法,在该方法中就会进行@Autowired 注解的解析,然后实现自动装配。

23.Spring 事务的种类?

在 Spring 中,事务管理可以分为两大类:声明式事务管理和编程式事务管理。

三分恶面渣逆袭:Spring事务分类

三分恶面渣逆袭:Spring事务分类

介绍一下编程式事务管理?

编程式事务可以使用 TransactionTemplate 和 PlatformTransactionManager 来实现,需要显式执行事务。允许我们在代码中直接控制事务的边界,通过编程方式明确指定事务的开始、提交和回滚。

介绍一下声明式事务管理?

声明式事务是建立在 AOP 之上的。其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在目标方法执行完之后根据执行情况提交或者回滚事务。

相比较编程式事务,优点是不需要在业务逻辑代码中掺杂事务管理的代码, Spring 推荐通过 @Transactional 注解的方式来实现声明式事务管理,也是日常开发中最常用的。

不足的地方是,声明式事务管理最细粒度只能作用到方法级别,无法像编程式事务那样可以作用到代码块级别。

说说两者的区别?
  • 编程式事务管理:需要在代码中显式调用事务管理的 API 来控制事务的边界,比较灵活,但是代码侵入性较强,不够优雅。
  • 声明式事务管理:这种方式使用 Spring 的 AOP 来声明事务,将事务管理代码从业务代码中分离出来。优点是代码简洁,易于维护。但缺点是不够灵活,只能在预定义的方法上使用事务。

24.说说 Spring 的事务隔离级别?

①、ISOLATION_DEFAULT:使用数据库默认的隔离级别(你们爱咋咋滴 😁),MySQL 默认的是可重复读,Oracle 默认的读已提交。

②、ISOLATION_READ_UNCOMMITTED:读未提交,允许事务读取未被其他事务提交的更改。这是隔离级别最低的设置,可能会导致“脏读”问题。

③、ISOLATION_READ_COMMITTED:读已提交,确保事务只能读取已经被其他事务提交的更改。这可以防止“脏读”,但仍然可能发生“不可重复读”和“幻读”问题。

④、ISOLATION_REPEATABLE_READ:可重复读,确保事务可以多次从一个字段中读取相同的值,即在这个事务内,其他事务无法更改这个字段,从而避免了“不可重复读”,但仍可能发生“幻读”问题。

⑤、ISOLATION_SERIALIZABLE:串行化,这是最高的隔离级别,它完全隔离了事务,确保事务序列化执行,以此来避免“脏读”、“不可重复读”和“幻读”问题,但性能影响也最大。

25.Spring 的事务传播机制?

事务的传播机制定义了在方法被另一个事务方法调用时,这个方法的事务行为应该如何。

Spring 提供了一系列事务传播行为,这些传播行为定义了事务的边界和事务上下文如何在方法调用链中传播。

三分恶面渣逆袭:6种事务传播机制

三分恶面渣逆袭:6种事务传播机制

  • REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。Spring 的默认传播行为。
  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
  • MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • REQUIRES_NEW:总是启动一个新的事务,如果当前存在事务,则将当前事务挂起。
  • NOT_SUPPORTED:总是以非事务方式执行,如果当前存在事务,则将当前事务挂起。
  • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前事务不存在,则行为与 REQUIRED 一样。嵌套事务是一个子事务,它依赖于父事务。父事务失败时,会回滚子事务所做的所有操作。但子事务异常不一定会导致父事务的回滚。

protected 和 private 加事务会生效吗

在 Spring 中,只有通过 Spring 容器的 AOP 代理调用的公开方法(public method)上的@Transactional注解才会生效

如果在 protected、private 方法上使用@Transactional,这些事务注解将不会生效,原因:Spring 默认使用基于 JDK 的动态代理(当接口存在时)或基于 CGLIB 的代理(当只有类时)来实现事务。这两种代理机制都只能代理公开的方法。

26.声明式事务实现原理了解吗?

Spring 的声明式事务管理是通过 AOP(面向切面编程)和代理机制实现的。

第一步,在 Bean 初始化阶段创建代理对象

Spring 容器在初始化单例 Bean 的时候,会遍历所有的 BeanPostProcessor 实现类,并执行其 postProcessAfterInitialization 方法。

在执行 postProcessAfterInitialization 方法时会遍历容器中所有的切面,查找与当前 Bean 匹配的切面,这里会获取事务的属性切面,也就是 @Transactional 注解及其属性值。

然后根据得到的切面创建一个代理对象,默认使用 JDK 动态代理创建代理,如果目标类是接口,则使用 JDK 动态代理,否则使用 Cglib。

第二步,在执行目标方法时进行事务增强操作

当通过代理对象调用 Bean 方法的时候,会触发对应的 AOP 增强拦截器,声明式事务是一种环绕增强,对应接口为MethodInterceptor,事务增强对该接口的实现为TransactionInterceptor,类图如下:

27.声明式事务在哪些情况下会失效?

三分恶面渣逆袭:声明式事务的几种失效的情况

三分恶面渣逆袭:声明式事务的几种失效的情况

1、@Transactional 应用在非 public 修饰的方法上

如果 Transactional 注解应用在非 public 修饰的方法上,Transactional 将会失效。

是因为在 Spring AOP 代理时,TransactionInterceptor (事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource 的 computeTransactionAttribute方法,获取 Transactional 注解的事务配置信息。

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }
}

此方法会检查目标方法的修饰符是否为 public,不是 public 则不会获取 @Transactional 的属性配置信息。

2、@Transactional 注解属性 propagation 设置错误
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行;错误使用场景:在业务逻辑必须运行在事务环境下以确保数据一致性的情况下使用 SUPPORTS。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:总是以非事务方式执行,如果当前存在事务,则挂起该事务。错误使用场景:在需要事务支持的操作中使用 NOT_SUPPORTED。
  • TransactionDefinition.PROPAGATION_NEVER:总是以非事务方式执行,如果当前存在事务,则抛出异常。错误使用场景:在应该在事务环境下执行的操作中使用 NEVER。
3、@Transactional 注解属性 rollbackFor 设置错误

rollbackFor 用来指定能够触发事务回滚的异常类型。Spring 默认抛出未检查 unchecked 异常(继承自 RuntimeException 的异常)或者 Error 才回滚事务,其他异常不会触发回滚事务。

三分恶面渣逆袭:Spring默认支持的异常回滚

三分恶面渣逆袭:Spring默认支持的异常回滚

// 希望自定义的异常可以进行回滚
@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class)

若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。

4、同一个类中方法调用,导致@Transactional 失效

开发中避免不了会对同一个类里面的方法调用,比如有一个类 Test,它的一个方法 A,A 调用本类的方法 B(不论方法 B 是用 public 还是 private 修饰),但方法 A 没有声明注解事务,而 B 方法有。

则外部调用方法 A 之后,方法 B 的事务是不会起作用的。这也是经常犯错误的一个地方。

那为啥会出现这种情况呢?其实还是由 Spring AOP 代理造成的,因为只有事务方法被当前类以外的代码调用时,才会由 Spring 生成的代理对象来管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值