java 注入 循环_spring依赖注入——循环依赖

上一篇博客简单地分析了下依赖注入。但是对于依赖注入的很多细节,都没有深入的分析。这一篇博客会继续分析spring的依赖注入。这篇博客会解决分析getBean缓存时候遗留下来的循环依赖问题。

循环依赖分析

首先明确下,只有单例情况下,spring才会试着去解决循环依赖问题,多例是不会去解决循环依赖的。这个也好理解,如果是多例的话,比如a -> b 并且 b -> a 那么,当A a=new A(); 之后要注入b,b却是多例的,那么究竟该注入哪个B是不确定的。如下图:

AAffA0nNPuCLAAAAAElFTkSuQmCC

接下来我们分析,为啥会有循环依赖的问题。

先来分析没有循环依赖的问题public static class A{

private B b;    //省略get和set方法}public static class B{

}

这个时候,如果spring先初始化A,然后会发现A依赖于B,然后就会初始化B,最后注入到A里。

整个流程用代码表示大概如下所示:A a = 创建A

B b = 创建B

-----> 创建A子流程  a.setB(b);

但是假设B也依赖了A呢?即public static class B{

private A a;

}

那样依赖注入过程就会变成(简单示例)A a = 创建A

B b = 创建B

---->  创建B子流程 b.setA(????);  //  这个时候A还没创建完成呢a.setB(b);

那么如何解决呢?很简单,把那个还没创建完的A(只是new了,但是没有进行依赖注入的A)set到B里就好了。

AAffA0nNPuCLAAAAAElFTkSuQmCC

弄清了这个流程之后,我们再来分析spring是如何进行依赖注入的。

spring对引用类型的注入

这里我先从spring对引用属性的注入开始。

即ref的注入private Object resolveReference(Object argName, RuntimeBeanReference ref) {    try {        String refName = ref.getBeanName();

refName = String.valueOf(doEvaluate(refName));        if (ref.isToParent()) {            if (this.beanFactory.getParentBeanFactory() == null) {                throw new BeanCreationException(                        this.beanDefinition.getResourceDescription(), this.beanName,                        "Can't resolve reference to bean '" + refName +                        "' in parent factory: no parent factory available");

}            return this.beanFactory.getParentBeanFactory().getBean(refName);

}        else {            Object bean = this.beanFactory.getBean(refName);            this.beanFactory.registerDependentBean(refName, this.beanName);            return bean;

}

}    catch (BeansException ex) {        throw new BeanCreationException(                this.beanDefinition.getResourceDescription(), this.beanName,                "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);

}

}

实现很简单,就是从beanFactory里获取要依赖的对象

我们再来回顾下流程

AAffA0nNPuCLAAAAAElFTkSuQmCC

问题是,当走到6时候,似乎又会回到1,这样不就死循环了么?

重点就是,第六步的获取A,我们回到doGetBean方法中。

getSingleton 方法protected 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 != NULL_OBJECT ? singletonObject : null);

}

我们在分析初始化bean的缓存部分时,曾分析过这几个缓存。当时其实只知道了singletonObjects是存储了已经创建了的对象。

现在让我们回头再看看这些缓存。

首先看看singletonFactories赋值的地方。

doCreateBean// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&

isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {    if (logger.isDebugEnabled()) {

logger.debug("Eagerly caching bean '" + beanName +                "' to allow for resolving potential circular references");

}

addSingletonFactory(beanName, new ObjectFactory() {        @Override

public Object getObject() throws BeansException {            //默认实现返回bean

return getEarlyBeanReference(beanName, mbd, bean);

}

});

}

在创建bean时候,有这样一段代码,当需要进行提前暴露时候(当前创建对象单例 + 允许循环引用 + 当前类正在被创建)会调用addSingletonFactory方法protected void addSingletonFactory(String beanName, ObjectFactory> singletonFactory) {

Assert.notNull(singletonFactory, "Singleton factory must not be null");    synchronized (this.singletonObjects) {        if (!this.singletonObjects.containsKey(beanName)) {            this.singletonFactories.put(beanName, singletonFactory);            this.earlySingletonObjects.remove(beanName);            this.registeredSingletons.add(beanName);

}

}

}

这里就碰见了我们要分析的singletonFactories,它存储了beanName -> 此处的匿名内部类singletonFactory。

singletonFactory

我们再回到getSingleton方法,以及之前绘制的图ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {

singletonObject = singletonFactory.getObject();    this.earlySingletonObjects.put(beanName, singletonObject);    this.singletonFactories.remove(beanName);

}

逻辑很简单

AAffA0nNPuCLAAAAAElFTkSuQmCC

三级缓存

我们经常听说spring通过三级缓存解决了循环依赖,其实三级缓存非常简单。就是指我们分析过的Map singletonObjects

Map> singletonFactories

Map earlySingletonObjects

这里分别举这样三个例子。

A 依赖 B(B不依赖A)

AAffA0nNPuCLAAAAAElFTkSuQmCC

一级缓存

A 依赖 B && B依赖A

AAffA0nNPuCLAAAAAElFTkSuQmCC

三级缓存

A依赖B && B依赖A + B依赖C && C 依赖 A

AAffA0nNPuCLAAAAAElFTkSuQmCC

作者:端吉

链接:https://www.jianshu.com/p/10f94b776e55

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

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

相关文章

前端学习(1393):多人管理项目13加密实现

blog.js //管理页面 //展示页面 const express require(express);const admin express.Router();admin.get(/login, (req, res) > {res.render(admin/login) }); admin.get(/user, (req, res) > {res.render(admin/user) }); admin.post(/login, async(req, res) >…

java提高篇四_(转)java提高篇(四)-----理解java的三大特性之多态

面向对象编程有三大特性:封装、继承、多态。封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。继承是为了重用父类代码。…

前端学习(1397):项目包含的知识点cookie和session2

const express require(express); //创建网站服务器 const app express(); //开放静态资源文件 const path require(path); //引入 const bodyPaser require(body-parser);const session require(express-session); require(./model/connect)//处理post app.use(bodyPase…

前端学习(1401):多人管理21新增用户

const { User } require(../../model/user);module.exports async (req, res) > {// 获取到地址栏中的id参数const { message, id } req.query;// 如果当前传递了id参数if (id) {// 修改操作let user await User.findOne({_id: id});// 渲染用户编辑页面(修改)res.rende…

C# 添加类库依赖

转载于:https://www.cnblogs.com/dekevin/p/4350049.html

前端学习(1402):多人管理22验证joi

// 引入joi模块 const Joi require(joi);// 定义对象的验证规则 const schema {username: Joi.string().min(2).max(5).required().error(new Error(username属性没有通过验证)),birth: Joi.number().min(1900).max(2020).error(new Error(birth没有通过验证)) };async funct…

win2003+IIS6+PHP5.3.8+MSSQL2008的安装配置

转载于:https://www.cnblogs.com/nxping/p/4351033.html

Linux给Java程序设置端口_扫描服务端口的Java程序

在Linux下用C写了一个扫描指定IP地址对外开放端口号的程序。扫描自己的机器的端口号速度还是挺快的,用编写的程序扫描在美国的服务器时,等了10分钟,端口号才扫到1000左右。于是就想到了用多线程,可是linux c的多线程不会&#xff…

第二次北京之行-游颐和园

转载于:https://www.cnblogs.com/motadou/p/4354613.html

java中级做dao模型_DAO-持久层-领域对象-贫血模型

原文关于"贫血模型"的讨论几乎没有停止过,在openfans.org的开发过程中,我们也讨论了很久,我觉的有很多东西应该记下来:明确一下意思先:DAO:数据操作对象,会操作数据库持久层:能提供对象持久化服务的一系列组件或服务领域对象:描述领域模型的对象,是通过业务分析进行系…

JSP分页技术的实现(利用当前页进行前后加减,并利用href进行当前页面传值,传值当然是那个当前值变量)...

一、可滚动结果集 Connection con DriverManager.getConnection(); PreparedStatement stmt con.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY); ResultSet rs stmt.executeQuery(); 常用方法: (1)rs.absolute(n); 可以…

前端学习(1406):多人管理26邮箱地址是否存在

// 引入用户集合的构造函数 const { User, validateUser } require(../../model/user); // 引入加密模块 const bcrypt require(bcryptjs);module.exports async(req, res, next) > {try {await validateUser(req.body)} catch (e) {// 验证没有通过// e.message// 重定向…

我的世界JAVA刷怪范围_《我的世界》只有刷怪蛋能够刷新生物吗?并不是,还有一种物品!...

《我的世界》MC能够刷新生物的只有刷怪蛋吗?不,还有一种物品!《我的世界》只有刷怪蛋能够刷新生物吗?并不是,还有一种物品!在沙盒游戏《我的世界》里面,绝大部分的生物会自然生成于主世界、下界…

Android上传文件至服务器(转)

本实例实现每隔5秒上传一次,通过服务器端获取手机上传过来的文件信息并做相应处理;采用AndroidStruts2技术。 一、Android端实现文件上传 1)、新建一个Android项目命名为androidUpload,目录结构如下: 2)、新建FormFile类&#xff…

前端学习(1409):多人管理29安装json转换工具

json转换工具 打开谷歌 chrome://flags/#extensions-on-chrome-urls

前端学习(1410):多人管理30数据分页

// 导入用户集合构造函数 const { User } require(../../model/user);module.exports async (req, res) > {// 接收客户端传递过来的当前页参数let page req.query.page || 1;// 每一页显示的数据条数let pagesize 10;// 查询用户数据的总数let count await User.count…

php 添加样式,添加样式到php html电子邮件

我仔细查看了这个问题,我在此发现的是添加以下内容:$headers MIME-Version: 1.0 . "\r\n";$headers . Content-type: text/html; charsetiso-8859-1 . "\r\n";和我想发送一个时事通讯类型的电子邮件,所以造型真的很重要.我观看的所有视频都只是…