【金三银四】Spring面试题

news2024/11/18 0:23:54

目录

  • 1、什么是Spring
  • 2、说一下Spring的IOC
  • 3、Spring的AOP
  • 4、连接点?切入点?
  • 5、Spring AOP 是通过什么实现的
  • 6、Spring Bean的生命周期是怎么样的?
  • 7、Spring Bean的初始化过程是怎么样
  • 8、Spring的事务传播机制有哪些?
  • 9、Autowired和Resource的区别
  • 10、Spring中如何开启事务?
      • 编程式事务管理
      • 声明式事务管理
  • 11、Spring中用到了哪些设计模式
  • 12、什么是Spring的循环依赖问题?
  • 13、Spring事务失效可能是哪些原因?
  • 14、什么是Spring的三级缓存?如何解决循环依赖的问题的?

1、什么是Spring

Spring 是一个开源的Java应用程序框架,由Rod Johnson创建并在2003年发布,旨在简化企业级Java应用的开发。Spring以其轻量级、模块化和基于依赖注入(Dependency Injection, DI)的设计原则而闻名,它通过提供一套全面的解决方案,可以帮助开发者更容易地构建、测试、部署和管理Java应用程序。

Spring框架的核心特性包括:

  1. 核心容器(Core Container): 这是Spring框架的基础,包含了BeanFactory,ApplicationContext等组件,用于创建、配置和管理Java对象(Bean)及其依赖关系。

  2. 依赖注入(DI): Spring通过DI机制管理对象的依赖关系,无需对象自己去查找或创建它所依赖的对象。开发者只需要声明依赖关系,Spring容器会在运行时自动注入。

  3. 面向切面编程(AOP): Spring提供了面向切面编程的支持,允许开发者将横切关注点(如事务管理、日志记录、权限检查等)与业务逻辑解耦,通过切面织入实现模块化的关注点分离。

  4. 数据访问/集成(Data Access/Integration): 包括对JDBC、ORM框架(如Hibernate、JPA)的支持,以及事务管理抽象,简化了数据访问层的开发。

  5. Web框架(Spring MVC): 提供了一个完整的MVC框架,用于构建web应用程序,支持RESTful服务的开发,并与前端技术良好集成。

  6. 消息传递(Messaging): 支持消息队列(如RabbitMQ、JMS)集成,便于构建分布式系统间的通信。

  7. 测试支持(Testing): 提供了一系列辅助工具,简化了对基于Spring的应用程序的单元测试和集成测试。

  8. Spring Boot: 是Spring家族的一员,进一步简化了新Spring应用的初始搭建以及开发过程,具有自动化配置、嵌入式服务器和健康检查等功能。

Spring框架因其高度的灵活性和可扩展性,已经成为现代Java企业级应用开发的事实标准之一。通过整合众多优秀开源项目和提供统一的编程和配置模型,Spring极大地提高了开发效率和代码质量。

2、说一下Spring的IOC

Spring框架中的IOC(Inversion of Control,控制反转)是其核心机制之一,它是一种设计原则,主要应用于软件的设计和开发中,用来降低模块之间的耦合度,增强程序的可扩展性和可维护性。在Spring框架的具体实现中,IOC容器扮演着至关重要的角色。

基本概念:

  • 控制反转:在传统的编程方式中,一个类往往需要自行创建它所依赖的对象。而在IOC中,这种创建和管理依赖对象的责任从应用程序本身转移到了一个专门的容器,即Spring的IOC容器。这意味着对象不再自己管理它们的依赖关系,而是由容器在运行时动态地为其注入所需要的依赖。

IOC容器的主要功能:

  1. 对象的生命周期管理:IOC容器负责创建对象实例,管理它们的生命周期,包括初始化、依赖注入、清理等阶段。

  2. 依赖注入(Dependency Injection, DI):IOC容器根据配置信息,负责将依赖关系传递给对象。注入方式包括构造器注入、setter注入或字段注入等。

  3. 松耦合:通过依赖注入,对象不再关心其依赖对象的创建细节,只需定义好接口,降低了不同模块之间的耦合度,使得代码更加模块化和可测试。

IOC容器的工作原理:

  • 配置:开发者通过XML、Java注解或者Java配置类等方式定义Bean(即构成应用程序的各种对象)以及它们的依赖关系。
  • 解析与注册:Spring IOC容器在启动时会读取这些配置信息,解析其中的Bean定义,并将Bean注册到容器中。
  • 依赖注入:当应用程序请求某个Bean时,IOC容器不仅会创建这个Bean,还会根据配置找到它依赖的所有其他Bean,并将它们注入进来,建立起对象间的依赖关系。

总结来说,Spring的IOC机制实现了应用程序组件之间的解耦,使得程序组件只需要关注自身的业务逻辑,而不需要关心彼此之间的依赖是如何管理和创建的,极大地提升了代码的复用性和系统的可维护性。

3、Spring的AOP

Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)是一个强大的模块,它允许开发者将横切关注点(Cross-cutting Concerns)从业务逻辑中分离出来,并以声明式的方式集中处理。横切关注点是指那些跨越多个对象或类的方法,如日志记录、事务管理、权限检查、性能监控等,这些功能在整个应用程序中往往是重复出现且与业务逻辑紧密相关的,但在传统OOP(面向对象编程)中很难做到模块化和复用。

在Spring AOP中,主要有以下几个核心概念:

  1. Aspect(切面):一个切面表示一个关注点的模块化,它包含advice(通知)和pointcut(切入点)。切面可以被看作是对系统行为进行修改或增强的模块。

  2. Advice(通知):通知是切面的具体实现,是在某个连接点(Join Point)执行的代码片段。通知有多种类型,包括前置通知(Before advice)、后置通知(After returning advice)、最终通知(After (finally) advice)、异常通知(After throwing advice)和环绕通知(Around advice)等。例如,前置通知就是在目标方法执行前执行的代码,环绕通知则可以完整地控制目标方法的执行过程。

  3. Pointcut(切入点):切入点定义了通知应该在哪种连接点上执行,它是一个或多个连接点的集合。连接点通常是方法执行这样的程序执行点。通过表达式(如AspectJ切点表达式)来匹配感兴趣的执行点。

  4. 连接点(Join Point):在Spring AOP中,连接点通常指的是方法执行的时刻,它是程序执行过程中明确的点,可以插入自定义的行为(即通知)。

  5. 代理(Proxying):Spring AOP通过生成代理对象来实现切面的编织(Weaving),即在代理对象的方法调用过程中,根据切面定义执行相应的通知。

使用Spring AOP,开发者可以编写简洁、清晰的切面类或注解,然后由Spring容器在运行时自动将这些切面织入到目标对象中,从而达到在不影响原有业务逻辑的前提下,实现横切关注点的统一处理和模块化管理的目的。这样既简化了代码,又增强了系统的可维护性和可扩展性。

4、连接点?切入点?

当然可以,通过一个简单的例子来帮助您理解Spring AOP中的连接点(Join Point)和切入点(Pointcut)的区别:

连接点(Join Point)
想象一下,你正在开发一个在线购物系统,其中包含了各种各样的类和方法,比如OrderService类中的placeOrder()方法用于处理订单,UserService类中的authenticateUser()方法用于用户身份验证,以及PaymentService类中的processPayment()方法用于处理支付操作等。在Spring AOP的上下文中,每一个方法调用就是一个潜在的连接点,因为每个方法都可以是应用AOP增强的地方。

例如:

  • OrderService.placeOrder()
  • UserService.authenticateUser()
  • PaymentService.processPayment()

切入点(Pointcut)
而切入点则是对这些众多连接点的一个特定选择或定义,它用来指定哪些连接点应该被特定的通知(Advice)所影响。例如,你可能想在所有的业务服务层方法执行前后都记录日志,这时你可以定义一个切入点,表达式可能是 "execution(* com.example.service..*(..))",这个表达式会匹配所有在com.example.service及其子包下的任何方法调用。

所以,如果我们的切面(Aspect)有一个日志记录的前置通知(Before advice),并且将其关联到上述切入点,则每当OrderServiceUserServicePaymentService中的任何方法被调用时,日志记录的代码就会被执行。这里的切入点就是对所有服务层方法调用这一组连接点的选择器。

5、Spring AOP 是通过什么实现的

Spring AOP 是通过代理模式实现的;

Spring AOP实现动态代理的两种方式:

  1. JDK动态代理

    • 当目标类至少实现了一个接口时,Spring AOP会选择使用Java的内置java.lang.reflect.Proxy类来创建代理对象。这种方式下,Spring会生成一个实现了目标类所实现的所有接口的代理类,然后在代理类的方法调用时插入切面逻辑(通知)。
  2. CGLIB代理

    • 当目标类没有实现任何接口时,Spring AOP会转而使用CGLIB库来生成一个继承自目标类的子类作为代理。CGLIB通过生成字节码技术为类创建子类,并覆盖其中的方法,在方法的前后插入切面逻辑。

    • CglibAopProxy是Spring框架中用于创建CGLIB代理的类,它同样实现了Spring的AopProxy接口,但它的getProxy方法会创建一个CGLIB代理对象,而非JDK动态代理对象。

这两种代理方式都是为了在不改变原始业务类代码的基础上,通过代理机制在目标方法执行前后添加额外的功能(例如事务管理、日志记录、权限检查等)。Spring框架根据目标类的特点智能选择合适的代理方式,确保AOP功能的透明实现。

6、Spring Bean的生命周期是怎么样的?

Spring框架管理的Bean(也就是Java对象)有一套完整的生命周期,涵盖了从创建到销毁的全过程。以下是Spring Bean的生命周期各阶段概述:

  1. 实例化(Instantiation)

    • Spring IoC容器通过反射调用类的无参构造函数创建Bean的实例。
  2. 填充属性(Populate Properties)

    • Spring容器利用BeanDefinition中的信息,通过setter方法或者构造器注入的方式为Bean实例设置属性值,即依赖注入(Dependency Injection)。
  3. 初始化(Initialization)

    • 初始化阶段涉及多个步骤:
      • 初始化前处理(Post-Process Before Initialization)
        • InstantiationAwareBeanPostProcessorpostProcessBeforeInitialization方法会被调用。
      • 初始化方法调用(Invocation of init-method)
        • 如果Bean定义中指定了init-method属性,那么Spring会在Bean实例化并填充属性之后调用该方法。
      • 初始化后处理(Post-Process After Initialization)
        • InitializingBean接口的afterPropertiesSet方法(如果Bean实现了该接口)会被调用。
        • SmartInitializingSingleton接口的方法(如果Bean实现了该接口)在所有Singletons都初始化完成后被调用。
        • BeanPostProcessorpostProcessAfterInitialization方法也会在此阶段被执行。
  4. 使用(Usage)

    • 完成初始化后,Bean就可以被Spring容器或其他Bean通过ApplicationContext获取并使用了。
  5. 销毁(Destruction)

    • 在Spring容器关闭时,Bean会经历销毁阶段:
      • 预销毁处理(Pre-Destruction Callback)
        • 如果Bean实现了DisposableBean接口,其destroy方法会被调用。
      • 指定销毁方法调用(Invocation of destroy-method)
        • 如果Bean定义中指定了destroy-method属性,则调用此方法来释放资源。
      • 销毁后处理(Post-Destruction Callbacks)
        • 虽然不在标准的生命周期内明确指出,但Spring容器可能还有其他的回调机制来清理资源。

总结来说,Spring Bean的生命周期是一个有序的过程,允许开发者在特定的点上通过自定义方法来介入Bean的创建和销毁流程,增强了程序的灵活性和可控制性。在整个过程中,Spring容器负责管理和协调这些活动,确保Bean的生命周期行为符合预期。

7、Spring Bean的初始化过程是怎么样

在这里插入图片描述

Spring Bean的初始化过程可以分为以下几个步骤:

  1. 实例化(Instantiation)

    • Spring IoC容器根据BeanDefinition信息创建Bean的实例,通常通过调用无参构造函数来实例化对象。
  2. 设置属性值(Population of Properties)

    • Spring容器接下来会根据BeanDefinition中定义的属性注入规则,为已实例化的Bean设置属性值,包括通过构造函数注入、setter方法注入或者其他自定义注入器实现的依赖注入。
  3. 初始化前回调(Callback before Initialization)

    • 如果Bean实现了org.springframework.beans.factory.config.BeanPostProcessor接口的子类,那么在其初始化之前,Spring会调用postProcessBeforeInitialization(Object bean, String beanName)方法对Bean进行预处理。
  4. 初始化方法调用(Invocation of Init Method)

    • 如果在Bean的定义中指定了init-method属性,Spring会在属性设置完成后调用这个方法来初始化Bean的内容。另外,如果Bean实现了org.springframework.beans.factory.InitializingBean接口,那么Spring会调用其afterPropertiesSet()方法进行初始化。
  5. 初始化后回调(Callback after Initialization)

    • 初始化方法执行完毕后,Spring再次调用BeanPostProcessor接口的postProcessAfterInitialization(Object bean, String beanName)方法,允许对初始化后的Bean做进一步的处理。

总的来说,Spring Bean的初始化过程确保了Bean具备了运行所需的状态,并完成了所有必要的初始化逻辑,之后该Bean便可以被应用程序正常地使用了。在Bean的生命周期中,初始化是非常关键的一个阶段,它使Bean达到了可以使用的准备状态。后续,Bean还会有注册销毁回调方法、正常使用直至最终销毁的过程。


实例化与初始化更详细地说:

实例化(Instantiation)
在Spring框架中,实例化是指Spring IoC容器负责通过反射调用对应的构造函数来创建Bean对象的过程。根据配置信息(如XML配置文件、注解或Java配置类),容器会选择合适的构造器参数并传递给构造函数,从而在Java堆内存中分配空间并生成Bean实例。

初始化(Initialization)
初始化发生在实例化之后,它是对新创建的Bean对象进一步设置和准备以供应用使用的阶段。在Spring中,初始化包括但不限于以下几个步骤:

  • 属性填充(Population):Spring容器会通过依赖注入(Dependency Injection)的方式,将配置好的属性值注入到Bean的相应字段或setter方法中,这一过程通常对应populateBean方法的执行。
  • 初始化方法调用:如果Bean定义中指定了初始化方法(如通过@PostConstruct注解标识的方法),Spring会在Bean的所有必要依赖都注入完成后调用这些方法,确保Bean已经准备好执行其业务逻辑。
  • Bean后处理器(Bean Post Processor)的应用:Spring容器还允许Bean后处理器参与到Bean的初始化流程中,它们可以在初始化前后进行额外的处理工作,比如通过afterPropertiesSet方法进行自定义初始化操作。

总之,在Spring容器管理Bean的生命周期中,实例化关注的是如何创建对象本身,而初始化则涉及到了对象创建后的状态设置和准备工作,确保Bean处于可使用的状态。

8、Spring的事务传播机制有哪些?

Spring的事务传播机制用于控制在多个事务方法相互调用时事务的行为。

在复杂的业务场景中,多个事务方法之间的调用可能会导致事务的不一致,如出现数据丢失、重复提交等问题,使用事务传播机制可以避免这些问题的发生,保证事务的一致性和完整性。

Spring框架提供了七种事务传播机制,用于控制事务方法在不同场景下的事务行为,特别是当事务方法互相调用时,如何管理和传播事务上下文。以下是Spring事务传播机制的详细介绍:

事务传播机制描述
Required如果当前存在事务,则加入该事务;如果当前没有事务,则新建一个事务。
Supports如果当前存在事务,则参与当前事务;若无事务,则不创建新事务,按非事务方式执行。
Mandatory必须在现有事务中执行,若当前无事务,则抛出异常。
Requires_New总是新建一个事务,如果当前存在事务,则挂起当前事务。新建的事务与外部事务相互独立。
Not_Supported执行时不支持事务,如果当前存在事务,则挂起当前事务直到方法执行完毕。
Never不允许在事务环境中执行,如果当前存在事务,则抛出异常。
Nested如果当前存在事务,则在嵌套事务内执行。嵌套事务可以单独提交或回滚,但外部事务失败时,所有嵌套事务都将被回滚。

通过在Spring中为方法注解@Transactional(propagation = Propagation.{类型})的方式来指定事务传播行为。其中,{类型}应替换为上述表格中的具体传播机制名称。

9、Autowired和Resource的区别

@Autowired@Resource 都是用来在Spring框架中进行依赖注入的注解,但它们有一些关键的区别:

  1. 来源和标准

    • @Autowired 是Spring框架自身提供的注解,位于 org.springframework.beans.factory.annotation.Autowired 包下。
    • @Resource 并非Spring独有,而是由Java EE规范引入的注解,它属于J2EE(现在称为Java EE)的一部分,位于 javax.annotation.Resource 包下。尽管如此,Spring框架仍然支持这个注解的依赖注入功能。
  2. 注入策略

    • @Autowired 默认按照类型(byType)进行依赖注入,也就是说,Spring容器会查找与字段或方法参数类型匹配的Bean进行注入。如果同一类型的Bean有多个候选者并且都没有明确指定Qualifier注解,那么在某些情况下可能会导致歧义,这时需要显式添加@Qualifier注解来指定具体的Bean。
    • @Resource 在Spring中默认按照名称(byName)进行注入,即尝试寻找与字段名或setter方法名相匹配的Bean名称进行注入。不过,当找不到与名称匹配的Bean时,@Resource 也会退而采用类型匹配的方式进行注入。
  3. 名称解析优先级

    • @Autowired 注解在考虑名称匹配时通常需要配合@Qualifier注解。
    • @Resource 更倾向于名称匹配,因此在Bean名称具有唯一性的情况下,它可以简化代码,因为无需额外的@Qualifier注解。
  4. 注解位置

    • 两者都可以用于字段、构造器以及setter方法上。
  5. 编译器支持和IDE提示

    • 对于IDEA等现代IDE而言,对于@Autowired@Resource的提示和支持可能有所不同,@Resource由于其标准性质,可能会得到更好的跨框架支持和工具提示。

综上所述,选择使用哪一个注解主要取决于项目的具体需求和团队约定。在Spring项目中,@Autowired 使用更为普遍,但在需要强调基于名称注入或者遵循Java EE规范的项目中,@Resource 可能是一个更好的选择。同时需要注意的是,为了能够使用@Resource,还需要确保在Spring配置中正确导入了相应的注解支持。

10、Spring中如何开启事务?

在Spring框架中开启事务主要有两种方式:编程式事务管理和声明式事务管理。

编程式事务管理

编程式事务管理要求开发人员在代码中手动管理事务的边界,包括开启、提交或回滚事务。使用编程式事务,你需要直接操作PlatformTransactionManager接口的实现类。例如,通过TransactionTemplate或直接调用TransactionManagerbeginTransaction()commit()rollback()方法来控制事务。

@Autowired
private PlatformTransactionManager transactionManager;

public void someServiceMethod() {
    TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
        // 执行业务操作
        ...
        transactionManager.commit(status);
    } catch (Exception e) {
        transactionManager.rollback(status);
        throw e;
    }
}

声明式事务管理

声明式事务管理则是通过在业务逻辑方法或类上添加@Transactional注解来启用事务,Spring框架会自动根据注解的属性来管理事务的生命周期。

@Service
public class SomeService {

    @Transactional
    public void someTransactionalMethod() {
        // 这个方法将在一个事务中执行
        // 如果方法体内的代码出现未捕获的异常,事务将自动回滚
        // 否则,事务将在方法结束时自动提交
        ...
    }
}

在Spring Boot应用中,声明式事务管理的开启更为便捷,通常只需要以下几步:

  1. 配置数据源和事务管理器,Spring Boot通常会自动配置DataSourceTransactionManagerJpaTransactionManager作为事务管理器。
  2. 在服务类或需要进行事务控制的方法上使用@Transactional注解。
  3. 确保Spring事务注解驱动已启用,通常在Spring Boot应用中,只要包含spring-txspring-orm等相关的起步依赖,以及正确的组件扫描配置,就会自动启用声明式事务的支持。

注意,为了事务生效,被@Transactional注解修饰的类必须是由Spring容器管理的Bean。此外,Spring AOP代理负责处理@Transactional注解,所以对于类内部方法调用事务可能不会生效,因为它绕过了AOP代理。如果需要在同一个类内部的方法调用之间也保持事务,可能需要调整设计或者明确通过代理对象来调用事务方法。

11、Spring中用到了哪些设计模式

Spring框架中广泛运用了许多设计模式,这里列举出Spring中常用的设计模式及其应用场景:

  1. 单例模式(Singleton Pattern)

    • Spring框架中的IoC容器默认将所有的Bean配置为单例模式,确保在整个应用程序中,每个Bean只有一个实例。
  2. 工厂模式(Factory Pattern)

    • Spring通过BeanFactoryApplicationContext实现了工厂模式,它们负责创建和管理Bean对象,降低了对象间的耦合度。
  3. 工厂方法模式(Factory Method Pattern)

    • 在Spring中,Bean的创建可以通过配置元数据(如XML配置或注解)指定,类似于工厂方法模式,可以根据不同条件创建不同类型的对象。
  4. 抽象工厂模式(Abstract Factory Pattern)

    • Spring通过一系列的子接口和实现类组成,可以根据配置创建不同类型的Bean集合,体现出抽象工厂模式的思想。
  5. 代理模式(Proxy Pattern)

    • Spring AOP(面向切面编程)通过动态代理(JDK代理或CGLIB代理)实现切面的织入,使得代理对象可以在方法调用前后执行增强逻辑。
  6. 适配器模式(Adapter Pattern)

    • 在Spring AOP和Spring MVC中,通过适配器模式转换接口,使得不同的类能够协同工作。例如,Spring AOP的通知(Advice)可以被适配到目标对象上,Spring MVC中控制器的适配器将HTTP请求映射到Controller方法上。
  7. 装饰者模式(Decorator Pattern)

    • 虽然不是直接体现在Spring框架核心功能上,但在处理Filter链、HandlerInterceptor链等场景时,可以通过装饰者模式的思想来扩展功能。
  8. 模板方法模式(Template Method Pattern)

    • 如Spring的JdbcTemplate类,它提供了一个执行SQL查询的基本结构,子类只需要实现特定的抽象方法来填充实际的SQL语句和处理结果。
  9. 策略模式(Strategy Pattern)

    • 在Spring框架中,策略模式可以用在处理不同类型的事务管理策略、数据源切换策略等场景。
  10. 责任链模式(Chain of Responsibility Pattern)

    • Spring的拦截器(Interceptor)和过滤器(Filter)可以形成一个责任链,请求依次经过链上的每一个拦截器或过滤器处理。
  11. 服务定位器模式(Service Locator Pattern)

    • Spring的IoC容器在某种程度上也可以看作是一种服务定位器,客户端通过容器获取所需的Bean,而不是直接new出来。
  12. 依赖注入(Dependency Injection,DI)

    • 虽然不是严格意义上的设计模式,但DI是一种设计原则,通过构造器注入、setter注入等方式实现了对象之间的解耦,体现了控制反转(Inversion of Control, IoC)思想。

这些设计模式在Spring框架中得到了深入应用,帮助构建了一个高度模块化、松耦合和易扩展的系统架构。

12、什么是Spring的循环依赖问题?

Spring的循环依赖问题是指在Spring框架的依赖注入过程中,两个或多个Bean之间相互依赖,形成了一个闭环引用的情况。具体来说,当Bean A依赖于Bean B,而Bean B反过来又依赖于Bean A,或者通过多条依赖链形成闭环(例如A->B->C->A),这样就会导致Spring容器在初始化这些Bean时陷入困境,因为每个Bean在完成初始化前都需要等待依赖的Bean被初始化,而依赖的Bean又在等待它本身的初始化完成,这就形成了循环依赖。

Spring框架对循环依赖的处理能力是有一定限制的,具体表现在以下几点:

  1. 构造器循环依赖
    Spring不能解决构造器注入导致的循环依赖。如果Bean A和Bean B在构造器中互相引用,Spring容器在创建Bean时发现循环依赖,将会抛出BeanCurrentlyInCreationException异常,表示无法解决循环依赖问题。

  2. setter注入或字段注入的循环依赖
    对于单例(Singleton)作用域的Bean,Spring通过三级缓存机制巧妙地解决了setter或字段注入造成的循环依赖问题。Spring在创建Bean时,会将正在创建但尚未初始化完全的Bean暂存起来,以便其他Bean在依赖注入时能够拿到。通过这种方式,Spring能够在大多数情况下处理好setter注入的循环依赖,即“一、二、二”形式的循环依赖(Bean A依赖于初始化阶段的Bean B,Bean B依赖于已实例化但未初始化完成的Bean A)。

请注意,对于原型(Prototype)作用域的Bean,Spring默认不解决循环依赖问题,同样会抛出异常。另外,如果Bean间依赖关系涉及到异步处理(如使用@Async注解)或其他特殊场景,也可能无法正确解决循环依赖。

13、Spring事务失效可能是哪些原因?

Spring事务失效的原因多种多样,以下是一些常见的情况:

  1. 方法未被Spring代理

    • 如果带有@Transactional注解的方法所在类没有被Spring容器管理(即没有使用@Service@Repository@Component@Controller等注解),则Spring不会为其生成代理对象,导致事务失效。
  2. 方法可见性问题

    • @Transactional注解的方法必须是public的,如果是private、protected或包访问权限,Spring AOP代理无法对其进行增强,事务功能将不会生效。
  3. 内部方法调用

    • 在同一个类中,如果一个非@Transactional的方法直接调用了同类中带有@Transactional注解的方法,事务将不会生效,因为Spring的AOP代理在这种情况下不会介入。解决办法是通过外部代理类或接口来进行调用。
  4. 异常处理不当

    • 如果事务方法内部捕获了异常并且没有再次抛出,Spring事务就不能感知到异常的发生,进而无法触发事务的回滚。只有未被捕获的、符合事务回滚规则的异常才能导致事务回滚。
  5. 事务传播行为配置错误

    • 不正确的事务传播行为可能导致事务未能正确开启、挂起或加入到现有的事务中。
  6. 未配置事务管理器

    • 如果没有正确配置事务管理器(如DataSourceTransactionManagerJpaTransactionManager),事务功能将不可用。
  7. 事务注解无效

    • 如果在方法上未启用@Transactional注解,或者注解被误配置(如没有指定正确的rollbackFor或noRollbackFor属性),则事务将不会生效。
  8. Spring AOP代理的问题

    • 若方法被final修饰,或者使用CGLIB代理时遇到final类或final方法,由于final方法不能被重写,因此Spring无法对这些方法进行代理增强,事务功能也就无法实现。
  9. 数据源或连接池配置问题

    • 数据源未配置事务支持,或者连接池不支持事务,也可能导致Spring事务失效。
  10. 自定义切面干扰

    • 当自定义的切面处理逻辑与Spring事务切面的逻辑发生冲突,如错误地处理了异常或改变了事务状态,可能会导致事务失效。

总之,Spring事务失效通常是由于配置、注解使用不当或对AOP代理机制理解不足所引起的,针对具体失效场景,应当仔细检查以上提及的各种原因并采取相应措施修复。

14、什么是Spring的三级缓存?如何解决循环依赖的问题的?

Spring 框架中的三级缓存是用来解决对象之间的循环依赖问题的,在Spring容器进行依赖注入的过程中发挥作用。以下是三级缓存的详细介绍以及它们如何协同工作来解决循环依赖问题:

  1. 一级缓存 (singletonObjects):

    • 这个是最常用的缓存,也被称作“单例池”。
    • 存储已经完成初始化并且可以对外暴露的单例Bean实例。
    • 当一个Bean的实例化、依赖注入及初始化全过程都完成后,它会被放入一级缓存中,后续请求同一Bean时,Spring容器直接从一级缓存中返回。
  2. 二级缓存 (earlySingletonObjects 或 singletonFactories):

    • 二级缓存用于存储尚未完成初始化(也就是属性尚未注入)但已经被实例化的Bean。
    • 当Spring检测到循环依赖且需要提前暴露Bean实例时,会将已实例化但尚未完成属性注入的Bean放入二级缓存中。
    • 对于早期暴露的Bean,二级缓存可能存储实际的半成品对象(earlySingletonObjects),也可能存储能够产生半成品对象的工厂方法(singletonFactories)。
  3. 三级缓存 (prototypeFactories 或 earlySingletonPrototypeHandlers):

    • 在某些特殊场景下,例如原型Bean间的循环依赖,可能需要用到三级缓存。
    • 对于原型Bean,由于它们每次都新建实例,所以三级缓存的作用方式与单例Bean不同,主要用于临时保存能快速生成新实例的工厂方法。

解决循环依赖的具体步骤如下:

  • 当Spring容器在实例化A时发现A依赖B,它首先尝试从一级缓存查找B是否存在,如果不存在,则继续查找二级缓存。
  • 如果在二级缓存中找到了B的早期实例(半成品),则可以直接注入到A中,从而打破了循环依赖。
  • 如果二级缓存中也没有B,则Spring开始实例化B,并在实例化B的过程中发现B又依赖A,这时,它将已经实例化但尚未完成初始化的A对象放入二级缓存。
  • B接着从二级缓存中拿到A的早期实例,完成自己的实例化和部分依赖注入。
  • 最后,当A完成了自身的初始化后,将完整实例放入一级缓存,B也可以顺利完成剩余的初始化过程,并最终进入一级缓存。

通过这样的策略,Spring确保了在单例Bean之间即使存在循环依赖也能安全、有序地进行依赖注入,避免了因循环依赖导致的死锁或其他运行时异常。对于原型Bean,虽然Spring默认不支持循环依赖,但在一些高级场景下可以通过自定义扩展解决。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1506236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt 中Json文件的操作

Json文件的读取 QFile file("data.json"); //准备好的文件file.open(QIODevice::ReadOnly|QIODevice::Text);QByteArray arr file.readAll();QJsonDocument jsonDoc QJsonDocument::fromJson(arr);QJsonObject jsonObj jsonDoc.object();qint32 id jsonObj["…

网络工程师笔记10 ( RIP / OSPF协议 )

RIP 学习路由信息的时候需要配认证 RIP规定超过15跳认定网络不可达 链路状态路由协议-OSPF 1. 产生lsa 2. 生成LSDB数据库 3. 进行spf算法,生成最有最短路径 4. 得出路由表

Python错题集-8:AttributeError(找不到对应的对象的属性)

1问题描述 AttributeError: AxesSubplot object has no attribute arc 2代码详情 import matplotlib.pyplot as plt# 创建一个新的图形和坐标轴 fig, ax plt.subplots()# 定义弧线的参数 center (0.5, 0.5) # 圆心坐标 (x, y) width 1.0 # 半径 height 0.5 # 半径 ang…

学习笔记。。。

1.字符串的拼接 1.sprintf() 往字符串的前面或中间、后面拼接一个字符串。 2.strncpy()用来复制字符串的前n个字符 //dest为目标数组,src为源数组,n为要复制的字符个数 2.char* My_strncpy(char* dest, const char* src, int n) 3.char *strcat(ch…

【Axure高保真原型】可视化动点素材

今天和粉丝们免费分享可视化动点素材的原型模板,该模板使用简单,复制粘贴,预览时即可实现动点效果,本案例提供红黄蓝绿4中颜色的动点,如果需要其他颜色,可以自行编辑svg里面的代码 【原型效果】 【模板下载…

Leetcode 59.螺旋矩阵Ⅱ

1.题目 2.思路 (借用代码随想录的图) 1.我们将转一圈看作一个循环(1->2->3->4->5->6->7->8 这是一个循环) 2.在这个循环里,我们要画四条边(上右下左) 填充上行从左到右 填…

[天天向上] 学习方法论-事半功倍的问题解决方法

目录 一、尝试独立解决问题1. 关于独立2. 像密室逃脱一样 二、提问的艺术1. 合适的自我介绍1.1 群名片2.2 研究方向/业务内容 2. 详细的问题描述2.1 问题描述要点2.2 描述格式2.3 问题内容描述,尤其是当前进展和问题 3. 如何让更多的人为你解答4. 如何结束提问更优雅…

ThreadLocal使用,配合拦截器HandlerInterceptor使用

ThreadLocal使用,配合拦截器HandlerInterceptor使用 ThreadLocal的使用场景通常涉及多线程环境下需要为每个线程保留独立状态的情况。它提供了一种简单的方式来管理线程本地变量,使得每个线程都可以独立地访问和修改自己的变量副本,而不会影…

《C语言文件操作》

目录 1. 文本数据和二进制 1.1 文本数据 1.2 二进制数据 1.3文本文件和二进制文件 2. 文件的打开和关闭 2.1 文件指针 2.2 打开文件 2.3 关闭文件 2.4 注意事项: 3. 文本文件的读写 3.1 向文件中写入数据 3.2 从文件中读取数据 3.3 注意事项 4. 二进制文件的读写…

如何对于单元格数据进行清洗处理

如何对于单元格数据进行清洗处理 陪伴意味着有人愿意把最美好的东西给你, 那就是时间。 当然陪伴也是一个很平常的事情, 日复一日,年复一年。 到最后陪伴就成了一种习惯。 约定好的相逢,伴你天荒地老! 陪伴是最长情的告…

Git分支管理(Git分支的原理、创建、切换、合并、删除分支)

系列文章目录 文章一:Git基本操作 文章目录 系列文章目录前言一、Git分支是什么二、Git分支的原理三、创建分支四、切换分支五、合并分支六、删除分支 前言 在上一篇文章中,我们学习了如何使用Git的一些基本操作,例如安装Git、创建本地仓库…

Unity使用Addressable热更新

先看热更新的gif: Addressable是Unity推出的打ab包方案。不需要手动写AB打包脚手架了,不需要关心依赖,这也简化了ab热更新的流程。Addressable打包需要先将资源放入group中,按group来打包,每个group对应一个ScriptableObject的配置…

Unity 关节:铰链、弹簧、固定、物理材质:摩檫力、 特效:拖尾、

组件-物理-关节:铰链(类似门轴) 自动动作、多少力可以将其断开、 弹簧可以连接另一个刚体(拖动即可) 固定一般是等待一个断裂力,造成四分五裂的效果。 物理材质 设置摩檫力,则可以创造冰面的…

【算法面试题】-04

执行时长 def min_execution_time(n, size, tasks):a 0ans sizei 0while i < size:tmp tasks[i]a tmpif a < n:a 0else:a - ni 1ans a // nif a % n ! 0:ans 1return ans# 读取输入 n int(input()) size int(input()) tasks list(map(int, input().split()))…

macOS14.4安装FFmpeg及编译FFmpeg源码

下载二进制及源码包 二进制 使用brew安装ffmpeg : brew install ffmpeg 成功更新到ffmpeg6.1 下载FFmpeg源码

SMART PLC自适应低通滤波器(收放卷线速度滤波)

一阶低通滤波器更多内容请参考信号处理专栏相关文章,常用链接如下: 1、SMART PLC 低通滤波器和模拟量采集应用 https://rxxw-control.blog.csdn.net/article/details/136595982https://rxxw-control.blog.csdn.net/article/details/1365959822、SMART PLC双线性变换和后向差…

实拆一个Philips剃须刀

拆卸难度很大&#xff0c;怪不得防水 FR&#xff1a;徐海涛(hunkXu)

【Prometheus】DataModel

数据模型 DataModel 指标 Metric metric 包含 metric name 和 metric label 格式&#xff1a; <metric name>{<label name><label value>, ...}例如&#xff1a;服务器 HTTP 接口 /messages 的总请求数 api_http_requests_total{method"POST",…

算法第二十六天-删除有序数组中的重复项Ⅱ

删除有序数组中的重复项 题目要求 解题思路 题目要求中提到原地修改&#xff0c;那么肯定需要一个指针指向当前即将放置元素的位置&#xff0c;需要另外一个指针向后遍历所有元素&#xff0c;所以[双指针]解法呼之欲出。 慢指针slow&#xff1a;指向当前元素放置的位置&…

旅游资源网站|基于SSM 框架+vue+ Mysql+Java+B/S架构技术的旅游资源网站设计与实现(可运行源码+数据库+设计文档+部署说明+视频演示)

目录 文末获取源码 前台首页功能 管理员功能 用户功能模块 数据库设计 系统结构设计 lunwen参考 概述 源码获取 文末获取源码 前台首页功能 旅游资源网站 &#xff0c;在系统首页可以查看首页、景点信息、酒店信息、客房信息、交流论坛、红色文化、个人中心、后台管理…