目录
- 前言
- 1. 问题所示
- 2. 原理分析
- 3. 基本知识
- 4. @Lazy注解
前言
通过实战更好的回馈问题,意识更加深刻
起因是我出现如下问题之后,才意识到中了Spring的循环依赖了!
1. 问题所示
在执行项目的时候,出现如下问题,问题如下所示:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'tyreRepareOrderController' defined in file [F:\java_project\xx\xx-service\xx-equipment\target\classes\org\xx\equipment\controller\TyreRepareOrderController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'tyreRepareOrderServiceImpl': Bean with name 'tyreRepareOrderServiceImpl' has been injected into other beans [tyreRepareOrderServiceImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1338)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
截图如下所示:
2. 原理分析
这是Spring循环依赖的经典问题了
原本我是ServiceImpl类中实现了Service中的方法!
之后又在ServiceImpl类 通过 注入 Service方法来操作,说的普通一点就是死锁了!
解决方案就是要么使用Spring中的三级缓存(这是面试的经典题)
或者使用this.super直接使用,从而不用再次注入,而又能调用其方法!
3. 基本知识
Spring循环依赖是指两个或多个Bean彼此依赖,形成一个循环引用的情况。这种情况可能导致应用程序无法正常启动或运行,因此需要特殊处理。
在Spring中,循环依赖的处理是通过使用三级缓存解决的。三级缓存是指Spring容器中的三个缓存区域,分别是singletonObjects、earlySingletonObjects和singletonFactories。在处理循环依赖时,Spring会通过提前暴露半成品Bean的方式来解决。
具体来说,当一个Bean被创建时,Spring会将其提前暴露给singletonFactories缓存,即使它还没有完全初始化。这样,其他Bean在需要引用这个Bean时,可以通过singletonFactories获取到它的引用,而不是等待其完全初始化。当Bean初始化完成后,会将其放入singletonObjects缓存,表示完全初始化的Bean。
这样,循环依赖中的Bean就能够在初始化过程中相互引用,而不会导致死循环或空指针异常。
需要注意的是,Spring的循环依赖处理机制对于单例(singleton)的Bean是有效的,对于其他作用域的Bean(如prototype)可能会有不同的处理方式。
下面是一个简单的Java代码示例,演示了Spring中的循环依赖情况:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class A {@Autowiredprivate B b;// Other code...
}@Component
public class B {@Autowiredprivate A a;// Other code...
}
在上述例子中,类A依赖类B,而类B又依赖类A,形成了循环依赖。
Spring会通过三级缓存解决这种循环依赖。当一个Bean被创建时,它会被提前暴露给singletonFactories缓存,以便其他Bean可以引用它。下面是如何解除循环依赖的代码解释:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;@Component
public class A {@Autowired@Lazyprivate B b;// Other code...
}@Component
public class B {@Autowired@Lazyprivate A a;// Other code...
}
在解除循环依赖时,可以使用@Lazy
注解标记依赖关系,表示在需要时才进行实际的Bean初始化。
这样,当Bean A和Bean B相互引用时,它们的初始化将被延迟到第一次实际使用时。这有助于打破循环依赖,因为Bean的初始化被推迟到真正需要的时候。
4. @Lazy注解
@Lazy
注解是Spring框架提供的一种机制,用于延迟加载(Lazy Initialization)Bean。
当一个Bean被标记为@Lazy
时,它的初始化将会被推迟到第一次被实际使用的时候,而不是在容器启动时立即初始化。
延迟加载的一些补充:
优点 | 作用范围 | 使用场景 |
---|---|---|
1.性能优化: 避免在应用启动时就初始化所有Bean,节省了启动时间和资源。 2.解决循环依赖: 通过延迟加载,可以解决某些情况下的循环依赖问题,如前面提到的循环依赖的处理。 | @Lazy 注解可以用于标记在字段、方法、构造函数或配置类(@Configuration )的@Bean 方法上。 | 适用于那些在启动时不一定会被立即使用的Bean,特别是当Bean的初始化过程较为耗时或涉及到一些外部资源。 |
与单例(Singleton)的关系: 默认情况下,Spring容器中的Bean是单例的。
使用@Lazy
注解可以将单例Bean的初始化延迟到第一次使用时。
XML配置中的等效方式: 在XML配置中,可以使用lazy-init="true"
属性来实现与@Lazy
注解相同的效果。
<bean id="myBean" class="com.example.MyBean" lazy-init="true"><!-- Bean configuration -->
</bean>
总体而言,@Lazy
注解是Spring框架提供的一个有用的特性,可用于优化应用程序的启动性能,尤其是在处理大型应用或有复杂依赖关系的情况下。