【金三银四】Spring面试题

目录

  • 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 {@Transactionalpublic 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.mzph.cn/news/736160.shtml

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

相关文章

割点原理及封装好的割点类

作者推荐 视频算法专题 预备知识 本分析针对:连通无向图G。 搜索树 节点的父子关系:任意 节点的邻接 节点除了已处理 节点,都是它的子 节点。 以任意一点为根开始DFS,计算所有 节点的父子关系。只保留个子 节点到父 节点形成…

Visual Studio单步调试中监视窗口变灰的问题

在vs调试中,写了这样一条语句 while((nfread(buf, sizeof(float), N, pf))>0) 然而,在调试中,只要一执行while这条语句,监视窗口中的变量全部变为灰色,不能查看,是程序本身并没有报错,能够继…

Python编程与人工智能应用 MOOC题目

第二次作业 1. 2. . 3.考察“字符串的这些api函数均是提供一个拷贝本”的知识点。 4.这边的2别忘,前闭后开区间,否则对于121这样会认为是质数(11*11)

VMware 集群-虚拟机配置反亲和性(互斥)

简介 博客:https://songxwn.com/ 为实现应用系统的冗余,经常会双机或者多机部署(如数据库集群等)。在VMware 集群里面,要保证不同应用集群的节点虚拟机在不同的物理宿主机上,防止单个宿主机故障&#xff…

开发指南004-@Query参数写法

JPA的Query注解和函数参数的绑定有多种写法,总结如下: 1、使用:形参名 2、使用?数值,数值表示形参位置,1表示第一个形参,依次类推 3、使用Param("参数名"):参数名 4、获取实体类名称,使用#{#entityName}

在高并发、高性能、高可用 三高项目中如何设计适合实际业务场景的分布式id(一)

分布式ID组件:黄金链路上的关键基石 在现代分布式系统中,分布式ID组件无疑扮演着至关重要的角色。作为整个系统的黄金链路上的关键组件,它的稳定性和可靠性直接关乎到整个系统的正常运作。一旦分布式ID组件出现问题,黄金链路上的…

HTML静态网页成品作业(HTML+CSS)——阜阳剪纸介绍设计制作(1个页面)

🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有1个页面。 二、作品演示 三、代…

对NENU研究生教务系统网站做自动化测试

由上一篇博客可知(解决NENU研究生教务系统网站“不能创建对象”等兼容性问题-CSDN博客),我通过Tampermonkey脚本解决了NENU研究生教务系统网站在非IE内核浏览器中访问时“不能创建对象”的问题。 在解决问题的过程中,为了Tampermo…

外卖平台订餐流程架构的实践

当我们想要在外卖平台上订餐时,背后其实涉及到复杂的技术架构和流程设计。本文将就外卖平台订餐流程的架构进行介绍,并探讨其中涉及的关键技术和流程。 ## 第一步:用户端体验 用户通过手机应用或网页访问外卖平台,浏览菜单、选择…

010Editor汉化版+下载+注册码+模板bug

项目场景: 这天我想使用我的不知名的一个破解版本的010Edit来查看一个EXE程序,并想使用模板功能,但是发现没有该模板还无法下载最新模板 问题描述 010Edit联网后需要注册码: 010 Editor 激活码生成器 使用方法 参照教程使用0…

GitHub Desktop的常用操作【图形化】

文章目录 【1】仓库的创建和删除【2】文件操作【3】分支原理与分支操作1.分支创建2.分支合并 【4】标签 【1】仓库的创建和删除 在本地创建一个新的仓库: 然后输入仓库的名称,描述,并选择路径: 点击完后就发现我们的仓库创建好…

RocketMQ存储设计深度解析

引言 在分布式系统中,消息中间件扮演着至关重要的角色,它负责系统间异步消息的传递,确保信息可靠传输。Apache RocketMQ(以下简称RocketMQ)是这一领域中的一个优秀代表。RocketMQ以其高性能、高可靠性和高扩展性赢得了…

图片表格特征不明显怎么转成结构化excel?如何定制最划算?

金鸣定制识别是一种基于OCR(光学字符识别)技术的先进解决方案,旨在为用户提供高效、准确的数据抽取和识别服务。该服务允许用户根据自身的需求,提交样本图片,利用金鸣识别现有的OCR模型进行训练,从而有效降…

【Flutter 面试题】dart是值传递还是引用传递?

【Flutter 面试题】dart是值传递还是引用传递? 文章目录 写在前面解答补充说明值传递示例引用传递示例总结 写在前面 关于我 ,小雨青年 👉 CSDN博客专家,GitChat专栏作者,阿里云社区专家博主,51CTO专家博主…

【蓝桥杯】k倍区间

一.题目描述 二.问题分析 对于该问题,标签上写的是暴力,但是如果使用暴力的话,会超时。 首先,对于两个数a,b(假设a小于b),若a与b对k取余后结果相同,则b-a可以整除k。 …

计算机网络—OSPF单区域配置

目录 目录 1.实验环境准备 2.配置 OSPF 3.验证 OSPF 配置 4.修改 OSPF hello 和 dead 时间参数 5.OSPF缺省路由发布及验证 6.控制 OSPF DR/BDR 的选举 7.配置文件 拓扑图&#xff1a; 1.实验环境准备 基本配置以及IP编址。 <Huawei>system-view Enter system vi…

strlen和sizeof的应用与区别

sizeof和strlen作为都能求大小的工具两者之间有何不同, strlen: 1. strlrn计算的是什么的大小 strlen计算的是字符串长度的大小&#xff0c;所以strlen在计算字符串长度时会一直顺着字符串的元素一个一个的查找&#xff0c;一直到查询到了/0才会停止 2.strlen属于库函数&am…

easyexcel文件上传

easyexcel文件上传 前言&#xff1a;功能开发中&#xff0c;难免碰到数据上传下载功能&#xff0c;excel上传常见用于报表上传&#xff0c;绩效上传&#xff0c;考勤上传… 使用步骤&#xff1a; 1&#xff0c;编写业务层&#xff1a; 1&#xff0c;添加easyexcel依赖 <…

[Java安全入门]三.URLDNS链

一.前言 在初步学习java的序列化和反序列化之后&#xff0c;这里学习java反序列化漏洞的一个利用链&#xff0c;也是比较基础的一条链。 由于URLDNS不需要依赖第三方的包&#xff0c;同时不限制jdk的版本&#xff0c;所以通常用于检测反序列化的点。 二.代码展开分析 构造链 …

Spring AOP 原理

&#x1f496; AOP 介绍 AOP&#xff0c;也就是 Aspect-oriented Programming&#xff0c;译为面向切面编程。 简单点说&#xff0c;就是把一些业务逻辑中的相同代码抽取到一个独立的模块中&#xff0c;让业务逻辑更加清爽。 举个例子&#xff0c;假如我们现在需要在业务代码…