spring常见面试题

一、*介绍一下spring框架?

Spring框架是一个开源的Java/Java EE全功能栈(full-stack)的应用程序框架,旨在简化企业级应用程序的开发。通过IoC和DI,Spring降低了组件之间的耦合度,使得应用程序更加灵活和易于维护。面向切面编程(AOP)使得日志、事务管理等横切关注点可以从业务逻辑中分离出来,提高了代码的可读性和可维护性。

Spring框架的主要模块

Spring框架由多个模块组成,每个模块都针对软件开发的不同方面提供了解决方案。以下是一些核心的Spring模块:

  1. Spring Core

:提供了框架的基本组成部分,包括IoC(控制反转)和DI(依赖注入)功能。IoC和DI是Spring框架的核心特性,它们允许你将对象之间的依赖关系从代码中解耦出来,并通过配置文件或注解进行管理。

  1. Spring Beans

:在Spring Core的基础上,提供了更高级的企业级服务,如Bean的创建、配置和管理。

  1. Spring Context

:建立在Spring Core和Spring Beans的基础上,提供了一个应用程序上下文(ApplicationContext),这是一个配置文件信息的抽象表示,允许你在整个应用程序中共享配置信息。

  1. Spring Expression Language (SpEL)

:一个强大的表达式语言,用于在运行时查询和操作对象图。它可以被用于Bean定义中以及XML和注解驱动的配置中。

  1. Spring AOP(面向切面编程)

:提供了一种将横切关注点(如日志、事务管理等)与业务逻辑分离的方法,从而使代码更加清晰和模块化。

  1. Spring JDBC

:提供了JDBC(Java数据库连接)的抽象层,简化了数据库访问代码的编写,同时支持声明式事务管理。

  1. Spring ORM

:为使用ORM(对象关系映射)技术如Hibernate、JPA等提供了集成层,简化了数据持久化操作。

  1. Spring Web

:提供了基础的Web开发支持,包括文件上传、多部分请求处理等。

  1. Spring MVC

:Spring的Web MVC框架,它构建在Spring Web模块之上,提供了一种分离关注点的方式,使得开发Web应用程序变得更加简单。

  1. Spring Test

:提供了对Spring应用程序进行测试的支持,包括单元测试和集成测试。

二、*Spring AOP和AspectJ AOP有什么区别?

2.1、Spring AOP

Spring 在运行期会为目标对象生成一个动态代理对象,并在代理对象中实现对目标对象的增强。Spring AOP 的底层是通过以下 2 种动态代理机制,为目标对象(Target Bean)执行横向织入的。

Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现方式,但它们在实现方式、灵活性、依赖性和应用场景等方面存在一些显著的区别。以下是详细的对比分析:

2.2、实现方式

  • Spring AOP:
    • 基于代理:Spring AOP使用动态代理技术(包括JDK动态代理和CGLIB)在运行时生成代理对象,并将切面逻辑织入到代理对象中。
    • 代理机制:对于基于接口的目标类,Spring使用JDK动态代理;对于没有实现接口的目标类,Spring使用CGLIB生成子类来实现代理。
  • AspectJ AOP:
    • 基于字节码增强:AspectJ AOP在编译期或类加载期通过在目标类中直接插入字节码来实现切面逻辑的织入,不依赖于代理。
    • 灵活性:AspectJ提供了更强大和灵活的AOP功能,允许进行更细粒度的控制和切入。
  • 2.3、灵活性和依赖性

  • Spring AOP:
    • 依赖Spring:Spring AOP对Spring管理的Bean生效,基于代理的方式,支持更灵活的切点表达式。但由于是基于代理,它只能作用于Spring Bean的方法调用,无法应用于非Spring Bean的方法。
    • 集成性:与Spring框架有较强的集成,依赖于Spring的容器和代理机制,在Spring项目中更容易集成和使用。
  • AspectJ AOP:
    • 不依赖Spring:AspectJ AOP作为独立的框架,不依赖于Spring,可以在任何Java项目中使用。
    • 广泛应用:它可以对任何Java对象生效,不仅限于Spring管理的Bean,可以作用于所有的对象。
    • 额外配置:需要在编译时或类加载时进行织入,可能需要额外的配置和依赖。

2.4、性能

  • Spring AOP:
    • 动态代理的性能:由于采用动态代理,对目标类的影响相对较小,因此性能相对较高。但对于大规模或频繁的切面逻辑,可能会有一些性能损耗。
  • AspectJ AOP:
    • 字节码增强的性能:通过编译时或类加载时织入,虽然对字节码进行了修改和增强,可能对目标类的影响更大,但在某些场景下可能性能更高。然而,这也取决于具体的实现和配置。

2.5、应用场景

  • Spring AOP:
    • 轻量级AOP需求:适用于轻量级的AOP需求,如日志记录、事务管理等,对Spring框架集成友好。
  • AspectJ AOP:
    • 复杂AOP需求:适用于复杂的AOP需求,如系统架构中需要对各个层次的组件进行切面,或者对非Spring Bean的对象进行切面等。

综上所述,选择Spring AOP还是AspectJ AOP取决于具体的项目需求和场景。如果需要更强大和灵活的AOP功能,并且可以接受一些额外的配置和依赖,那么AspectJ AOP可能更为适合。而对于大多数Spring项目来说,Spring AOP是一个常用且足够的选择。

三、*如何正解Spring IOC

对象的作用域scope

lazy_init默认值是false,在spring容器启动时创建对象,并且只在scope=singleteon时有效;

@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

四、***Spring中的bean的作用域有哪些?Spring中的单例bean的线程安全吗?

4.1、spring的bean作用域

singleton:单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;将一直存活到容器退出,也就是说,它与IoC容器“几乎”拥有相同的“寿命”。Spring默认bean的模式是单例模式,@Controller也是bean的一种,也是单例模式;

  • 为什么spring bean要默认是单例呢?

1、减少对象创建开销,节省资源,提高性能。

2、简化依赖关系:在Spring框架中,Bean之间可能存在复杂的依赖关系。如果每次都创建新的Bean实例,可能会产生相互依赖的Bean获取不同实例的问题,从而破坏依赖关系。单例模式可以确保依赖关系的稳定性和一致性。

3、保证全局状态一致性,易于维护和测试:单例模式可以确保全局共享状态的一致性,避免了资源竞争和线程安全性问题。这有助于简化应用的维护和测试工作。

单例模式在多线程环境下存在线程安全问题!!!

prototype:原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;

容器在接到该类型对象的请求的时候,会每次都重新生成一个新的实例对象给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继的声明周期的管理工作,例如该对象的销毁。也就是说,容器每次返回给请求方一个新的实例对象后,就任由这个对象“自生自灭”了。

对于那些请求方不能共享使用的对象类型,应该将其bean定义的scope设置为prototype。这样,每个请求方可以得到自己专有的一个对象实例。通常,声明为prototype的对象都是一些有状态的,比如保存每个顾客信息的对象。

prototype多例模式时

1、容器启动时并没有初始化Controller变量,而是在每次调用时初始化一个新的Controller实例

2、多例模式下,静态变量多实例共享,非静态变量不共享。(本质上还是不同的对象)

注:Java中多个实例的static变量会共享同一块内存区域,也就是多个对象共享一个类的同一个静态成员变量。应为static是在类加载的时候初始化到方法区的,所有的实例会共享这块区域。

====下面是在web项目下才用到的===

request:搞web的大家都应该明白request的域了吧,就是每次请求都新产生一个实例,和prototype不同就是创建后,接下来的管理,spring依然在监听。

session:每次会话,同上。

global session:全局的web域,类似于servlet中的application

好了,上面都说了spring的controller默认是单例,那很自然就是singleton了。

4.2、单例的Controller,类中的非静态变量如何保证是线程安全的

@RestController@Scope(value = "prototype") @RequestMapping("/test/") public class TestController { private int num = 0; @GetMapping("/show") public Integer show() { // 添加@Scope(value = "prototype")每次打印结果都会加1 // 不添加@Scope(value = "prototype")每次打印结果都加1 System.out.println(num); return num; } @GetMapping("/add") public Integer add() { num ++; return show(); } }

最佳实践:

1、不要在controller中定义成员变量。使用局部变量,因为局部变量在每个线程中都有各自的实例,从而保证线程安全。

2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope("prototype"),将其设置为多例模式!或者使用ThreadLocal 保存每个线程自已的变量副本

4.3、单例模式如何保证并发

Spring 中controller、service、dao都为单例模式(注:默认状态)。既然是单例,那么在并发的情况下,会发生什么?

1)使用ThreadLocal保存每个线程自己的变量;

2)加锁;

==================================================

java 里,每个线程都有自己独享的空间,也就是栈内存。线程在调用方法的时候,会创建一个栈帧。也就是说调用一个方法的时候,也就是一个栈帧的入栈过程,该方法执行完毕,栈帧也就出栈了。换句话讲,成员方法对于每个线程事实上是私有的,而不是你表面看上去的那样是 "共享" 的。

那么为什么成员变量会出问题呢?

==================================================

如你所知道的,每个新建对象都存放在堆中,每个持有该对象引用的线程,都可以访问到它(只要你有那个权限)。这也就是说,成员变量对于每个线程,事实上是共享的。

五、***Spring中的bean生命周期?

Spring 中 Bean 的生命周期较复杂,大致可以分为以下 5 个阶段:

  1. Bean 的实例化
  2. Bean 属性赋值
  3. Bean 的初始化
  4. Bean 的使用
  5. Bean 的销毁

Spring 根据 Bean 的作用域来选择 Bean 的管理方式:

  • 对于 singleton 作用域的 Bean 来说,Spring IoC 容器能够精确地控制 Bean 何时被创建、何时初始化完成以及何时被销毁;
  • 对于 prototype 作用域的 Bean 来说,Spring IoC 容器只负责创建,然后就将 Bean 的实例交给客户端代码管理,Spring IoC 容器将不再跟踪其生命周期。

Bean 生命周期的整个执行过程描述如下:

  1. Spring 启动,查找并加载需要被 Spring 管理的 Bean,对 Bean 进行实例化。Bean容器利用Java Reflection API创建一个Bean的实例
  2. 对 Bean 进行属性注入。
  3. 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
  4. 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
  5. 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
  6. 如果 Bean 实现了 BeanPostProcessor 接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
  7. 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。
  8. 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
  9. 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
  10. 如果在 中指定了该 Bean 的作用域为 singleton,则将该 Bean 放入 Spring IoC 的缓存池中,触发 Spring 对该 Bean 的生命周期管理;如果在 中指定了该 Bean 的作用域为 prototype,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。
  11. 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法销毁 Bean;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

5.1、自定义 Bean 的生命周期

我们可以在 Spring Bean 生命周期的某个特定时刻,指定一些生命周期回调方法完成一些自定义的操作,对 Bean 的生命周期进行管理。Bean 的生命周期回调方法主要有两种:

  • 初始化回调方法:在 Spring Bean 被初始化后调用,执行一些自定义的回调操作。
  • 销毁回调方法:在 Spring Bean 被销毁前调用,执行一些自定义的回调操作。我们可以通过以下 3 种方式自定义 Bean 的生命周期回调方法:
  1. 通过接口实现

通过实现 InitializingBean 和 DisposableBean 接口,指定 Bean 的生命周期回调方法

  1. 使用注解实现

我们还可以通过 JSR-250 的 @PostConstruct 和 @PreDestroy 注解,指定 Bean 的生命周期回调方法。

5.2、Spring后置处理器(BeanPostProcessor)

通过该接口可以自定义调用初始化前后执行的操作方法。

该接口中包含了两个方法:

  • postProcessBeforeInitialization() 方法:在 Bean 实例化、属性注入后,初始化前调用。
  • postProcessAfterInitialization() 方法:在 Bean 实例化、属性注入、初始化都完成后调用。

当需要添加多个后置处理器实现类时,默认情况下 Spring 容器会根据后置处理器的定义顺序来依次调用。也可以通过实现 Ordered 接口的 getOrder 方法指定后置处理器的执行顺序。该方法返回值为整数,默认值为 0,取值越大优先级越低。

六、Spring 自动装配

Spring 共提供了 5 中自动装配规则,它们分别与 autowire 属性的 5 个取值对应,具体说明如下表。

七、Spring框架中用到了哪些设计模式

1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。

2.代理设计模式:Spring AOP功能的实现。

3.单例设计模式:Spring中的bean默认都是单例的。

4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。

5.包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。

7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。

八、@Component和@Bean的区别是什么

@Component通常用于类级别的注解,通过@Component,Spring会自动创建这些类的实例对象,并将其注入到Spring容器中。

@Bean主要用于方法级别的注解,它允许开发者自定义Bean的创建和初始化过程,包括指定Bean的名称、作用域、依赖关系等。

@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解来实现。

九、将一个类声明为Spring的bean的注解有哪些?

9.1、@Autowired与@Resources的区别?

@Autowired(required = true):byType

@Resource(name = "",type = "")

9.3、Spring的bean的注解

我们一般使用@Autowired注解去自动装配bean。而想要把一个类标识为可以用@Autowired注解自动装配的bean,可以采用以下的注解实现:

1.@Component注解。通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪一个层,可以使用@Component注解标注。

2.@Repository注解。对应持久层,即Dao层,主要用于数据库相关操作。

3.@Service注解。对应服务层,即Service层,主要涉及一些复杂的逻辑,需要用到Dao层(注入)。

4.@Controller注解。对应Spring MVC的控制层,即Controller层,主要用于接受用户请求并调用Service层的方法返回数据给前端页面。

十、***Spring事务

10.1、Spring事务管理的方式有几种?

1.编程式事务:在代码中硬编码(不推荐使用)。

2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。

10.2、Spring事务中的隔离级别有哪几种?

在TransactionDefinition接口中定义了五个表示隔离级别的常量:

ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。

ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

如果数据库的配置与spring中配置的隔离级别不一致时,以spring的配置为准!

10.3、Spring事务中有哪几种事务传播行为?

在TransactionDefinition接口中定义了七个表示事务传播行为的常量。

支持当前事务的情况:

PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。Support a current transaction, create a new one if none exists.This is the default setting of a transaction annotation。

PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。Support a current transaction, execute non-transactionally if none exists.

PROPAGATION_MANDATORY(强制性的): 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。Support a current transaction, throw an exception if none exists.

不支持当前事务的情况:

PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。Create a new transaction, and suspend the current transaction if one exists.

PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。Execute non-transactionally, suspend the current transaction if one exists.

PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。Execute non-transactionally, throw an exception if a transaction exists.

其他情况:

PROPAGATION_NESTED(嵌套): 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else.

10.4、spring事务失效的几种情况

1、方法自调用

声明式事务基于Spring AOP技术,Spring AOP的实现是靠(jdk实现接口/cglib继承类)动态代理的方式。

所以如果一个类的methodA调用本类中另一个方法methodB,实际上是这个类对自己的调用,并没有通过Spring容器去请求代理类,因此Spring AOP不起作用。

2、数据库不支持事务

如果使用mysql,而恰巧又使用MyISAM作为数据库Engine。则无论是编程式事务还是声明式事务都不会生效。因此,一定要确保数据库Engine支持事务。

3、事务传播机制配置错吴;

4、方法的访问修饰符不是public

5、异步方法

6、异常没有抛出或抛出的异常与捕获的异常类型不一致

@Transaction注解中的属性rollback定义了出现什么异常时,事务回滚。但是如果没有将异常抛出,做了try/catch处理,则Spring AOP无法感知,此时事务会失效。

@Transaction注解默认的rollback属性为RuntimeException.class,如果业务流程中定义的异常类型不是RuntimeException或者其子类,则事务也不会生效。

因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用rollbackFor属性明确指定异常。

7、bean没有被spring容器管理。

十一、***Spring是如何解决解决循环依赖的问题

spring循环依赖及解决方法_spring循环依赖及解决方式-CSDN博客

1、什么是循环依赖

1)A -> B,B -> A;:互相依赖

2)A -> B - > C -> A :三者依赖

3)A -> A :自我依赖

2、spring的解决方案

答:spring利用三级缓存机制来解决循环依赖问题;Spring内部维护了三个Map,也就是我们通常说的三级缓存。

在Spring的DefaultSingletonBeanRegistry类中,你会赫然发现类上方挂着这三个Map:

/** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

singletonObjects (一级缓存)它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方。

earlySingletonObjects(二级缓存)映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.

singletonFactories(三级缓存) 映射创建Bean的原始工厂

getBean(beanName)时,spring先从一级缓存中找,若一级缓存中没有;则从二级缓存中找;若一二级缓存中都没有,则证明该 bean还没有实例化。

1)一级缓存:存放所有成熟的bean;

2)二级缓存:存放所有早期bean,

3)三缓缓存:存储代理bean

后两个Map其实是“垫脚石”级别的,只是创建Bean的时候,用来借助了一下,创建完成就清掉了。那么Spring 是如何通过上面介绍的三级缓存来解决循环依赖的呢?这里只用 A,B 形成的循环依赖来举例:

实例化 A,此时 A 还未完成属性填充和初始化方法(@PostConstruct)的执行,A 只是一个半成品。

为 A 创建一个 Bean工厂,并放入到 singletonFactories 中。

发现 A 需要注入 B 对象,但是一级、二级、三级缓存均为发现对象 B。

实例化 B,此时 B 还未完成属性填充和初始化方法(@PostConstruct)的执行,B 只是一个半成品。

为 B 创建一个 Bean工厂,并放入到 singletonFactories 中。

发现 B 需要注入 A 对象,此时在一级、二级未发现对象

A,但是在三级缓存中发现了对象 A,从三级缓存中得到对象 A,并将对象 A 放入二级缓存中,同时删除三级缓存中的对象 A。(注意,此时的 A

还是一个半成品,并没有完成属性填充和执行初始化方法)

将对象 A 注入到对象 B 中。

对象 B 完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 B。(此时对象 B 已经是一个成品)

对象 A 得到对象B,将对象 B 注入到对象 A 中。(对象 A 得到的是一个完整的对象 B)

对象 A完成属性填充,执行初始化方法,并放入到一级缓存中,同时删除二级缓存中的对象 A。

3.无法解决的循环依赖问题

Spring 无法解决的循环依赖场景通常发生在以下几种情况:

  1. 构造器注入造成的循环依赖:Spring 无法解决构造器注入导致的循环依赖,因为对象创建过程中需要先知道依赖,而依赖又依赖于当前创建的对象。
  2. 方法注入造成的循环依赖:即使是方法注入(也就是常说的setter注入),如果注入的方法在构造对象的过程中被调用,也会导致循环依赖。
  3. 原型模式(Prototype Scope)下的循环依赖:Spring 默认情况下不支持原型模式下的循环依赖解决,因为它会在初次创建实例后缓存它,后续请求都返回这个实例,导致无法每次都返回一个新的对象。

解决方法:

  1. 将相互依赖的Bean中的某一个或几个Bean的作用域改为prototype,这样每次请求该Bean时,Spring都会创建一个新的实例。
  2. 使用@Lazy注解延迟加载,在需要的时候才创建对象。
  3. 使用@Autowired注解的required属性设置为false,允许不立即注入依赖。
  4. 使用@Bean注解的dependsOn属性,明确指定初始化顺序,避免循环依赖。
  5. 重构代码,尽量减少不必要的循环依赖。

更新实战知识分享:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值