200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > Spring注入方式及解决循环依赖

Spring注入方式及解决循环依赖

时间:2024-03-25 21:26:04

相关推荐

Spring注入方式及解决循环依赖

依赖注入的方式:

注解注入:@Autowire和@Resource:这种注解可以直接解决循环依赖问题,不需要额外处理构造方法器注入:构造方法注入需要使用@Lazy 注解来作用于循环依赖的属性setter注入:setter注入也可以直接解决循环依赖问题,不需要额外处理

注解注入:

@Autowrite是spring提供的注解;默认按照byType的方式进行注入,默认情况下要求依赖对象必须存在,可以设置它的required属性为false,如果想使用byName方式进行注解,可以配合@qualifier注解一起使用

@Autowiredprivate CycleBService cycleBService;@Autowired@Qualifier("cycleBServiceImpl")private CycleBService cycleBService;

@Resource默认按照ByName自动注入,由J2EE提供,@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。

所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

@Resource(name = "cycleBServiceImpl")private CycleBService cycleBService;@Resource(type = CycleBServiceImpl.class)private CycleBService cycleBService;

构造方法注入:

构造方法主要到底要不要加@Autowired?

@Autowired并不是必须的,不加也能注入成功,因为这个类会通过@RestController或者@Service等注解交给spring管理,这里不用加@Autowired也能成功的原因是,spring会对管理的bean的非默认构造函数的入参,尽量的匹配实例,进行实例化。这里还是建议加上@Autowired

private final CycleBService cycleBService;@Autowiredpublic CycleAServiceImpl(CycleBService cycleBService) {this.cycleBService = cycleBService;}

setter注入:

构造器注入参数太多了,显得很笨重,另外setter的方式能用让类在之后重新配置或者重新注入。

private CycleBService cycleBService;@Autowiredpublic void setter(CycleBService cycleBService){this.cycleBService=cycleBService;}

循环依赖问题:

@Servicepublic class CycleAServiceImpl implements CycleAService {private CycleBService cycleBService;@Autowiredpublic CycleAServiceImpl(CycleBService cycleBService) {this.cycleBService = cycleBService;}@Overridepublic void printA() {cycleBService.sayHello("我是A");}@Overridepublic void sayHello(String a) {System.out.println("hello:" + a);}}

@Servicepublic class CycleBServiceImpl implements CycleBService {private final CycleAService cycleAService;@Autowiredpublic CycleBServiceImpl(CycleAService cycleAService) {this.cycleAService = cycleAService;}@Overridepublic void printB() {System.out.println("我是B");cycleAService.printA();}@Overridepublic void sayHello(String a) {System.out.println("hello:" + a);}}

上图中 CycleAService 依赖 CycleBService ,CycleBService 依赖 CycleAService ,简单来说就是A里面依赖B,B里面依赖A,所以在初始化spring bean的时候回抛出循环依赖的异常(仅限于构造器注入的方式,@Autowired不会),可使用@Lazy 注解作用于CycleAService 上

private final CycleAService cycleAService;@Autowiredpublic CycleBServiceImpl(@Lazy CycleAService cycleAService) {this.cycleAService = cycleAService;}

这样就能解决循环依赖问题。那spring是怎么解决循环依赖呢,答案是三级缓存。

spring创建流程:

对Bean 的创建核心三个方法解释:

createBeanInstance:例化,其实也就是调用对象的构造方法实例化对象populateBean:填充属性,这一步主要是对bean的依赖属性进行注入(@Autowired)initializeBean:回到一些形如initMethod、InitializingBean等方法

从单例Bean的初始化来看,循环依赖发生在第二步,也就是填充属性的一步。

Spring容器的“三级缓存”

在Spring容器的整个生命周期中,只有Scope为singleton(单例)才会注入到spring工厂中,由于单例Bean只有一个对象,于是可以使用缓存来管理它。

实际上,spring运用了大量的cache手段,来在循环依赖问题的解决过程中甚至不惜使用了“三级缓存”,这也便是它设计的精妙之处~

三级缓存其实它更像是Spring容器工厂的内的术语,采用三级缓存模式来解决循环依赖问题,这三级缓存分别指:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {...// 从上至下 分表代表这“三级缓存”private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存.../** Names of beans that are currently in creation. */// 这个缓存也十分重要:它表示bean创建过程中都会在里面呆着~// 它在Bean开始创建时放值,创建完成时会将其移出~private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));/** Names of beans that have already been created at least once. */// 当这个Bean被创建完成后,会标记为这个 注意:这里是set集合 不会重复// 至少被创建了一次的 都会放进这里~~~~private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));}

注:AbstractBeanFactory继承自DefaultSingletonBeanRegistry

singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

获取单例Bean源码:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {...@Override@Nullablepublic Object getSingleton(String beanName) {return getSingleton(beanName, true);}@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;}...public boolean isSingletonCurrentlyInCreation(String beanName) {return this.singletonsCurrentlyInCreation.contains(beanName);}protected boolean isActuallyInCreation(String beanName) {return isSingletonCurrentlyInCreation(beanName);}...}

先从一级缓存singletonObjects中去获取。(如果获取到就直接return)如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。(如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存)

加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

getSingleton()从缓存里获取单例对象步骤分析可知,Spring解决循环依赖的诀窍:就在于singletonFactories这个三级缓存。这个Cache里面都是ObjectFactory,它是解决问题的关键。

// 它可以将创建对象的步骤封装到ObjectFactory中 交给自定义的Scope来选择是否需要创建对象来灵活的实现scope。 具体参见Scope接口@FunctionalInterfacepublic interface ObjectFactory<T> {T getObject() throws BeansException;

经过ObjectFactory.getObject()后,此时放进了二级缓存earlySingletonObjects内。这个时候对象已经实例化了,虽然还不完美,但是对象的引用已经可以被其它引用了。

此处说一下二级缓存earlySingletonObjects它里面的数据什么时候添加什么移除???

添加:向里面添加数据只有一个地方,就是上面说的getSingleton()里从三级缓存里挪过来

移除:addSingleton、addSingletonFactory、removeSingleton从语义中可以看出添加单例、添加单例工厂ObjectFactory的时候都会删除二级缓存里面对应的缓存值,是互斥的。

总结

依旧以上面A、B类使用属性field注入循环依赖的例子为例,对整个流程做文字步骤总结如下:

使用context.getBean(A.class),旨在获取容器内的单例A(若A不存在,就会走A这个Bean的创建流程),显然初次获取A是不存在的,因此走A的创建之路~实例化A(注意此处仅仅是实例化),并将它提前放进三级缓存中(此时A已经实例化完成,已经可以被引用了)初始化A:@Autowired依赖注入B(此时需要去容器内获取B)为了完成依赖注入B,会通过getBean(B)去容器内找B。但此时B在容器内不存在,就走向B的创建之路~实例化B,并将其放入三级缓存中。(此时B也能够被引用了)初始化B,@Autowired依赖注入A(此时需要去容器内获取A)此处重要:初始化B时会调用getBean(A)去容器内找到A,从一级缓存->二级缓存->三级缓存,一级一级获取,上面我们已经说过了此时候因为A已经实例化完成了并且放进了三级缓存里,所以这个时候去看缓存里是已经存在A的引用了的,所以getBean(A)能够正常返回B初始化成功(此时已经注入A成功了,已成功持有A的引用了),return(注意此处return相当于是返回最上面的getBean(B)这句代码,回到了初始化A的流程中~)。因为B实例已经成功返回了,因此最终A也初始化成功到此,B持有的已经是初始化完成的A,A持有的也是初始化完成的B,完美~

站的角度高一点,宏观上看Spring处理循环依赖的整个流程就是如此。希望这个宏观层面的总结能更加有助于小伙伴们对Spring解决循环依赖的原理的了解,同时也顺便能解释为何构造器循环依赖就不好使的原因。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。